2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
34#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
39 static constexpr const char* plane_frag = R
"_(#version 450
40 layout(location = 0) in vec2 v_texcoord;
41 layout(location = 0) out vec4 fragColor;
42 layout(binding = 3) uniform sampler2D src_tex;
44 vec2 flip_y(vec2 tc) {
45 // Only OpenGL. The rest of the engine puts its geometry through
46 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
47 // a hardcoded triangle in raw NDC and does not, so the correction it needs
48 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
49 // NDI and every other consumer an upside-down picture.
50 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
53 return vec2(tc.x, 1.0 - tc.y);
57 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
58 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
59 // 10-bit value packed into the low 10 bits of the R16 word.
60 fragColor = vec4(yuv.%2 * (1023.0 / 65535.0), 0.0, 0.0, 1.0);
66 QRhiTexture* texture{};
67 QRhiTextureRenderTarget* rt{};
68 QRhiRenderPassDescriptor* rp{};
69 QRhiShaderResourceBindings* srb{};
70 QRhiGraphicsPipeline* pipeline{};
71 QRhiReadbackResult readback{};
89 QRhiSampler* m_sampler{};
94 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
95 PlaneResources& plane,
int w,
int h,
const char* component,
96 const QString& colorConversion)
98 plane.texture = rhi.newTexture(
99 QRhiTexture::R16, QSize{w, h}, 1,
100 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
101 plane.texture->create();
103 plane.rt = rhi.newTextureRenderTarget({plane.texture});
104 plane.rp = plane.rt->newCompatibleRenderPassDescriptor();
105 plane.rt->setRenderPassDescriptor(plane.rp);
108 plane.srb = rhi.newShaderResourceBindings();
109 plane.srb->setBindings({
110 QRhiShaderResourceBinding::sampledTexture(
111 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
117 QString::fromLatin1(plane_frag).arg(colorConversion).arg(component));
118 plane.pipeline = rhi.newGraphicsPipeline();
119 plane.pipeline->setShaderStages({
120 {QRhiShaderStage::Vertex, vs},
121 {QRhiShaderStage::Fragment, fs},
123 plane.pipeline->setVertexInputLayout({});
124 plane.pipeline->setShaderResourceBindings(plane.srb);
125 plane.pipeline->setRenderPassDescriptor(plane.rp);
126 plane.pipeline->create();
130 execPlane(QRhi& rhi, QRhiCommandBuffer& cb, PlaneResources& plane,
int w,
int h)
132 cb.beginPass(plane.rt, Qt::black, {0.0f, 0});
133 cb.setGraphicsPipeline(plane.pipeline);
134 cb.setShaderResources(plane.srb);
135 cb.setViewport(QRhiViewport(0, 0, w, h));
138 auto* batch = rhi.nextResourceUpdateBatch();
139 batch->readBackTexture(QRhiReadbackDescription{plane.texture}, &plane.readback);
144 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
145 int height,
const QString& colorConversion)
override
150 m_sampler = rhi.newSampler(
151 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
152 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
156 initPlane(rhi, state, inputRGBA, m_planes[0], width, height,
"x", colorConversion);
157 initPlane(rhi, state, inputRGBA, m_planes[1], width / 2, height,
"y", colorConversion);
158 initPlane(rhi, state, inputRGBA, m_planes[2], width / 2, height,
"z", colorConversion);
161 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
163 execPlane(rhi, cb, m_planes[0], m_width, m_height);
164 execPlane(rhi, cb, m_planes[1], m_width / 2, m_height);
165 execPlane(rhi, cb, m_planes[2], m_width / 2, m_height);
170 const QRhiReadbackResult&
readback(
int plane)
const override
172 return m_planes[plane].readback;
177 for(
auto& p : m_planes)
184struct YUV422P10Encoder : GPUVideoEncoder
187 static constexpr const char* common = R
"_(
188 int flip_y_int(int y, int h) {
189 // See GPUVideoEncoder::y_flip_glsl: only OpenGL. The rest of the engine
190 // negates Y through renderer.clipSpaceCorrMatrix on Vulkan; this pass
191 // indexes texels directly and does not, so it must not flip there.
192 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
198 uint comp10(vec3 rgb, int c) {
199 vec3 yuv = clamp(convert_from_rgb(rgb), 0.0, 1.0);
200 float v = (c == 0) ? yuv.x : (c == 1 ? yuv.y : yuv.z);
201 return uint(v * 1023.0 + 0.5);
203 // Two 16-bit values -> 4 little-endian bytes in an RGBA8 texel.
204 vec4 pack2(uint a, uint b) {
205 return vec4(float(a & 0xFFu), float((a >> 8u) & 0xFFu),
206 float(b & 0xFFu), float((b >> 8u) & 0xFFu)) / 255.0;
211 static constexpr const char* y_frag = R
"_(#version 450
212 layout(location = 0) in vec2 v_texcoord;
213 layout(location = 0) out vec4 fragColor;
214 layout(binding = 3) uniform sampler2D src_tex;
218 ivec2 sz = textureSize(src_tex, 0);
219 ivec2 o = ivec2(gl_FragCoord.xy);
220 int sy = flip_y_int(o.y, sz.y);
222 uint a = comp10(texelFetch(src_tex, ivec2(min(x0, sz.x - 1), sy), 0).rgb, 0);
223 uint b = comp10(texelFetch(src_tex, ivec2(min(x0 + 1, sz.x - 1), sy), 0).rgb, 0);
224 fragColor = pack2(a, b);
230 static constexpr const char* uv_frag = R
"_(#version 450
231 layout(location = 0) in vec2 v_texcoord;
232 layout(location = 0) out vec4 fragColor;
233 layout(binding = 3) uniform sampler2D src_tex;
236 uint chroma(int x, int sy, ivec2 sz) {
237 uint a = comp10(texelFetch(src_tex, ivec2(min(x, sz.x - 1), sy), 0).rgb, %3);
238 uint b = comp10(texelFetch(src_tex, ivec2(min(x + 1, sz.x - 1), sy), 0).rgb, %3);
239 return (a + b) >> 1u;
242 ivec2 sz = textureSize(src_tex, 0);
243 ivec2 o = ivec2(gl_FragCoord.xy);
244 int sy = flip_y_int(o.y, sz.y);
246 fragColor = pack2(chroma(x0, sy, sz), chroma(x0 + 2, sy, sz));
250 struct PlaneResources
252 QRhiTexture* texture{};
253 QRhiTextureRenderTarget* rt{};
254 QRhiRenderPassDescriptor* rp{};
255 QRhiShaderResourceBindings* srb{};
256 QRhiGraphicsPipeline* pipeline{};
257 QRhiReadbackResult readback{};
262 delete pipeline;
delete srb;
delete rp;
delete rt;
delete texture;
263 pipeline =
nullptr; srb =
nullptr; rp =
nullptr; rt =
nullptr; texture =
nullptr;
267 PlaneResources m_planes[3];
268 QRhiSampler* m_sampler{};
273 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
274 PlaneResources& plane,
int packW,
int packH,
const QString& frag)
278 plane.texture = rhi.newTexture(
279 QRhiTexture::RGBA8, QSize{packW, packH}, 1,
280 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
281 plane.texture->create();
283 plane.rt = rhi.newTextureRenderTarget({plane.texture});
284 plane.rp = plane.rt->newCompatibleRenderPassDescriptor();
285 plane.rt->setRenderPassDescriptor(plane.rp);
288 plane.srb = rhi.newShaderResourceBindings();
289 plane.srb->setBindings({
290 QRhiShaderResourceBinding::sampledTexture(
291 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
297 plane.pipeline = rhi.newGraphicsPipeline();
298 plane.pipeline->setShaderStages({
299 {QRhiShaderStage::Vertex, vs}, {QRhiShaderStage::Fragment, fs}});
300 plane.pipeline->setVertexInputLayout({});
301 plane.pipeline->setShaderResourceBindings(plane.srb);
302 plane.pipeline->setRenderPassDescriptor(plane.rp);
303 plane.pipeline->create();
306 void execPlane(QRhi& rhi, QRhiCommandBuffer& cb, PlaneResources& p)
308 cb.beginPass(p.rt, Qt::black, {0.0f, 0});
309 cb.setGraphicsPipeline(p.pipeline);
310 cb.setShaderResources(p.srb);
311 cb.setViewport(QRhiViewport(0, 0, p.w, p.h));
313 auto* batch = rhi.nextResourceUpdateBatch();
314 batch->readBackTexture(QRhiReadbackDescription{p.texture}, &p.readback);
319 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
320 int height,
const QString& colorConversion)
override
324 m_sampler = rhi.newSampler(
325 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
326 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
329 const QString cm = QString::fromLatin1(common);
330 const QString y = QString::fromLatin1(y_frag).arg(colorConversion, cm);
332 = QString::fromLatin1(uv_frag).arg(colorConversion, cm, QStringLiteral(
"1"));
334 = QString::fromLatin1(uv_frag).arg(colorConversion, cm, QStringLiteral(
"2"));
337 initPlane(rhi, state, inputRGBA, m_planes[0], width / 2, height, y);
338 initPlane(rhi, state, inputRGBA, m_planes[1], width / 4, height, u);
339 initPlane(rhi, state, inputRGBA, m_planes[2], width / 4, height, v);
342 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
344 execPlane(rhi, cb, m_planes[0]);
345 execPlane(rhi, cb, m_planes[1]);
346 execPlane(rhi, cb, m_planes[2]);
351 const QRhiReadbackResult&
readback(
int plane)
const override
353 return m_planes[plane].readback;
358 for(
auto& p : m_planes)
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
Global state associated to a rendering context.
Definition RenderState.hpp:37
Definition encoders/YUV422P10.hpp:62
GPU RGBA -> yuv422p10le encoder (planar 4:2:2, 10-bit).
Definition encoders/YUV422P10.hpp:36
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition encoders/YUV422P10.hpp:165
void release() override
Release all GPU resources.
Definition encoders/YUV422P10.hpp:172
const QRhiReadbackResult & readback(int plane) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition encoders/YUV422P10.hpp:167
void init(QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override
Definition encoders/YUV422P10.hpp:140
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition encoders/YUV422P10.hpp:158