Loading...
Searching...
No Matches
decoders/V210.hpp
1#pragma once
2#include <Gfx/Graph/decoders/ColorSpace.hpp>
3#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
4extern "C" {
5#include <libavformat/avformat.h>
6}
7
8namespace score::gfx
9{
38{
39 // %1 = user filter, %2 = colorMatrix() (emits convert_to_rgb)
40 static const constexpr auto frag = R"_(#version 450
41
42)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
43
44layout(binding=3) uniform sampler2D u_tex;
45
46layout(location = 0) in vec2 v_texcoord;
47layout(location = 0) out vec4 fragColor;
48
49%2
50
51vec4 processTexture(vec4 tex) {
52 vec4 processed = convert_to_rgb(tex);
53 { %1 }
54 return processed;
55}
56
57uint readWord(int wx, int y) {
58 // Sample the RGBA8 texel that holds one v210 ULWord, repack the four
59 // bytes into a little-endian uint32 we can bit-extract from.
60 vec4 t = texelFetch(u_tex, ivec2(wx, y), 0);
61 uint b0 = uint(t.r * 255.0 + 0.5);
62 uint b1 = uint(t.g * 255.0 + 0.5);
63 uint b2 = uint(t.b * 255.0 + 0.5);
64 uint b3 = uint(t.a * 255.0 + 0.5);
65 return b0 | (b1 << 8u) | (b2 << 16u) | (b3 << 24u);
66}
67
68void main() {
69 // Output coordinates are in source-pixel space. mat.texSz is the
70 // logical (full-width) frame size that downstream nodes see.
71 int outX = int(floor(v_texcoord.x * mat.texSz.x));
72 int outY = int(floor(v_texcoord.y * mat.texSz.y));
73
74 int groupIdx = outX / 6;
75 int pixInGroup = outX - groupIdx * 6;
76 int wordBaseX = groupIdx * 4; // 4 ULWords per 6-pixel group
77
78 uint y10 = 0u, cb10 = 0u, cr10 = 0u;
79
80 if (pixInGroup == 0) {
81 uint w0 = readWord(wordBaseX + 0, outY);
82 cb10 = w0 & 0x3FFu;
83 y10 = (w0 >> 10u) & 0x3FFu;
84 cr10 = (w0 >> 20u) & 0x3FFu;
85 } else if (pixInGroup == 1) {
86 uint w0 = readWord(wordBaseX + 0, outY);
87 uint w1 = readWord(wordBaseX + 1, outY);
88 cb10 = w0 & 0x3FFu; // shared with pixel 0
89 y10 = w1 & 0x3FFu;
90 cr10 = (w0 >> 20u) & 0x3FFu; // shared with pixel 0
91 } else if (pixInGroup == 2) {
92 uint w1 = readWord(wordBaseX + 1, outY);
93 uint w2 = readWord(wordBaseX + 2, outY);
94 cb10 = (w1 >> 10u) & 0x3FFu;
95 y10 = (w1 >> 20u) & 0x3FFu;
96 cr10 = w2 & 0x3FFu;
97 } else if (pixInGroup == 3) {
98 uint w1 = readWord(wordBaseX + 1, outY);
99 uint w2 = readWord(wordBaseX + 2, outY);
100 cb10 = (w1 >> 10u) & 0x3FFu; // shared with pixel 2
101 y10 = (w2 >> 10u) & 0x3FFu;
102 cr10 = w2 & 0x3FFu; // shared with pixel 2
103 } else if (pixInGroup == 4) {
104 uint w2 = readWord(wordBaseX + 2, outY);
105 uint w3 = readWord(wordBaseX + 3, outY);
106 cb10 = (w2 >> 20u) & 0x3FFu;
107 y10 = w3 & 0x3FFu;
108 cr10 = (w3 >> 10u) & 0x3FFu;
109 } else { // 5
110 uint w2 = readWord(wordBaseX + 2, outY);
111 uint w3 = readWord(wordBaseX + 3, outY);
112 cb10 = (w2 >> 20u) & 0x3FFu; // shared with pixel 4
113 y10 = (w3 >> 20u) & 0x3FFu;
114 cr10 = (w3 >> 10u) & 0x3FFu; // shared with pixel 4
115 }
116
117 // 10-bit limited-range YCbCr (Y in [64,940], Cb/Cr in [64,960]) divided
118 // by 1023 yields the same [0,1]-normalized signal the 8-bit limited
119 // range matrices in ColorSpace.hpp expect, modulo a tiny scale factor
120 // that's well below visible quantization. The matrix handles the
121 // [16/255, 235/255] -> [0, 1] expansion internally.
122 vec4 yuv = vec4(float(y10), float(cb10), float(cr10), 1023.0) / 1023.0;
123 fragColor = processTexture(yuv);
124}
125)_";
126
128 : decoder{d}
129 {
130 }
131
132 Video::ImageFormat& decoder;
133
134 std::pair<QShader, QShader> init(RenderList& r) override
135 {
136 auto& rhi = *r.state.rhi;
137 const auto w = decoder.width, h = decoder.height;
138
139 // v210 input texture: one RGBA8 texel per ULWord on the PADDED wire row
140 // (128-byte multiple). (w/6)*4 would truncate the tail group at widths
141 // not divisible by 6 and, worse, skew every row against the wire stride.
142 const int texW = ((w + 47) / 48) * 32;
143 {
144 auto tex
145 = rhi.newTexture(QRhiTexture::RGBA8, {texW, h}, 1, QRhiTexture::Flag{});
146 tex->create();
147
148 // Nearest sampling: the shader uses texelFetch but the binding
149 // model still requires a sampler. Linear would smear bytes
150 // across v210 word boundaries — meaningless.
151 auto sampler = rhi.newSampler(
152 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
153 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
154 sampler->create();
155
156 samplers.push_back({sampler, tex});
157 }
158
160 r.state, vertexShader(), QString(frag).arg("").arg(colorMatrix(decoder)));
161 }
162
163 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
164 {
165 // CPU-staging path: AVFrame.data[0] is raw v210 with row stride =
166 // frame.linesize[0] = ((width+47)/48)*128 — exactly the texture row.
167 const int texW = ((decoder.width + 47) / 48) * 32;
168 auto y_tex = samplers[0].texture;
169 QRhiTextureUploadEntry entry{
170 0, 0,
172 frame.data[0], texW, decoder.height, /*bpp=*/4, frame.linesize[0])};
173 QRhiTextureUploadDescription desc{entry};
174 res.uploadTexture(y_tex, desc);
175 }
176};
177
178} // namespace score::gfx
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:90
static QRhiTextureSubresourceUploadDescription createTextureUpload(uint8_t *pixels, int w, int h, int bytesPerPixel, int stride)
Utility method to create a QRhiTextureSubresourceUploadDescription.
Definition GPUVideoDecoder.cpp:22
List of nodes to be rendered to an output.
Definition RenderList.hpp:30
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:169
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
Definition VideoInterface.hpp:26
Decodes 10-bit 4:2:2 YCbCr packed in v210 (SMPTE ST 2110-20 / SDI native).
Definition decoders/V210.hpp:38
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition decoders/V210.hpp:163
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition decoders/V210.hpp:134