Loading...
Searching...
No Matches
UYVYCompute.hpp
1#pragma once
2#include <Gfx/Graph/Utils.hpp>
4#include <Gfx/Graph/encoders/ComputeEncoder.hpp>
5#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
6
7#include <private/qrhi_p.h>
8
9namespace score::gfx
10{
11
21{
22 static constexpr const char* compute_shader = R"_(#version 450
23 layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
24
25 layout(binding = 0) uniform sampler2D src_tex;
26 layout(std430, binding = 1) writeonly buffer UyvyBuf {
27 uint uyvy[];
28 };
29 layout(std140, binding = 2) uniform Params {
30 ivec2 src_size;
31 ivec2 _pad0;
32 uint line_stride_words; // bytes-per-row / 4
33 uint pairs_per_row; // == src_width / 2
34 uint _pad1[2];
35 };
36 )_" "%1" R"_(
37
38 uint to_byte(float v) {
39 return uint(clamp(v, 0.0, 1.0) * 255.0 + 0.5);
40 }
41
42 void main() {
43 uint pair_x = gl_GlobalInvocationID.x;
44 uint y = gl_GlobalInvocationID.y;
45 if (pair_x >= pairs_per_row || int(y) >= src_size.y)
46 return;
47
48 // See GPUVideoEncoder::y_flip_glsl: only OpenGL. The rest of the engine
49 // negates Y through renderer.clipSpaceCorrMatrix on Vulkan; this pass
50 // indexes texels directly and does not, so it must not flip there.
51 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
52 int src_y = int(y);
53 #else
54 int src_y = src_size.y - 1 - int(y);
55 #endif
56
57 uint x0 = pair_x * 2u;
58 vec3 a = clamp(convert_from_rgb(texelFetch(src_tex, ivec2(int(x0 ), src_y), 0).rgb), 0.0, 1.0);
59 vec3 b = clamp(convert_from_rgb(texelFetch(src_tex, ivec2(int(x0 + 1u), src_y), 0).rgb), 0.0, 1.0);
60
61 uint Y0 = to_byte(a.x);
62 uint Y1 = to_byte(b.x);
63 uint Cb = to_byte((a.y + b.y) * 0.5);
64 uint Cr = to_byte((a.z + b.z) * 0.5);
65
66 // UYVY little-endian DWORD: Cb, Y0, Cr, Y1
67 uint w = Cb | (Y0 << 8) | (Cr << 16) | (Y1 << 24);
68 uyvy[y * line_stride_words + pair_x] = w;
69 }
70 )_";
71
72 QRhiBuffer* m_paramsUBO{};
73 QRhiSampler* m_sampler{};
74 QRhiShaderResourceBindings* m_srb{};
75 QRhiComputePipeline* m_pipeline{};
76 int m_width{};
77 int m_height{};
78 uint32_t m_pairsPerRow{};
79 uint32_t m_lineStrideBytes{};
80
81 bool init(
82 QRhi& rhi, const RenderState& state, QRhiTexture* inputRGBA, int width,
83 int height, QRhiBuffer* outputBuffer,
84 const QString& colorConversion) override
85 {
86 if(!outputBuffer || (width % 2) != 0)
87 return false;
88 if(!rhi.isFeatureSupported(QRhi::Compute))
89 return false;
90
91 m_width = width;
92 m_height = height;
93 m_pairsPerRow = width / 2;
94 m_lineStrideBytes = width * 2; // UYVY: 2 bytes per pixel, no padding
95
96 m_paramsUBO = rhi.newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 32);
97 m_paramsUBO->setName("UYVYComputeEncoder::params");
98 if(!m_paramsUBO->create())
99 return false;
100
101 m_sampler = rhi.newSampler(
102 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
103 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
104 if(!m_sampler->create())
105 return false;
106
107 m_srb = rhi.newShaderResourceBindings();
108 m_srb->setBindings({
109 QRhiShaderResourceBinding::sampledTexture(
110 0, QRhiShaderResourceBinding::ComputeStage, inputRGBA, m_sampler),
111 QRhiShaderResourceBinding::bufferStore(
112 1, QRhiShaderResourceBinding::ComputeStage, outputBuffer),
113 QRhiShaderResourceBinding::uniformBuffer(
114 2, QRhiShaderResourceBinding::ComputeStage, m_paramsUBO),
115 });
116 if(!m_srb->create())
117 return false;
118
119 QShader cs = makeCompute(
120 state, QString::fromLatin1(compute_shader).arg(colorConversion));
121 m_pipeline = rhi.newComputePipeline();
122 m_pipeline->setShaderStage({QRhiShaderStage::Compute, cs});
123 m_pipeline->setShaderResourceBindings(m_srb);
124 if(!m_pipeline->create())
125 return false;
126
127 return true;
128 }
129
130 void exec(
131 QRhi& rhi, QRhiCommandBuffer& cb, QRhiResourceUpdateBatch* res) override
132 {
133 struct alignas(16) ParamsData
134 {
135 int32_t srcW, srcH;
136 int32_t pad0[2];
137 uint32_t lineStrideWords;
138 uint32_t pairsPerRow;
139 uint32_t pad1[2];
140 } p{
141 m_width, m_height,
142 {0, 0},
143 m_lineStrideBytes / 4,
144 m_pairsPerRow,
145 {0, 0}};
146 res->updateDynamicBuffer(m_paramsUBO, 0, sizeof(p), &p);
147
148 cb.beginComputePass(res);
149 cb.setComputePipeline(m_pipeline);
150 cb.setShaderResources(m_srb);
151 cb.dispatch((m_pairsPerRow + 31) / 32, m_height, 1);
152 cb.endComputePass();
153 }
154
155 void release() override
156 {
157 delete m_pipeline; m_pipeline = nullptr;
158 delete m_srb; m_srb = nullptr;
159 delete m_sampler; m_sampler = nullptr;
160 delete m_paramsUBO; m_paramsUBO = nullptr;
161 }
162};
163
164} // namespace score::gfx
GPU shader generation for RGB->YUV color space conversion (output/encoding).
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
QShader makeCompute(const RenderState &v, QString compute)
Compile a compute shader.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1327
Base interface for compute-shader RGBA -> AJA-format encoders.
Definition ComputeEncoder.hpp:36
Global state associated to a rendering context.
Definition RenderState.hpp:37
Compute-shader RGBA -> UYVY (2vuy) encoder targeting an external SSBO.
Definition UYVYCompute.hpp:21