Loading...
Searching...
No Matches
P216Packed.hpp
1#pragma once
2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
3
4namespace score::gfx
5{
6
31{
32 // %1 = colorMatrixOut() shader defining convert_from_rgb(vec3)
33 static constexpr const char* frag = R"_(#version 450
34 layout(location = 0) in vec2 v_texcoord;
35 layout(location = 0) out vec4 fragColor;
36 layout(binding = 3) uniform sampler2D src_tex;
37 )_" "%1" R"_(
38
39 vec2 flip_y(vec2 tc) {
40 // Only OpenGL. The rest of the engine puts its geometry through
41 // renderer.clipSpaceCorrMatrix, which negates Y on Vulkan; this pass draws
42 // a hardcoded triangle in raw NDC and does not, so the correction it needs
43 // is not the same one.
44 #if defined(QSHADER_SPIRV) || defined(QSHADER_MSL) || defined(QSHADER_HLSL)
45 return tc;
46 #else
47 return vec2(tc.x, 1.0 - tc.y);
48 #endif
49 }
50
51 // One 16-bit value as two RGBA8 bytes, little endian.
52 vec2 split16(float v) {
53 float u = clamp(v, 0.0, 1.0) * 65535.0;
54 float hi = floor(u / 256.0);
55 float lo = u - hi * 256.0;
56 return vec2(lo / 255.0, hi / 255.0);
57 }
58
59 void main() {
60 vec2 srcSz = vec2(textureSize(src_tex, 0));
61 float outRows = srcSz.y * 2.0;
62
63 // Which row of the framestore this fragment is on, and therefore which
64 // plane: the luma rows come first, then the chroma rows.
65 float outRow = floor(v_texcoord.y * outRows);
66 bool chroma = outRow >= srcSz.y;
67 float row = chroma ? outRow - srcSz.y : outRow;
68
69 // The source row, with the Y-flip folded in as everywhere else.
70 float sy = (flip_y(vec2(0.0, (row + 0.5) / srcSz.y))).y;
71
72 // Two source pixels per output texel, as for UYVY.
73 float halfW = srcSz.x * 0.5;
74 // floor() the product, not the product minus half a texel: at output
75 // texel i the interpolated value is exactly i + 0.5, so flooring it has
76 // half a texel of slack either way where flooring i itself has none.
77 float outPixel = floor(v_texcoord.x * halfW);
78 float sx0 = (outPixel * 2.0 + 0.5) / srcSz.x;
79 float sx1 = (outPixel * 2.0 + 1.5) / srcSz.x;
80
81 vec3 yuv0 = convert_from_rgb(texture(src_tex, vec2(sx0, sy)).rgb);
82 vec3 yuv1 = convert_from_rgb(texture(src_tex, vec2(sx1, sy)).rgb);
83
84 if(chroma) {
85 // One (Cb, Cr) pair per chroma site, averaged across the pixel pair.
86 float u = (yuv0.y + yuv1.y) * 0.5;
87 float v = (yuv0.z + yuv1.z) * 0.5;
88 fragColor = vec4(split16(u), split16(v));
89 } else {
90 // Two luma samples.
91 fragColor = vec4(split16(yuv0.x), split16(yuv1.x));
92 }
93 }
94 )_";
95
96 QRhiTexture* m_outTexture{};
97 QRhiTextureRenderTarget* m_renderTarget{};
98 QRhiRenderPassDescriptor* m_rpDesc{};
99 QRhiSampler* m_sampler{};
100 QRhiShaderResourceBindings* m_srb{};
101 QRhiGraphicsPipeline* m_pipeline{};
102 QRhiReadbackResult m_readback{};
103 int m_width{};
104 int m_height{};
105 bool m_readbackEnabled{true};
106
107 void init(
108 QRhi& rhi, const RenderState& state, QRhiTexture* inputRGBA, int width,
109 int height, const QString& colorConversion) override
110 {
111 m_width = width;
112 m_height = height;
113
114 // The framestore: half width because each texel carries two 16-bit values,
115 // twice the height because luma and chroma planes are stacked.
116 m_outTexture = rhi.newTexture(
117 QRhiTexture::RGBA8, QSize{width / 2, height * 2}, 1,
118 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
119 m_outTexture->create();
120
121 m_renderTarget = rhi.newTextureRenderTarget({m_outTexture});
122 m_rpDesc = m_renderTarget->newCompatibleRenderPassDescriptor();
123 m_renderTarget->setRenderPassDescriptor(m_rpDesc);
124 m_renderTarget->create();
125
126 // Nearest: every fetch here is at an exact texel centre, and a linear tap
127 // would blend neighbouring pixels into the packed bytes.
128 m_sampler = rhi.newSampler(
129 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
130 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
131 m_sampler->create();
132
133 m_srb = rhi.newShaderResourceBindings();
134 m_srb->setBindings({
135 QRhiShaderResourceBinding::sampledTexture(
136 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
137 });
138 m_srb->create();
139
140 auto [vertS, fragS] = makeShaders(
141 state, QString::fromLatin1(vertex_shader),
142 QString::fromLatin1(frag).arg(colorConversion));
143
144 m_pipeline = rhi.newGraphicsPipeline();
145 m_pipeline->setShaderStages({
146 {QRhiShaderStage::Vertex, vertS},
147 {QRhiShaderStage::Fragment, fragS},
148 });
149 m_pipeline->setVertexInputLayout({});
150 m_pipeline->setShaderResourceBindings(m_srb);
151 m_pipeline->setRenderPassDescriptor(m_rpDesc);
152 m_pipeline->create();
153 }
154
155 void exec(QRhi& rhi, QRhiCommandBuffer& cb) override
156 {
157 cb.beginPass(m_renderTarget, Qt::black, {0.0f, 0});
158 cb.setGraphicsPipeline(m_pipeline);
159 cb.setShaderResources(m_srb);
160 cb.setViewport(QRhiViewport(0, 0, m_width / 2, m_height * 2));
161 cb.draw(3);
162
163 if(m_readbackEnabled)
164 {
165 auto* readbackBatch = rhi.nextResourceUpdateBatch();
166 QRhiReadbackDescription rb(m_outTexture);
167 readbackBatch->readBackTexture(rb, &m_readback);
168 cb.endPass(readbackBatch);
169 }
170 else
171 {
172 cb.endPass();
174 }
176 int planeCount() const override { return 1; }
178 const QRhiReadbackResult& readback(int) const override { return m_readback; }
179 QRhiTexture* outputTexture() const noexcept override { return m_outTexture; }
180 void setReadbackEnabled(bool e) noexcept override { m_readbackEnabled = e; }
181
182 void release() override
183 {
184 delete m_pipeline;
185 m_pipeline = nullptr;
186 delete m_srb;
187 m_srb = nullptr;
188 delete m_sampler;
189 m_sampler = nullptr;
190 delete m_rpDesc;
191 m_rpDesc = nullptr;
192 delete m_renderTarget;
193 m_renderTarget = nullptr;
194 delete m_outTexture;
195 m_outTexture = nullptr;
196 }
197};
198
199} // 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: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, as ONE texture holding the whole framestore.
Definition P216Packed.hpp:31
void init(QRhi &rhi, const RenderState &state, QRhiTexture *inputRGBA, int width, int height, const QString &colorConversion) override
Definition P216Packed.hpp:104
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition P216Packed.hpp:152
void release() override
Release all GPU resources.
Definition P216Packed.hpp:179
void setReadbackEnabled(bool e) noexcept override
Definition P216Packed.hpp:177
QRhiTexture * outputTexture() const noexcept override
Definition P216Packed.hpp:176
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition P216Packed.hpp:173
const QRhiReadbackResult & readback(int) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition P216Packed.hpp:175