33 static constexpr const char* compute_shader = R
"_(#version 450
34 layout(local_size_x = 16, local_size_y = 1, local_size_z = 1) in;
36 layout(binding = 0) uniform sampler2D src_tex;
37 layout(std430, binding = 1) writeonly buffer V210Buf {
40 layout(std140, binding = 2) uniform Params {
43 uint line_stride_words; // bytes-per-row / 4
44 uint groups_per_row; // == src_width / 6
48 vec2 flip_y(vec2 tc) {
49 // Only OpenGL. The rest of the engine puts its geometry through
50 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
51 // a hardcoded triangle in raw NDC and does not, so the correction it needs
52 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
53 // NDI and every other consumer an upside-down picture.
54 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
57 return vec2(tc.x, 1.0 - tc.y);
62 uvec3 to_yuv10(vec3 rgb) {
63 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
64 return uvec3(yuv * 1023.0 + 0.5);
68 uint group_x = gl_GlobalInvocationID.x;
69 uint y = gl_GlobalInvocationID.y;
70 if (group_x >= groups_per_row || int(y) >= src_size.y)
73 // Y-flip on backends that need it (matches the fragment encoders):
74 // only OpenGL. The rest of the engine negates Y through
75 // renderer.clipSpaceCorrMatrix on Vulkan; this pass indexes texels
76 // directly and does not, so it must not flip there.
77 ivec2 srcSize = src_size;
78 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
81 int src_y = srcSize.y - 1 - int(y);
84 // Edge-clamp: the tail group at widths not divisible by 6 has fewer
85 // than 6 source pixels (texelFetch does NOT clamp on its own).
86 int xmax = src_size.x - 1;
87 int x0i = int(group_x * 6u);
88 uvec3 a = to_yuv10(texelFetch(src_tex, ivec2(min(x0i , xmax), src_y), 0).rgb);
89 uvec3 b = to_yuv10(texelFetch(src_tex, ivec2(min(x0i + 1, xmax), src_y), 0).rgb);
90 uvec3 c = to_yuv10(texelFetch(src_tex, ivec2(min(x0i + 2, xmax), src_y), 0).rgb);
91 uvec3 d = to_yuv10(texelFetch(src_tex, ivec2(min(x0i + 3, xmax), src_y), 0).rgb);
92 uvec3 e = to_yuv10(texelFetch(src_tex, ivec2(min(x0i + 4, xmax), src_y), 0).rgb);
93 uvec3 f = to_yuv10(texelFetch(src_tex, ivec2(min(x0i + 5, xmax), src_y), 0).rgb);
95 uint cb01 = (a.y + b.y) >> 1;
96 uint cr01 = (a.z + b.z) >> 1;
97 uint cb23 = (c.y + d.y) >> 1;
98 uint cr23 = (c.z + d.z) >> 1;
99 uint cb45 = (e.y + f.y) >> 1;
100 uint cr45 = (e.z + f.z) >> 1;
102 // v210 packing: 6 pixels = 4 little-endian 32-bit words.
103 uint w0 = cb01 | (a.x << 10) | (cr01 << 20);
104 uint w1 = b.x | (cb23 << 10) | (c.x << 20);
105 uint w2 = cr23 | (d.x << 10) | (cb45 << 20);
106 uint w3 = e.x | (cr45 << 10) | (f.x << 20);
108 uint base = y * line_stride_words + group_x * 4u;
109 v210[base + 0u] = w0;
110 v210[base + 1u] = w1;
111 v210[base + 2u] = w2;
112 v210[base + 3u] = w3;
116 QRhiBuffer* m_paramsUBO{};
117 QRhiSampler* m_sampler{};
118 QRhiShaderResourceBindings* m_srb{};
119 QRhiComputePipeline* m_pipeline{};
122 int m_groupsPerRow{};
123 uint32_t m_lineStrideBytes{};
127 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
128 int height, QRhiBuffer* outputBuffer,
129 const QString& colorConversion)
override
131 if(!outputBuffer || width % 2 != 0)
133 if(!rhi.isFeatureSupported(QRhi::Compute))
141 m_groupsPerRow = (width + 5) / 6;
142 m_lineStrideBytes = ((width + 47) / 48) * 128;
145 m_paramsUBO = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 32);
146 m_paramsUBO->setName(
"V210ComputeEncoder::params");
147 if(!m_paramsUBO->create())
150 m_sampler = rhi.newSampler(
151 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
152 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
153 if(!m_sampler->create())
156 m_srb = rhi.newShaderResourceBindings();
158 QRhiShaderResourceBinding::sampledTexture(
159 0, QRhiShaderResourceBinding::ComputeStage, inputRGBA, m_sampler),
160 QRhiShaderResourceBinding::bufferStore(
161 1, QRhiShaderResourceBinding::ComputeStage, outputBuffer),
162 QRhiShaderResourceBinding::uniformBuffer(
163 2, QRhiShaderResourceBinding::ComputeStage, m_paramsUBO),
169 state, QString::fromLatin1(compute_shader).arg(colorConversion));
170 m_pipeline = rhi.newComputePipeline();
171 m_pipeline->setShaderStage({QRhiShaderStage::Compute, cs});
172 m_pipeline->setShaderResourceBindings(m_srb);
173 if(!m_pipeline->create())
183 QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch* res)
override
185 struct alignas(16) ParamsData
189 uint32_t lineStrideWords;
190 uint32_t groupsPerRow;
195 m_lineStrideBytes / 4,
196 static_cast<uint32_t
>(m_groupsPerRow),
198 res->updateDynamicBuffer(m_paramsUBO, 0,
sizeof(p), &p);
200 cb.beginComputePass(res);
201 cb.setComputePipeline(m_pipeline);
202 cb.setShaderResources(m_srb);
204 (m_groupsPerRow + 15) / 16,
210 void release()
override
QShader makeCompute(const RenderState &v, QString compute)
Compile a compute shader.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1327