Loading...
Searching...
No Matches
RGBA.hpp
1#pragma once
2#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
3#include <Gfx/Graph/decoders/ColorSpace.hpp>
4extern "C" {
5#include <libavformat/avformat.h>
6}
7
8namespace score::gfx
9{
11{
12 static const constexpr auto rgb_filter = R"_(#version 450
13
14)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
15
16 layout(binding=3) uniform sampler2D y_tex;
17
18 layout(location = 0) in vec2 v_texcoord;
19 layout(location = 0) out vec4 fragColor;
20
21 vec4 processTexture(vec4 tex) {
22 vec4 processed = tex;
23 { %1 }
24 return processed;
25 }
26
27 void main ()
28 {
29 fragColor = processTexture(texture(y_tex, v_texcoord));
30 })_";
31
33 QRhiTexture::Format fmt, int bytes_per_pixel, Video::ImageFormat& d,
34 QString f = "", bool invertY = false, bool nearest = false)
35 : format{fmt}
36 , bytes_per_pixel{bytes_per_pixel}
37 , decoder{d}
38 , filter{std::move(f)}
39 , invertY{invertY}
40 , nearestSampling{nearest}
41 {
42 }
43 QRhiTexture::Format format;
44 int bytes_per_pixel{}; // bpp/8 !
45 bool invertY{}; // Mainly useful for Spout texture import
49 Video::ImageFormat& decoder;
50 QString filter;
51
52 std::pair<QShader, QShader> init(RenderList& r) override
53 {
54 auto& rhi = *r.state.rhi;
55 const auto w = decoder.width, h = decoder.height;
56
57 {
58 // Create a texture
59 auto tex = rhi.newTexture(format, QSize{w, h}, 1, QRhiTexture::Flag{});
60 tex->create();
61
62 // Create a sampler
63 auto sampler = rhi.newSampler(
64 nearestSampling ? QRhiSampler::Nearest : QRhiSampler::Linear,
65 nearestSampling ? QRhiSampler::Nearest : QRhiSampler::Linear,
66 QRhiSampler::None, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
67 sampler->create();
68
69 // Store both
70 samplers.push_back({sampler, tex});
71 }
72
74 r.state, vertexShader(invertY), QString(rgb_filter).arg(filter));
75 }
76
77 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
78 {
79 // Nothing particular, we just upload the whole buffer
80 setPixels(res, frame.data[0], frame.linesize[0]);
81 }
82
83 void
84 setPixels(QRhiResourceUpdateBatch& res, uint8_t* pixels, int stride) const noexcept
85 {
86 const auto w = decoder.width, h = decoder.height;
87 auto y_tex = samplers[0].texture;
88
89 QRhiTextureUploadEntry entry{
90 0, 0, createTextureUpload(pixels, w, h, bytes_per_pixel, stride)};
91
92 QRhiTextureUploadDescription desc{entry};
93 res.uploadTexture(y_tex, desc);
94 }
95};
96
98{
99 static const constexpr auto rgb_filter = R"_(#version 450
100
101)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
102
103 %1
104 layout(location = 0) in vec2 v_texcoord;
105 layout(location = 0) out vec4 fragColor;
106
107 vec4 processTexture(vec4 tex) {
108 vec4 processed = tex;
109 { %2 }
110 return processed;
111 }
112
113 void main ()
114 {
115 vec4 tex = vec4(1.);
116 %3
117 fragColor = processTexture(tex);
118 })_";
119
121 QRhiTexture::Format fmt, int bytes_per_pixel, QString planes,
122 Video::ImageFormat& d, QString f = "")
123 : format{fmt}
124 , bytes_per_pixel{bytes_per_pixel}
125 , planes{planes}
126 , decoder{d}
127 , filter{std::move(f)}
128 {
129 }
130 QRhiTexture::Format format;
131 int bytes_per_pixel{}; // bpp/8 !
132 QString planes{};
133 Video::ImageFormat& decoder;
134 QString filter;
135
136 std::pair<QShader, QShader> init(RenderList& r) override
137 {
138 auto& rhi = *r.state.rhi;
139 const auto w = decoder.width, h = decoder.height;
140
141 QString samplers_code;
142 QString read_texture_code;
143
144 const int binding_orig = 3;
145 for(int i = 0; i < planes.size(); i++)
146 {
147 samplers_code += QString(" layout(binding=%1) uniform sampler2D t%2;\n")
148 .arg(binding_orig + i)
149 .arg(i);
150 read_texture_code
151 += QString(" tex.%1 = texture(t%2, v_texcoord).r;\n")
152 .arg(planes[i])
153 .arg(i);
154
155 // Create a texture
156 auto tex = rhi.newTexture(format, QSize{w, h}, 1, QRhiTexture::Flag{});
157 tex->create();
158
159 // Create a sampler
160 auto sampler = rhi.newSampler(
161 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
162 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
163 sampler->create();
164
165 // Store both
166 samplers.push_back({sampler, tex});
167 }
168
170 r.state, vertexShader(),
171 QString(rgb_filter).arg(samplers_code).arg(filter).arg(read_texture_code));
172 }
173
174 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
175 {
176 // Nothing particular, we just upload the whole buffer
177 for(int i = 0; i < planes.size(); i++)
178 {
179 setPixels(res, samplers[i].texture, frame.data[i], frame.linesize[i]);
180 }
181 }
182
183 void setPixels(
184 QRhiResourceUpdateBatch& res, QRhiTexture* tex, uint8_t* pixels,
185 int stride) const noexcept
186 {
187 const auto w = decoder.width, h = decoder.height;
188
189 QRhiTextureUploadEntry entry{
190 0, 0, createTextureUpload(pixels, w, h, bytes_per_pixel, stride)};
191
192 QRhiTextureUploadDescription desc{entry};
193 res.uploadTexture(tex, desc);
194 }
195};
196
198{
199 static const constexpr auto rgb_filter = R"_(#version 450
200
201)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
202
203 layout(binding=3) uniform sampler2DRect y_tex;
204
205 layout(location = 0) in vec2 v_texcoord;
206 layout(location = 0) out vec4 fragColor;
207
208 vec4 processTexture(vec4 tex) {
209 vec4 processed = tex;
210 { %1 }
211 return processed;
212 }
213
214 void main ()
215 {
216 fragColor = processTexture(texture(y_tex, v_texcoord));
217 })_";
218
219 static constexpr const char* vertex = R"_(#version 450
220layout(location = 0) in vec2 position;
221layout(location = 1) in vec2 texcoord;
222
223layout(location = 0) out vec2 v_texcoord;
224
225)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
226
227out gl_PerVertex { vec4 gl_Position; };
228
229void main()
230{
231 v_texcoord = texcoord * mat.texSz.xy;
232 gl_Position = renderer.clipSpaceCorrMatrix * vec4(position.x * mat.scale.x, position.y * mat.scale.y, 0.0, 1.);
233#if defined(QSHADER_HLSL) || defined(QSHADER_MSL)
234 gl_Position.y = - gl_Position.y;
235#endif
236}
237)_";
238
240 QRhiTexture::Format fmt, int bytes_per_pixel, Video::ImageFormat& d,
241 QString f = "")
242 : format{fmt}
243 , bytes_per_pixel{bytes_per_pixel}
244 , decoder{d}
245 , filter{std::move(f)}
246 {
247 }
248 QRhiTexture::Format format;
249 int bytes_per_pixel{}; // bpp/8 !
250 Video::ImageFormat& decoder;
251 QString filter;
252
253 std::pair<QShader, QShader> init(RenderList& r) override
254 {
255 auto& rhi = *r.state.rhi;
256 const auto w = decoder.width, h = decoder.height;
257
258 {
259 // Create a texture
260 auto tex = rhi.newTexture(format, QSize{w, h}, 1, QRhiTexture::Flag{});
261 tex->create();
262
263 // Create a sampler
264 auto sampler = rhi.newSampler(
265 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
266 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
267 sampler->create();
268
269 // Store both
270 samplers.push_back({sampler, tex});
271 }
272
273 return score::gfx::makeShaders(r.state, vertex, QString(rgb_filter).arg(filter));
274 }
275
276 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
277 {
278 // Nothing particular, we just upload the whole buffer
279 setPixels(res, frame.data[0], frame.linesize[0]);
280 }
281
282 void
283 setPixels(QRhiResourceUpdateBatch& res, uint8_t* pixels, int stride) const noexcept
284 {
285 const auto w = decoder.width, h = decoder.height;
286 auto y_tex = samplers[0].texture;
287
288 QRhiTextureUploadEntry entry{
289 0, 0, createTextureUpload(pixels, w, h, bytes_per_pixel, stride)};
290
291 QRhiTextureUploadDescription desc{entry};
292 res.uploadTexture(y_tex, desc);
293 }
294};
295
297{
298 static const constexpr auto rgb_filter = R"_(#version 450
299
300)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
301
302 layout(binding=3) uniform sampler2D y_tex;
303
304 layout(location = 0) in vec2 v_texcoord;
305 layout(location = 0) out vec4 fragColor;
306
307 vec4 processTexture(vec4 tex) {
308 vec4 processed = tex;
309 { %1 }
310 return processed;
311 }
312
313 void main ()
314 {
315 float w = mat.texSz.x;
316 float h = mat.texSz.y;
317 int x = int(floor(v_texcoord.x * w) * 3.);
318 int y = int(v_texcoord.y * h);
319 float r = texelFetch(y_tex, ivec2(x + 0, y), 0).r;
320 float g = texelFetch(y_tex, ivec2(x + 1, y), 0).r;
321 float b = texelFetch(y_tex, ivec2(x + 2, y), 0).r;
322 fragColor = processTexture(vec4(r, g, b, 1.));
323 })_";
324
325 RGB24Decoder(Video::ImageFormat& d, QString f = "")
326 : bytes_per_pixel{3}
327 , decoder{d}
328 , filter{std::move(f)}
329 {
330 }
331 int bytes_per_pixel{}; // bpp/8 !
332 Video::ImageFormat& decoder;
333 QString filter;
334
335 std::pair<QShader, QShader> init(RenderList& r) override
336 {
337 auto& rhi = *r.state.rhi;
338 const auto w = decoder.width, h = decoder.height;
339
340 {
341 // A data texture, not a colour one: the bytes are read individually
342 // with texelFetch, so no QRhiTexture::sRGB -- the flag would apply the
343 // sRGB->linear EOTF to every raw byte on fetch.
344 auto tex = rhi.newTexture(QRhiTexture::R8, QSize{w * 3, h}, 1);
345 tex->create();
346
347 // Create a sampler
348 auto sampler = rhi.newSampler(
349 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
350 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
351 sampler->create();
352
353 // Store both
354 samplers.push_back({sampler, tex});
355 }
356
358 r.state, vertexShader(), QString(rgb_filter).arg(filter));
359 }
360
361 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
362 {
363 // Nothing particular, we just upload the whole buffer
364 setPixels(res, frame.data[0], frame.linesize[0]);
365 }
366
367 void
368 setPixels(QRhiResourceUpdateBatch& res, uint8_t* pixels, int stride) const noexcept
369 {
370 const auto w = decoder.width, h = decoder.height;
371 auto y_tex = samplers[0].texture;
372
373 QRhiTextureUploadEntry entry{
374 0, 0, createTextureUpload(pixels, w, h, bytes_per_pixel, stride)};
375
376 QRhiTextureUploadDescription desc{entry};
377 res.uploadTexture(y_tex, desc);
378 }
379};
380
382{
383 static const constexpr auto rgb_filter = R"_(#version 450
384
385)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
386
387 layout(binding=3) uniform sampler2D y_tex;
388
389 layout(location = 0) in vec2 v_texcoord;
390 layout(location = 0) out vec4 fragColor;
391
392 vec4 processTexture(vec4 tex) {
393 vec4 processed = tex;
394 { %1 }
395 return processed;
396 }
397
398 void main ()
399 {
400 float w = mat.texSz.x;
401 float h = mat.texSz.y;
402 int x = int(floor(v_texcoord.x * w) * 3.);
403 int y = int(v_texcoord.y * h);
404 const float s = )_" SCORE_GFX_MSB_ALIGNED_SCALE R"_(;
405 float r = texelFetch(y_tex, ivec2(x + 0, y), 0).r * s;
406 float g = texelFetch(y_tex, ivec2(x + 1, y), 0).r * s;
407 float b = texelFetch(y_tex, ivec2(x + 2, y), 0).r * s;
408 fragColor = processTexture(vec4(r, g, b, 1.));
409 })_";
410
411 RGB48Decoder(Video::ImageFormat& d, QString f = "")
412 : decoder{d}
413 , filter{std::move(f)}
414 {
415 }
416 Video::ImageFormat& decoder;
417 QString filter;
418
419 std::pair<QShader, QShader> init(RenderList& r) override
420 {
421 auto& rhi = *r.state.rhi;
422 const auto w = decoder.width, h = decoder.height;
423
424 {
425 auto tex = rhi.newTexture(QRhiTexture::R16, QSize{w * 3, h}, 1, QRhiTexture::Flag{});
426 tex->create();
427
428 auto sampler = rhi.newSampler(
429 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
430 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
431 sampler->create();
432
433 samplers.push_back({sampler, tex});
434 }
435
437 r.state, vertexShader(), QString(rgb_filter).arg(filter));
438 }
439
440 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
441 {
442 const auto w = decoder.width, h = decoder.height;
443 auto y_tex = samplers[0].texture;
444
445 // 3 R16 samples per pixel = 6 bytes per pixel
446 QRhiTextureUploadEntry entry{
447 0, 0, createTextureUpload(frame.data[0], w * 3, h, 2, frame.linesize[0])};
448
449 QRhiTextureUploadDescription desc{entry};
450 res.uploadTexture(y_tex, desc);
451 }
452};
453
454// RGBA / BGRA 16-bit-per-channel (rgba64le / bgra64le). The samples are
455// 16-bit UNORM integers and QRhi has no four-channel 16-bit UNORM format, so
456// -- as for RGB48 -- they go into an R16 data texture of width w*4 and the
457// four components are reassembled with texelFetch.
459{
460 static const constexpr auto rgb_filter = R"_(#version 450
461
462)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
463
464 layout(binding=3) uniform sampler2D y_tex;
465
466 layout(location = 0) in vec2 v_texcoord;
467 layout(location = 0) out vec4 fragColor;
468
469 vec4 processTexture(vec4 tex) {
470 vec4 processed = tex;
471 { %1 }
472 return processed;
473 }
474
475 void main ()
476 {
477 float w = mat.texSz.x;
478 float h = mat.texSz.y;
479 int x = int(floor(v_texcoord.x * w) * 4.);
480 int y = int(v_texcoord.y * h);
481 const float s = )_" SCORE_GFX_MSB_ALIGNED_SCALE R"_(;
482 float r = texelFetch(y_tex, ivec2(x + 0, y), 0).r * s;
483 float g = texelFetch(y_tex, ivec2(x + 1, y), 0).r * s;
484 float b = texelFetch(y_tex, ivec2(x + 2, y), 0).r * s;
485 float a = texelFetch(y_tex, ivec2(x + 3, y), 0).r * s;
486 fragColor = processTexture(vec4(r, g, b, a));
487 })_";
488
489 RGBA64Decoder(Video::ImageFormat& d, QString f = "")
490 : decoder{d}
491 , filter{std::move(f)}
492 {
493 }
494 Video::ImageFormat& decoder;
495 QString filter;
496
497 std::pair<QShader, QShader> init(RenderList& r) override
498 {
499 auto& rhi = *r.state.rhi;
500 const auto w = decoder.width, h = decoder.height;
501
502 {
503 auto tex = rhi.newTexture(QRhiTexture::R16, QSize{w * 4, h}, 1, QRhiTexture::Flag{});
504 tex->create();
505
506 auto sampler = rhi.newSampler(
507 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
508 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
509 sampler->create();
510
511 samplers.push_back({sampler, tex});
512 }
513
515 r.state, vertexShader(), QString(rgb_filter).arg(filter));
516 }
517
518 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
519 {
520 const auto w = decoder.width, h = decoder.height;
521 auto y_tex = samplers[0].texture;
522
523 // 4 R16 samples per pixel = 8 bytes per pixel
524 QRhiTextureUploadEntry entry{
525 0, 0, createTextureUpload(frame.data[0], w * 4, h, 2, frame.linesize[0])};
526
527 QRhiTextureUploadDescription desc{entry};
528 res.uploadTexture(y_tex, desc);
529 }
530};
531
532}
533
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
Definition RGBA.hpp:11
bool nearestSampling
Definition RGBA.hpp:48
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:52
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:77
Definition RGBA.hpp:198
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:276
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:253
Definition RGBA.hpp:98
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:174
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:136
Definition RGBA.hpp:297
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:361
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:335
Definition RGBA.hpp:382
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:419
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:440
Definition RGBA.hpp:459
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition RGBA.hpp:518
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition RGBA.hpp:497