2#include <Gfx/Graph/encoders/GPUVideoEncoder.hpp>
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;
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)
47 return vec2(tc.x, 1.0 - tc.y);
51 // One 16-bit sample as two RGBA8 bytes, little endian. The sample is the
52 // 8-bit code shifted left by 8 (see SCORE_GFX_MSB_ALIGNED_SCALE), so
53 // limited-range white is 235 << 8 == 60160. Rounded before the split: a
54 // fractional low byte would be rounded on its own by the UNORM write and
55 // could carry into nothing.
56 vec2 split16(float v) {
57 float u = floor(clamp(v, 0.0, 1.0) * 65280.0 + 0.5);
58 float hi = floor(u / 256.0);
59 float lo = u - hi * 256.0;
60 return vec2(lo / 255.0, hi / 255.0);
64 vec2 srcSz = vec2(textureSize(src_tex, 0));
65 float outRows = srcSz.y * 2.0;
67 // Which row of the framestore this fragment is on, and therefore which
68 // plane: the luma rows come first, then the chroma rows.
69 float outRow = floor(v_texcoord.y * outRows);
70 bool chroma = outRow >= srcSz.y;
71 float row = chroma ? outRow - srcSz.y : outRow;
73 // The source row, with the Y-flip folded in as everywhere else.
74 float sy = (flip_y(vec2(0.0, (row + 0.5) / srcSz.y))).y;
76 // Two source pixels per output texel, as for UYVY.
77 float halfW = srcSz.x * 0.5;
78 // floor() the product, not the product minus half a texel: at output
79 // texel i the interpolated value is exactly i + 0.5, so flooring it has
80 // half a texel of slack either way where flooring i itself has none.
81 float outPixel = floor(v_texcoord.x * halfW);
82 float sx0 = (outPixel * 2.0 + 0.5) / srcSz.x;
83 float sx1 = (outPixel * 2.0 + 1.5) / srcSz.x;
85 vec3 yuv0 = convert_from_rgb(texture(src_tex, vec2(sx0, sy)).rgb);
86 vec3 yuv1 = convert_from_rgb(texture(src_tex, vec2(sx1, sy)).rgb);
89 // One (Cb, Cr) pair per chroma site, averaged across the pixel pair.
90 float u = (yuv0.y + yuv1.y) * 0.5;
91 float v = (yuv0.z + yuv1.z) * 0.5;
92 fragColor = vec4(split16(u), split16(v));
95 fragColor = vec4(split16(yuv0.x), split16(yuv1.x));
100 QRhiTexture* m_outTexture{};
101 QRhiTextureRenderTarget* m_renderTarget{};
102 QRhiRenderPassDescriptor* m_rpDesc{};
103 QRhiSampler* m_sampler{};
104 QRhiShaderResourceBindings* m_srb{};
105 QRhiGraphicsPipeline* m_pipeline{};
106 QRhiReadbackResult m_readback{};
109 bool m_readbackEnabled{
true};
112 QRhi& rhi,
const RenderState& state, QRhiTexture* inputRGBA,
int width,
113 int height,
const QString& colorConversion)
override
120 m_outTexture = rhi.newTexture(
121 QRhiTexture::RGBA8, QSize{width / 2, height * 2}, 1,
122 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
123 m_outTexture->create();
125 m_renderTarget = rhi.newTextureRenderTarget({m_outTexture});
126 m_rpDesc = m_renderTarget->newCompatibleRenderPassDescriptor();
127 m_renderTarget->setRenderPassDescriptor(m_rpDesc);
128 m_renderTarget->create();
132 m_sampler = rhi.newSampler(
133 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
134 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
137 m_srb = rhi.newShaderResourceBindings();
139 QRhiShaderResourceBinding::sampledTexture(
140 3, QRhiShaderResourceBinding::FragmentStage, inputRGBA, m_sampler),
146 QString::fromLatin1(frag).arg(colorConversion));
148 m_pipeline = rhi.newGraphicsPipeline();
149 m_pipeline->setShaderStages({
150 {QRhiShaderStage::Vertex, vertS},
151 {QRhiShaderStage::Fragment, fragS},
153 m_pipeline->setVertexInputLayout({});
154 m_pipeline->setShaderResourceBindings(m_srb);
155 m_pipeline->setRenderPassDescriptor(m_rpDesc);
156 m_pipeline->create();
159 void exec(QRhi& rhi, QRhiCommandBuffer& cb)
override
161 cb.beginPass(m_renderTarget, Qt::black, {0.0f, 0});
162 cb.setGraphicsPipeline(m_pipeline);
163 cb.setShaderResources(m_srb);
164 cb.setViewport(QRhiViewport(0, 0, m_width / 2, m_height * 2));
167 if(m_readbackEnabled)
169 auto* readbackBatch = rhi.nextResourceUpdateBatch();
170 QRhiReadbackDescription rb(m_outTexture);
171 readbackBatch->readBackTexture(rb, &m_readback);
172 cb.endPass(readbackBatch);
182 const QRhiReadbackResult&
readback(
int)
const override {
return m_readback; }
183 QRhiTexture*
outputTexture() const noexcept
override {
return m_outTexture; }
189 m_pipeline =
nullptr;
196 delete m_renderTarget;
197 m_renderTarget =
nullptr;
199 m_outTexture =
nullptr;
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:1707
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:108
void exec(QRhi &rhi, QRhiCommandBuffer &cb) override
Definition P216Packed.hpp:156
void release() override
Release all GPU resources.
Definition P216Packed.hpp:183
void setReadbackEnabled(bool e) noexcept override
Definition P216Packed.hpp:181
QRhiTexture * outputTexture() const noexcept override
Definition P216Packed.hpp:180
int planeCount() const override
Number of readback planes (1 for UYVY, 2 for NV12, 3 for I420).
Definition P216Packed.hpp:177
const QRhiReadbackResult & readback(int) const override
Get the readback result for a given plane. Valid after endOffscreenFrame.
Definition P216Packed.hpp:179