Loading...
Searching...
No Matches
I420.hpp
1#pragma once
2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
3
4namespace score::gfx
5{
6
22{
23 // %1 = colorMatrixOut() shader defining convert_from_rgb(vec3)
24 static constexpr const char* y_frag = R"_(#version 450
25 layout(location = 0) in vec2 v_texcoord;
26 layout(location = 0) out vec4 fragColor;
27 layout(binding = 3) uniform sampler2D src_tex;
28 )_" "%1" R"_(
29 vec2 flip_y(vec2 tc) {
30 // Only OpenGL. The rest of the engine puts its geometry through
31 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
32 // a hardcoded triangle in raw NDC and does not, so the correction it needs
33 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
34 // NDI and every other consumer an upside-down picture.
35 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
36 return tc;
37 #else
38 return vec2(tc.x, 1.0 - tc.y);
39 #endif
40 }
41 void main() {
42 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
43 vec3 yuv = convert_from_rgb(rgb);
44 fragColor = vec4(yuv.x, 0.0, 0.0, 1.0);
45 }
46 )_";
47
48 static constexpr const char* u_frag = R"_(#version 450
49 layout(location = 0) in vec2 v_texcoord;
50 layout(location = 0) out vec4 fragColor;
51 layout(binding = 3) uniform sampler2D src_tex;
52 )_" "%1" R"_(
53 vec2 flip_y(vec2 tc) {
54 // Only OpenGL. The rest of the engine puts its geometry through
55 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
56 // a hardcoded triangle in raw NDC and does not, so the correction it needs
57 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
58 // NDI and every other consumer an upside-down picture.
59 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
60 return tc;
61 #else
62 return vec2(tc.x, 1.0 - tc.y);
63 #endif
64 }
65 void main() {
66 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
67 vec3 yuv = convert_from_rgb(rgb);
68 fragColor = vec4(yuv.y, 0.0, 0.0, 1.0);
69 }
70 )_";
71
72 static constexpr const char* v_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;
76 )_" "%1" R"_(
77 vec2 flip_y(vec2 tc) {
78 // Only OpenGL. The rest of the engine puts its geometry through
79 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
80 // a hardcoded triangle in raw NDC and does not, so the correction it needs
81 // is not the same one. Flipping on Vulkan as well handed libav, GStreamer,
82 // NDI and every other consumer an upside-down picture.
83 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
84 return tc;
85 #else
86 return vec2(tc.x, 1.0 - tc.y);
87 #endif
88 }
89 void main() {
90 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
91 vec3 yuv = convert_from_rgb(rgb);
92 fragColor = vec4(yuv.z, 0.0, 0.0, 1.0);
93 }
94 )_";
95
96 struct PlaneResources
97 {
98 QRhiTexture* texture{};
99 QRhiTextureRenderTarget* rt{};
100 QRhiRenderPassDescriptor* rp{};
101 QRhiShaderResourceBindings* srb{};
102 QRhiGraphicsPipeline* pipeline{};
103 QRhiReadbackResult readback{};
104
105 void destroy()
106 {
107 delete pipeline;
108 delete srb;
109 delete rp;
110 delete rt;
111 delete texture;
112 pipeline = nullptr;
113 srb = nullptr;
114 rp = nullptr;
115 rt = nullptr;
116 texture = nullptr;
117 }
118 };
119
120 PlaneResources m_planes[3]; // Y, U, V
121 QRhiSampler* m_sampler{};
122 int m_width{};
123 int m_height{};
124
125 void initPlane(
126 QRhi& rhi, const RenderState& state, QRhiTexture* inputRGBA,
127 PlaneResources& plane, int w, int h, const char* fragTemplate,
128 const QString& colorConversion)
129 {
130 plane.texture = rhi.newTexture(
131 QRhiTexture::R8, QSize{w, h}, 1,
132 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
133 plane.texture->create();
134
135 plane.rt = rhi.newTextureRenderTarget({plane.texture});
136 plane.rp = plane.rt->newCompatibleRenderPassDescriptor();
137 plane.rt->setRenderPassDescriptor(plane.rp);
138 plane.rt->create();
139
140 plane.srb = rhi.newShaderResourceBindings();
141 plane.srb->setBindings({
142 QRhiShaderResourceBinding::sampledTexture(
143 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
144 });
145 plane.srb->create();
146
147 auto [vs, fs] = makeShaders(
148 state, QString::fromLatin1(vertex_shader),
149 QString::fromLatin1(fragTemplate).arg(colorConversion));
150 plane.pipeline = rhi.newGraphicsPipeline();
151 plane.pipeline->setShaderStages({
152 {QRhiShaderStage::Vertex, vs},
153 {QRhiShaderStage::Fragment, fs},
154 });
155 plane.pipeline->setVertexInputLayout({});
156 plane.pipeline->setShaderResourceBindings(plane.srb);
157 plane.pipeline->setRenderPassDescriptor(plane.rp);
158 plane.pipeline->create();
159 }
160
161 void execPlane(QRhi& rhi, QRhiCommandBuffer& cb, PlaneResources& plane, int w, int h)
162 {
163 cb.beginPass(plane.rt, Qt::black, {0.0f, 0});
164 cb.setGraphicsPipeline(plane.pipeline);
165 cb.setShaderResources(plane.srb);
166 cb.setViewport(QRhiViewport(0, 0, w, h));
167 cb.draw(3);
168
169 auto* batch = rhi.nextResourceUpdateBatch();
170 batch->readBackTexture(QRhiReadbackDescription{plane.texture}, &plane.readback);
171 cb.endPass(batch);
172 }
173
174 void init(
175 QRhi& rhi, const RenderState& state, QRhiTexture* inputRGBA, int width,
176 int height, const QString& colorConversion) override
177 {
178 m_width = width;
179 m_height = height;
180
181 m_sampler = rhi.newSampler(
182 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
183 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
184 m_sampler->create();
185
186 initPlane(rhi, state, inputRGBA, m_planes[0], width, height, y_frag, colorConversion);
187 initPlane(rhi, state, inputRGBA, m_planes[1], width / 2, height / 2, u_frag, colorConversion);
188 initPlane(rhi, state, inputRGBA, m_planes[2], width / 2, height / 2, v_frag, colorConversion);
190
191 void exec(QRhi& rhi, QRhiCommandBuffer& cb) override
192 {
193 execPlane(rhi, cb, m_planes[0], m_width, m_height);
194 execPlane(rhi, cb, m_planes[1], m_width / 2, m_height / 2);
195 execPlane(rhi, cb, m_planes[2], m_width / 2, m_height / 2);
197
198 int planeCount() const override { return 3; }
199
200 const QRhiReadbackResult& readback(int plane) const override
201 {
202 return m_planes[plane].readback;
203 }
204
205 void release() override
206 {
207 for(auto& p : m_planes)
208 p.destroy();
209 delete m_sampler;
210 m_sampler = nullptr;
211 }
212};
213
214} // namespace score::gfx
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
GPU RGBA->I420 encoder (planar 4:2:0).
Definition I420.hpp:22
void init(QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override
Definition I420.hpp:165
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition I420.hpp:182
const QRhiReadbackResult & readback(int plane) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition I420.hpp:191
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition I420.hpp:189
void release() override
Release all GPU resources.
Definition I420.hpp:196