2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
32 static constexpr const char* frag = R
"_(#version 450
33 layout(location = 0) in vec2 v_texcoord;
34 layout(location = 0) out vec4 fragColor;
35 layout(binding = 3) uniform sampler2D src_tex;
38 vec2 flip_y_uv(vec2 tc) {
39 // Only OpenGL. The rest of the engine puts its geometry through
40 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
41 // a hardcoded triangle in raw NDC and does not, so the correction it needs
42 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
43 // NDI and every other consumer an upside-down picture.
44 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
47 return vec2(tc.x, 1.0 - tc.y);
51 // 10-bit unsigned (0..1023) for one source pixel.
52 uvec3 to_yuv10(vec3 rgb) {
53 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
54 return uvec3(yuv * 1023.0 + 0.5);
57 // Pack a 32-bit value as 4 little-endian bytes into the RGBA8 output.
61 float((w >> 8u) & 0xFFu),
62 float((w >> 16u) & 0xFFu),
63 float((w >> 24u) & 0xFFu)) / 255.0;
67 ivec2 srcSize = textureSize(src_tex, 0);
69 // Output word index (X): gl_FragCoord.x is exact and never flipped.
70 int outX = int(gl_FragCoord.x);
72 // Source row (Y): derive from the interpolated texcoord, exactly like
73 // UYVYEncoder. gl_FragCoord.y's origin differs after HLSL/MSL cross-
74 // compilation (it flipped the v210 output vertically on D3D11), whereas
75 // flip_y_uv(v_texcoord) is correct on GL, Vulkan and D3D alike.
77 int(flip_y_uv(v_texcoord).y * float(srcSize.y)), 0, srcSize.y - 1);
79 // Each output row is N v210 words (RGBA8 texels), N == srcWidth/6 * 4.
80 int wordInGroup = outX & 3; // 0..3
81 int groupIdx = outX >> 2; // 6-pixel group index
82 int srcX0 = groupIdx * 6; // first source pixel in group
84 // texelFetch is clamped via sampler/edge handling; for safety on the
85 // last partial group (shouldn't happen since width % 6 == 0) we clamp.
86 int x0 = min(srcX0 , srcSize.x - 1);
87 int x1 = min(srcX0 + 1, srcSize.x - 1);
88 int x2 = min(srcX0 + 2, srcSize.x - 1);
89 int x3 = min(srcX0 + 3, srcSize.x - 1);
90 int x4 = min(srcX0 + 4, srcSize.x - 1);
91 int x5 = min(srcX0 + 5, srcSize.x - 1);
94 if (wordInGroup == 0) {
95 uvec3 a = to_yuv10(texelFetch(src_tex, ivec2(x0, srcY), 0).rgb);
96 uvec3 b = to_yuv10(texelFetch(src_tex, ivec2(x1, srcY), 0).rgb);
97 uint cb01 = (a.y + b.y) >> 1;
98 uint cr01 = (a.z + b.z) >> 1;
99 w = cb01 | (a.x << 10) | (cr01 << 20);
100 } else if (wordInGroup == 1) {
101 uvec3 b = to_yuv10(texelFetch(src_tex, ivec2(x1, srcY), 0).rgb);
102 uvec3 c = to_yuv10(texelFetch(src_tex, ivec2(x2, srcY), 0).rgb);
103 uvec3 d = to_yuv10(texelFetch(src_tex, ivec2(x3, srcY), 0).rgb);
104 uint cb23 = (c.y + d.y) >> 1;
105 w = b.x | (cb23 << 10) | (c.x << 20);
106 } else if (wordInGroup == 2) {
107 uvec3 c = to_yuv10(texelFetch(src_tex, ivec2(x2, srcY), 0).rgb);
108 uvec3 d = to_yuv10(texelFetch(src_tex, ivec2(x3, srcY), 0).rgb);
109 uvec3 e = to_yuv10(texelFetch(src_tex, ivec2(x4, srcY), 0).rgb);
110 uvec3 f = to_yuv10(texelFetch(src_tex, ivec2(x5, srcY), 0).rgb);
111 uint cr23 = (c.z + d.z) >> 1;
112 uint cb45 = (e.y + f.y) >> 1;
113 w = cr23 | (d.x << 10) | (cb45 << 20);
115 uvec3 e = to_yuv10(texelFetch(src_tex, ivec2(x4, srcY), 0).rgb);
116 uvec3 f = to_yuv10(texelFetch(src_tex, ivec2(x5, srcY), 0).rgb);
117 uint cr45 = (e.z + f.z) >> 1;
118 w = e.x | (cr45 << 10) | (f.x << 20);
120 fragColor = pack32(w);
124 QRhiTexture* m_outTexture{};
125 QRhiTextureRenderTarget* m_renderTarget{};
126 QRhiRenderPassDescriptor* m_rpDesc{};
127 QRhiSampler* m_sampler{};
128 QRhiShaderResourceBindings* m_srb{};
129 QRhiGraphicsPipeline* m_pipeline{};
130 QRhiReadbackResult m_readback{};
134 bool m_readbackEnabled{
true};
137 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
138 int height,
const QString& colorConversion)
override
147 m_outW = ((width + 47) / 48) * 32;
149 m_outTexture = rhi.newTexture(
150 QRhiTexture::RGBA8, QSize{m_outW, height}, 1,
151 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
152 m_outTexture->create();
154 m_renderTarget = rhi.newTextureRenderTarget({m_outTexture});
155 m_rpDesc = m_renderTarget->newCompatibleRenderPassDescriptor();
156 m_renderTarget->setRenderPassDescriptor(m_rpDesc);
157 m_renderTarget->create();
161 m_sampler = rhi.newSampler(
162 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
163 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
166 m_srb = rhi.newShaderResourceBindings();
168 QRhiShaderResourceBinding::sampledTexture(
169 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
175 QString::fromLatin1(frag).arg(colorConversion));
177 m_pipeline = rhi.newGraphicsPipeline();
178 m_pipeline->setShaderStages({
179 {QRhiShaderStage::Vertex, vertS},
180 {QRhiShaderStage::Fragment, fragS},
182 m_pipeline->setVertexInputLayout({});
183 m_pipeline->setShaderResourceBindings(m_srb);
184 m_pipeline->setRenderPassDescriptor(m_rpDesc);
185 m_pipeline->create();
188 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
190 cb.beginPass(m_renderTarget, Qt::black, {0.0f, 0});
191 cb.setGraphicsPipeline(m_pipeline);
192 cb.setShaderResources(m_srb);
193 cb.setViewport(QRhiViewport(0, 0, m_outW, m_height));
196 if(m_readbackEnabled)
198 auto* readbackBatch = rhi.nextResourceUpdateBatch();
199 readbackBatch->readBackTexture(QRhiReadbackDescription{m_outTexture}, &m_readback);
200 cb.endPass(readbackBatch);
209 const QRhiReadbackResult&
readback(
int)
const override {
return m_readback; }
210 QRhiTexture*
outputTexture() const noexcept
override {
return m_outTexture; }
215 delete m_pipeline; m_pipeline =
nullptr;
216 delete m_srb; m_srb =
nullptr;
217 delete m_sampler; m_sampler =
nullptr;
218 delete m_rpDesc; m_rpDesc =
nullptr;
219 delete m_renderTarget; m_renderTarget =
nullptr;
220 delete m_outTexture; m_outTexture =
nullptr;
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag, int multiViewCount)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1238
Base class for GPU-side video format conversion (RGBA to YUV).
Definition GPUVideoEncoder.hpp:30
static constexpr const char * vertex_shader
Definition GPUVideoEncoder.hpp:74
Cross-backend RGBA -> v210 encoder.
Definition encoders/V210.hpp:30
void init(QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override
Definition encoders/V210.hpp:133
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition encoders/V210.hpp:205
void release() override
Release all GPU resources.
Definition encoders/V210.hpp:210
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition encoders/V210.hpp:185
void setReadbackEnabled(bool e) noexcept override
Definition encoders/V210.hpp:208
const QRhiReadbackResult & readback(int) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition encoders/V210.hpp:206
QRhiTexture * outputTexture() const noexcept override
Definition encoders/V210.hpp:207