Loading...
Searching...
No Matches
encoders/NV12.hpp
1#pragma once
2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
3
4namespace score::gfx
5{
6
18{
19 // %1 = colorMatrixOut() shader defining convert_from_rgb(vec3)
20 static constexpr const char* y_frag = R"_(#version 450
21 layout(location = 0) in vec2 v_texcoord;
22 layout(location = 0) out vec4 fragColor;
23 layout(binding = 3) uniform sampler2D src_tex;
24 )_" "%1" R"_(
25 vec2 flip_y(vec2 tc) {
26 #if defined(QSHADER_MSL) || defined(QSHADER_HLSL)
27 return tc;
28 #else
29 return vec2(tc.x, 1.0 - tc.y);
30 #endif
31 }
32 void main() {
33 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
34 vec3 yuv = convert_from_rgb(rgb);
35 fragColor = vec4(yuv.x, 0.0, 0.0, 1.0);
36 }
37 )_";
38
39 // Rendered at half resolution. Bilinear sampling averages the 2x2 block.
40 static constexpr const char* uv_frag = R"_(#version 450
41 layout(location = 0) in vec2 v_texcoord;
42 layout(location = 0) out vec4 fragColor;
43 layout(binding = 3) uniform sampler2D src_tex;
44 )_" "%1" R"_(
45 vec2 flip_y(vec2 tc) {
46 #if defined(QSHADER_MSL) || defined(QSHADER_HLSL)
47 return tc;
48 #else
49 return vec2(tc.x, 1.0 - tc.y);
50 #endif
51 }
52 void main() {
53 vec3 rgb = texture(src_tex, flip_y(v_texcoord)).rgb;
54 vec3 yuv = convert_from_rgb(rgb);
55 fragColor = vec4(yuv.y, yuv.z, 0.0, 1.0);
56 }
57 )_";
58
59 // Y plane resources
60 QRhiTexture* m_yTexture{};
61 QRhiTextureRenderTarget* m_yRT{};
62 QRhiRenderPassDescriptor* m_yRP{};
63 QRhiShaderResourceBindings* m_ySRB{};
64 QRhiGraphicsPipeline* m_yPipeline{};
65 QRhiReadbackResult m_yReadback{};
66
67 // UV plane resources
68 QRhiTexture* m_uvTexture{};
69 QRhiTextureRenderTarget* m_uvRT{};
70 QRhiRenderPassDescriptor* m_uvRP{};
71 QRhiShaderResourceBindings* m_uvSRB{};
72 QRhiGraphicsPipeline* m_uvPipeline{};
73 QRhiReadbackResult m_uvReadback{};
75 // Shared
76 QRhiSampler* m_sampler{};
77 int m_width{};
78 int m_height{};
79
80 void init(
81 QRhi& rhi, const RenderState& state, QRhiTexture* inputRGBA, int width,
82 int height, const QString& colorConversion = colorMatrixOut()) override
83 {
84 m_width = width;
85 m_height = height;
86
87 m_sampler = rhi.newSampler(
88 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
89 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
90 m_sampler->create();
91
92 auto vertSrc = QString::fromLatin1(vertex_shader);
93
94 // Y plane setup
95 {
96 m_yTexture = rhi.newTexture(
97 QRhiTexture::R8, QSize{width, height}, 1,
98 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
99 m_yTexture->create();
100
101 m_yRT = rhi.newTextureRenderTarget({m_yTexture});
102 m_yRP = m_yRT->newCompatibleRenderPassDescriptor();
103 m_yRT->setRenderPassDescriptor(m_yRP);
104 m_yRT->create();
105
106 m_ySRB = rhi.newShaderResourceBindings();
107 m_ySRB->setBindings({
108 QRhiShaderResourceBinding::sampledTexture(
109 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
110 });
111 m_ySRB->create();
112
113 auto [vs, fs] = makeShaders(
114 state, vertSrc, QString::fromLatin1(y_frag).arg(colorConversion));
115 m_yPipeline = rhi.newGraphicsPipeline();
116 m_yPipeline->setShaderStages({
117 {QRhiShaderStage::Vertex, vs},
118 {QRhiShaderStage::Fragment, fs},
119 });
120 m_yPipeline->setVertexInputLayout({});
121 m_yPipeline->setShaderResourceBindings(m_ySRB);
122 m_yPipeline->setRenderPassDescriptor(m_yRP);
123 m_yPipeline->create();
124 }
125
126 // UV plane setup
127 {
128 m_uvTexture = rhi.newTexture(
129 QRhiTexture::RG8, QSize{width / 2, height / 2}, 1,
130 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
131 m_uvTexture->create();
132
133 m_uvRT = rhi.newTextureRenderTarget({m_uvTexture});
134 m_uvRP = m_uvRT->newCompatibleRenderPassDescriptor();
135 m_uvRT->setRenderPassDescriptor(m_uvRP);
136 m_uvRT->create();
137
138 m_uvSRB = rhi.newShaderResourceBindings();
139 m_uvSRB->setBindings({
140 QRhiShaderResourceBinding::sampledTexture(
141 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
142 });
143 m_uvSRB->create();
144
145 auto [vs, fs]
146 = makeShaders(state, vertSrc, QString::fromLatin1(uv_frag).arg(colorConversion));
147 m_uvPipeline = rhi.newGraphicsPipeline();
148 m_uvPipeline->setShaderStages({
149 {QRhiShaderStage::Vertex, vs},
150 {QRhiShaderStage::Fragment, fs},
151 });
152 m_uvPipeline->setVertexInputLayout({});
153 m_uvPipeline->setShaderResourceBindings(m_uvSRB);
154 m_uvPipeline->setRenderPassDescriptor(m_uvRP);
155 m_uvPipeline->create();
156 }
157 }
158
159 void exec(QRhi& rhi, QRhiCommandBuffer& cb) override
160 {
161 // Pass 1: Y plane (full resolution)
162 cb.beginPass(m_yRT, Qt::black, {1.0f, 0});
163 cb.setGraphicsPipeline(m_yPipeline);
164 cb.setShaderResources(m_ySRB);
165 cb.setViewport(QRhiViewport(0, 0, m_width, m_height));
166 cb.draw(3);
167
168 auto* yReadbackBatch = rhi.nextResourceUpdateBatch();
169 yReadbackBatch->readBackTexture(QRhiReadbackDescription{m_yTexture}, &m_yReadback);
170 cb.endPass(yReadbackBatch);
171
172 // Pass 2: UV plane (half resolution)
173 cb.beginPass(m_uvRT, Qt::black, {1.0f, 0});
174 cb.setGraphicsPipeline(m_uvPipeline);
175 cb.setShaderResources(m_uvSRB);
176 cb.setViewport(QRhiViewport(0, 0, m_width / 2, m_height / 2));
177 cb.draw(3);
178
179 auto* uvReadbackBatch = rhi.nextResourceUpdateBatch();
180 uvReadbackBatch->readBackTexture(
181 QRhiReadbackDescription{m_uvTexture}, &m_uvReadback);
182 cb.endPass(uvReadbackBatch);
183 }
184
185 int planeCount() const override { return 2; }
187 const QRhiReadbackResult& readback(int plane) const override
188 {
189 return plane == 0 ? m_yReadback : m_uvReadback;
190 }
191
192 void release() override
193 {
194 delete m_uvPipeline;
195 delete m_uvSRB;
196 delete m_uvRP;
197 delete m_uvRT;
198 delete m_uvTexture;
199 delete m_yPipeline;
200 delete m_ySRB;
201 delete m_yRP;
202 delete m_yRT;
203 delete m_yTexture;
204 delete m_sampler;
205 m_uvPipeline = m_yPipeline = nullptr;
206 m_uvSRB = m_ySRB = nullptr;
207 m_uvRP = m_yRP = nullptr;
208 m_uvRT = m_yRT = nullptr;
209 m_uvTexture = m_yTexture = nullptr;
210 m_sampler = nullptr;
211 }
212};
213
214} // namespace score::gfx
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:395
Base class for GPU-side video format conversion (RGBA to YUV).
Definition GPUVideoEncoder.hpp:30
static constexpr const char * vertex_shader
Definition GPUVideoEncoder.hpp:63
GPU RGBA->NV12 encoder (semi-planar 4:2:0).
Definition encoders/NV12.hpp:18
void release() override
Release all GPU resources.
Definition encoders/NV12.hpp:186
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition encoders/NV12.hpp:153
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition encoders/NV12.hpp:179
const QRhiReadbackResult & readback(int plane) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition encoders/NV12.hpp:181