2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
38#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
44 static constexpr const char* y_frag = R
"_(#version 450
45 layout(location = 0) in vec2 v_texcoord;
46 layout(location = 0) out vec4 fragColor;
47 layout(binding = 3) uniform sampler2D src_tex;
49 vec2 flip_y(vec2 tc) {
50 // Only OpenGL. The rest of the engine puts its geometry through
51 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
52 // a hardcoded triangle in raw NDC and does not, so the correction it needs
53 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
54 // NDI and every other consumer an upside-down picture.
55 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
58 return vec2(tc.x, 1.0 - tc.y);
62 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
63 vec3 yuv = convert_from_rgb(rgb);
64 fragColor = vec4(clamp(yuv.x, 0.0, 1.0), 0.0, 0.0, 1.0);
72 static constexpr const char* uv_frag = R
"_(#version 450
73 layout(location = 0) in vec2 v_texcoord;
74 layout(location = 0) out vec4 fragColor;
75 layout(binding = 3) uniform sampler2D src_tex;
77 vec2 flip_y(vec2 tc) {
78 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
81 return vec2(tc.x, 1.0 - tc.y);
85 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
86 vec3 yuv = convert_from_rgb(rgb);
87 fragColor = vec4(clamp(yuv.y, 0.0, 1.0), clamp(yuv.z, 0.0, 1.0), 0.0, 1.0);
91 QRhiTexture* m_yTexture{};
92 QRhiTextureRenderTarget* m_yRT{};
93 QRhiRenderPassDescriptor* m_yRP{};
94 QRhiShaderResourceBindings* m_ySRB{};
95 QRhiGraphicsPipeline* m_yPipeline{};
96 QRhiReadbackResult m_yReadback{};
98 QRhiTexture* m_uvTexture{};
99 QRhiTextureRenderTarget* m_uvRT{};
100 QRhiRenderPassDescriptor* m_uvRP{};
101 QRhiShaderResourceBindings* m_uvSRB{};
102 QRhiGraphicsPipeline* m_uvPipeline{};
103 QRhiReadbackResult m_uvReadback{};
105 QRhiSampler* m_sampler{};
108 bool m_readbackEnabled{
true};
111 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
112 QRhiTexture::Format fmt,
int w,
int h,
const char* frag,
113 const QString& colorConversion, QRhiTexture*& tex,
114 QRhiTextureRenderTarget*& rt, QRhiRenderPassDescriptor*& rp,
115 QRhiShaderResourceBindings*& srb, QRhiGraphicsPipeline*& pipeline)
117 tex = rhi.newTexture(
119 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
122 rt = rhi.newTextureRenderTarget({tex});
123 rp = rt->newCompatibleRenderPassDescriptor();
124 rt->setRenderPassDescriptor(rp);
127 srb = rhi.newShaderResourceBindings();
129 QRhiShaderResourceBinding::sampledTexture(
130 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
136 QString::fromLatin1(frag).arg(colorConversion));
137 pipeline = rhi.newGraphicsPipeline();
138 pipeline->setShaderStages({
139 {QRhiShaderStage::Vertex, vs},
140 {QRhiShaderStage::Fragment, fs},
142 pipeline->setVertexInputLayout({});
143 pipeline->setShaderResourceBindings(srb);
144 pipeline->setRenderPassDescriptor(rp);
149 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
150 int height,
const QString& colorConversion)
override
155 m_sampler = rhi.newSampler(
156 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
157 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
161 rhi, state, inputRGBA, QRhiTexture::R16, width, height, y_frag,
162 colorConversion, m_yTexture, m_yRT, m_yRP, m_ySRB, m_yPipeline);
164 rhi, state, inputRGBA, QRhiTexture::RG16, width / 2, height, uv_frag,
165 colorConversion, m_uvTexture, m_uvRT, m_uvRP, m_uvSRB, m_uvPipeline);
168 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
170 cb.beginPass(m_yRT, Qt::black, {0.0f, 0});
171 cb.setGraphicsPipeline(m_yPipeline);
172 cb.setShaderResources(m_ySRB);
173 cb.setViewport(QRhiViewport(0, 0, m_width, m_height));
175 if(m_readbackEnabled)
177 auto* yb = rhi.nextResourceUpdateBatch();
178 yb->readBackTexture(QRhiReadbackDescription{m_yTexture}, &m_yReadback);
186 cb.beginPass(m_uvRT, Qt::black, {0.0f, 0});
187 cb.setGraphicsPipeline(m_uvPipeline);
188 cb.setShaderResources(m_uvSRB);
189 cb.setViewport(QRhiViewport(0, 0, m_width / 2, m_height));
191 if(m_readbackEnabled)
193 auto* uvb = rhi.nextResourceUpdateBatch();
194 uvb->readBackTexture(QRhiReadbackDescription{m_uvTexture}, &m_uvReadback);
205 const QRhiReadbackResult&
readback(
int plane)
const override
207 return plane == 0 ? m_yReadback : m_uvReadback;
214 return plane == 0 ? m_yTexture : m_uvTexture;
232 m_uvPipeline = m_yPipeline =
nullptr;
233 m_uvSRB = m_ySRB =
nullptr;
234 m_uvRP = m_yRP =
nullptr;
235 m_uvRT = m_yRT =
nullptr;
236 m_uvTexture = m_yTexture =
nullptr;
241struct P216Encoder : GPUVideoEncoder
243 static constexpr const char* common = R
"_(
244 int flip_y_int(int y, int h) {
245 // See GPUVideoEncoder::y_flip_glsl: only OpenGL. The rest of the engine
246 // negates Y through renderer.clipSpaceCorrMatrix on Vulkan; this pass
247 // indexes texels directly and does not, so it must not flip there.
248 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
254 // Component (0=Y,1=U,2=V) as a full-range 16-bit word.
255 uint s16(vec3 rgb, int c) {
256 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
257 float v = (c == 0) ? yuv.x : (c == 1 ? yuv.y : yuv.z);
258 return uint(v * 65535.0 + 0.5);
260 vec4 pack2(uint a, uint b) {
261 return vec4(float(a & 0xFFu), float((a >> 8u) & 0xFFu),
262 float(b & 0xFFu), float((b >> 8u) & 0xFFu)) / 255.0;
267 static constexpr const char* y_frag = R
"_(#version 450
268 layout(location = 0) in vec2 v_texcoord;
269 layout(location = 0) out vec4 fragColor;
270 layout(binding = 3) uniform sampler2D src_tex;
274 ivec2 sz = textureSize(src_tex, 0);
275 ivec2 o = ivec2(gl_FragCoord.xy);
276 int sy = flip_y_int(o.y, sz.y);
278 uint a = s16(texelFetch(src_tex, ivec2(min(x0, sz.x - 1), sy), 0).rgb, 0);
279 uint b = s16(texelFetch(src_tex, ivec2(min(x0 + 1, sz.x - 1), sy), 0).rgb, 0);
280 fragColor = pack2(a, b);
286 static constexpr const char* uv_frag = R
"_(#version 450
287 layout(location = 0) in vec2 v_texcoord;
288 layout(location = 0) out vec4 fragColor;
289 layout(binding = 3) uniform sampler2D src_tex;
292 uint avg2(int x, int y, ivec2 sz, int c) {
293 int xa = min(x, sz.x - 1), xb = min(x + 1, sz.x - 1);
294 uint s = s16(texelFetch(src_tex, ivec2(xa, y), 0).rgb, c)
295 + s16(texelFetch(src_tex, ivec2(xb, y), 0).rgb, c);
299 ivec2 sz = textureSize(src_tex, 0);
300 ivec2 o = ivec2(gl_FragCoord.xy);
302 int sy = flip_y_int(o.y, sz.y);
303 fragColor = pack2(avg2(x0, sy, sz, 1), avg2(x0, sy, sz, 2));
307 struct PlaneResources
309 QRhiTexture* texture{};
310 QRhiTextureRenderTarget* rt{};
311 QRhiRenderPassDescriptor* rp{};
312 QRhiShaderResourceBindings* srb{};
313 QRhiGraphicsPipeline* pipeline{};
314 QRhiReadbackResult readback{};
319 delete pipeline;
delete srb;
delete rp;
delete rt;
delete texture;
320 pipeline =
nullptr; srb =
nullptr; rp =
nullptr; rt =
nullptr; texture =
nullptr;
324 PlaneResources m_y, m_uv;
325 QRhiSampler* m_sampler{};
328 bool m_readbackEnabled{
true};
331 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
332 PlaneResources& p,
int packW,
int packH,
const QString& frag)
336 p.texture = rhi.newTexture(
337 QRhiTexture::RGBA8, QSize{packW, packH}, 1,
338 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
340 p.rt = rhi.newTextureRenderTarget({p.texture});
341 p.rp = p.rt->newCompatibleRenderPassDescriptor();
342 p.rt->setRenderPassDescriptor(p.rp);
344 p.srb = rhi.newShaderResourceBindings();
345 p.srb->setBindings({QRhiShaderResourceBinding::sampledTexture(
346 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler)});
349 p.pipeline = rhi.newGraphicsPipeline();
350 p.pipeline->setShaderStages({
351 {QRhiShaderStage::Vertex, vs}, {QRhiShaderStage::Fragment, fs}});
352 p.pipeline->setVertexInputLayout({});
353 p.pipeline->setShaderResourceBindings(p.srb);
354 p.pipeline->setRenderPassDescriptor(p.rp);
355 p.pipeline->create();
358 void execPlane(QRhi& rhi, QRhiCommandBuffer& cb, PlaneResources& p)
360 cb.beginPass(p.rt, Qt::black, {0.0f, 0});
361 cb.setGraphicsPipeline(p.pipeline);
362 cb.setShaderResources(p.srb);
363 cb.setViewport(QRhiViewport(0, 0, p.w, p.h));
365 if(m_readbackEnabled)
367 auto* batch = rhi.nextResourceUpdateBatch();
368 batch->readBackTexture(QRhiReadbackDescription{p.texture}, &p.readback);
378 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
379 int height,
const QString& colorConversion)
override
383 m_sampler = rhi.newSampler(
384 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
385 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
390 const QString y = QString::fromLatin1(y_frag)
391 .arg(colorConversion)
392 .arg(QString::fromLatin1(common));
393 const QString uv = QString::fromLatin1(uv_frag)
394 .arg(colorConversion)
395 .arg(QString::fromLatin1(common));
396 initPlane(rhi, state, inputRGBA, m_y, width / 2, height, y);
397 initPlane(rhi, state, inputRGBA, m_uv, width / 2, height, uv);
400 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
402 execPlane(rhi, cb, m_y);
403 execPlane(rhi, cb, m_uv);
408 const QRhiReadbackResult&
readback(
int plane)
const override
410 return plane == 0 ? m_y.readback : m_uv.readback;
415 return plane == 0 ? m_y.texture : m_uv.texture;
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:1303
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
GPU RGBA -> p216 encoder (semi-planar 4:2:2, 16-bit).
Definition P216.hpp:40
const QRhiReadbackResult & readback(int plane) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition P216.hpp:199
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition P216.hpp:162
void setReadbackEnabled(bool e) noexcept override
Definition P216.hpp:211
QRhiTexture * planeTexture(int plane) const noexcept
Definition P216.hpp:206
void init(QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override
Definition P216.hpp:142
void release() override
Release all GPU resources.
Definition P216.hpp:213
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition P216.hpp:197
Global state associated to a rendering context.
Definition RenderState.hpp:37