47 static constexpr const char* plane_frag = R
"_(#version 450
48 layout(location = 0) in vec2 v_texcoord;
49 layout(location = 0) out vec4 fragColor;
50 layout(binding = 3) uniform sampler2D src_tex;
52 vec2 flip_y(vec2 tc) {
53 // Only OpenGL. The rest of the engine puts its geometry through
54 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
55 // a hardcoded triangle in raw NDC and does not, so the correction it needs
56 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
57 // NDI and every other consumer an upside-down picture.
58 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
61 return vec2(tc.x, 1.0 - tc.y);
65 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
66 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
67 fragColor = vec4(yuv.%2 * float(%3), 0.0, 0.0, 1.0);
71#if QT_VERSION < QT_VERSION_CHECK(6, 10, 0)
74 static constexpr const char* pack_common = R
"_(
75 int flip_y_int(int y, int h) {
76 // See GPUVideoEncoder::y_flip_glsl: only OpenGL. The rest of the engine
77 // negates Y through renderer.clipSpaceCorrMatrix on Vulkan; this pass
78 // indexes texels directly and does not, so it must not flip there.
79 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
85 uint comp10(vec3 rgb, int c) {
86 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
87 float v = (c == 0) ? yuv.x : (c == 1 ? yuv.y : yuv.z);
88 return uint(v * 1023.0 + 0.5);
90 // Two 16-bit values -> 4 little-endian bytes in an RGBA8 texel.
91 vec4 pack2(uint a, uint b) {
92 return vec4(float(a & 0xFFu), float((a >> 8u) & 0xFFu),
93 float(b & 0xFFu), float((b >> 8u) & 0xFFu)) / 255.0;
98 static constexpr const char* pack_y_frag = R
"_(#version 450
99 layout(location = 0) in vec2 v_texcoord;
100 layout(location = 0) out vec4 fragColor;
101 layout(binding = 3) uniform sampler2D src_tex;
105 ivec2 sz = textureSize(src_tex, 0);
106 ivec2 o = ivec2(gl_FragCoord.xy);
107 int sy = flip_y_int(o.y, sz.y);
109 uint a = comp10(texelFetch(src_tex, ivec2(min(x0, sz.x - 1), sy), 0).rgb, 0);
110 uint b = comp10(texelFetch(src_tex, ivec2(min(x0 + 1, sz.x - 1), sy), 0).rgb, 0);
111 fragColor = pack2(a, b);
119 static constexpr const char* pack_c_frag = R
"_(#version 450
120 layout(location = 0) in vec2 v_texcoord;
121 layout(location = 0) out vec4 fragColor;
122 layout(binding = 3) uniform sampler2D src_tex;
125 uint chroma(int x, int oy, ivec2 sz) {
127 int xa = min(x, sz.x - 1);
128 int xb = min(x + 1, sz.x - 1);
129 int y0 = flip_y_int(min((oy << VS), sz.y - 1), sz.y);
130 int y1 = flip_y_int(min((oy << VS) + VS, sz.y - 1), sz.y);
131 uint s = comp10(texelFetch(src_tex, ivec2(xa, y0), 0).rgb, %3)
132 + comp10(texelFetch(src_tex, ivec2(xb, y0), 0).rgb, %3)
133 + comp10(texelFetch(src_tex, ivec2(xa, y1), 0).rgb, %3)
134 + comp10(texelFetch(src_tex, ivec2(xb, y1), 0).rgb, %3);
138 ivec2 sz = textureSize(src_tex, 0);
139 ivec2 o = ivec2(gl_FragCoord.xy);
141 fragColor = pack2(chroma(x0, o.y, sz), chroma(x0 + 2, o.y, sz));
148 QRhiTexture* texture{};
149 QRhiTextureRenderTarget* rt{};
150 QRhiRenderPassDescriptor* rp{};
151 QRhiShaderResourceBindings* srb{};
152 QRhiGraphicsPipeline* pipeline{};
153 QRhiReadbackResult readback{};
158 delete pipeline;
delete srb;
delete rp;
delete rt;
delete texture;
159 pipeline =
nullptr; srb =
nullptr; rp =
nullptr; rt =
nullptr;
165 int m_chromaShiftY{0};
166 PlaneResources m_planes[3];
167 QRhiSampler* m_sampler{};
171 YUVPlanarEncoder(
int bits,
int chromaShiftY)
173 , m_chromaShiftY{chromaShiftY}
178 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
179 PlaneResources& plane, QRhiTexture::Format fmt,
int w,
int h,
184 plane.texture = rhi.newTexture(
186 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
187 plane.texture->create();
189 plane.rt = rhi.newTextureRenderTarget({plane.texture});
190 plane.rp = plane.rt->newCompatibleRenderPassDescriptor();
191 plane.rt->setRenderPassDescriptor(plane.rp);
194 plane.srb = rhi.newShaderResourceBindings();
195 plane.srb->setBindings({
196 QRhiShaderResourceBinding::sampledTexture(
197 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
202 plane.pipeline = rhi.newGraphicsPipeline();
203 plane.pipeline->setShaderStages({
204 {QRhiShaderStage::Vertex, vs},
205 {QRhiShaderStage::Fragment, fs},
207 plane.pipeline->setVertexInputLayout({});
208 plane.pipeline->setShaderResourceBindings(plane.srb);
209 plane.pipeline->setRenderPassDescriptor(plane.rp);
210 plane.pipeline->create();
213 void execPlane(QRhi& rhi, QRhiCommandBuffer& cb, PlaneResources& p)
215 cb.beginPass(p.rt, Qt::black, {0.0f, 0});
216 cb.setGraphicsPipeline(p.pipeline);
217 cb.setShaderResources(p.srb);
218 cb.setViewport(QRhiViewport(0, 0, p.w, p.h));
220 auto* batch = rhi.nextResourceUpdateBatch();
221 batch->readBackTexture(QRhiReadbackDescription{p.texture}, &p.readback);
226 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
227 int height,
const QString& colorConversion)
override
232 m_sampler = rhi.newSampler(
233 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
234 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
237 const int cw = width / 2;
238 const int ch = height >> m_chromaShiftY;
240#if QT_VERSION < QT_VERSION_CHECK(6, 10, 0)
246 const QString cm = QString::fromLatin1(pack_common);
247 const QString vshift = QString::number(m_chromaShiftY);
248 const QString y = QString::fromLatin1(pack_y_frag).arg(colorConversion, cm);
249 const QString cb = QString::fromLatin1(pack_c_frag)
250 .arg(colorConversion, cm, QStringLiteral(
"1"), vshift);
251 const QString cr = QString::fromLatin1(pack_c_frag)
252 .arg(colorConversion, cm, QStringLiteral(
"2"), vshift);
254 rhi, state, inputRGBA, m_planes[0], QRhiTexture::RGBA8, width / 2, height,
257 rhi, state, inputRGBA, m_planes[1], QRhiTexture::RGBA8, width / 4, ch, cb);
259 rhi, state, inputRGBA, m_planes[2], QRhiTexture::RGBA8, width / 4, ch, cr);
264 const QString scale = m_bits == 8 ? QStringLiteral(
"1.0")
265 : QStringLiteral(
"0.0156097");
266 auto frag = [&](
const char* component) {
267 return QString::fromLatin1(plane_frag)
268 .arg(colorConversion)
272 const auto fmt = m_bits == 8 ? QRhiTexture::R8 : QRhiTexture::R16;
273 initPlane(rhi, state, inputRGBA, m_planes[0], fmt, width, height, frag(
"x"));
274 initPlane(rhi, state, inputRGBA, m_planes[1], fmt, cw, ch, frag(
"y"));
275 initPlane(rhi, state, inputRGBA, m_planes[2], fmt, cw, ch, frag(
"z"));
278 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
280 execPlane(rhi, cb, m_planes[0]);
281 execPlane(rhi, cb, m_planes[1]);
282 execPlane(rhi, cb, m_planes[2]);
287 const QRhiReadbackResult&
readback(
int plane)
const override
289 return m_planes[plane].readback;
294 for(
auto& p : m_planes)
301 static std::unique_ptr<YUVPlanarEncoder> p422_8()
303 return std::make_unique<YUVPlanarEncoder>(8, 0);
306 static std::unique_ptr<YUVPlanarEncoder> p420_8()
308 return std::make_unique<YUVPlanarEncoder>(8, 1);
311 static std::unique_ptr<YUVPlanarEncoder> p420_10()
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