Loading...
Searching...
No Matches
GpuUtils.hpp
1#pragma once
2
3#if SCORE_PLUGIN_GFX
4#include <Process/ExecutionContext.hpp>
5
6#include <Crousti/File.hpp>
7#include <Crousti/MessageBus.hpp>
8#include <Gfx/GfxExecNode.hpp>
9#include <Gfx/Graph/Node.hpp>
10#include <Gfx/Graph/OutputNode.hpp>
11#include <Gfx/Graph/RenderState.hpp>
12
13#include <score/tools/ThreadPool.hpp>
14
15#include <ossia-qt/invoke.hpp>
16
17#include <QCoreApplication>
18#include <QTimer>
19#include <QtGui/private/qrhi_p.h>
20
21#include <avnd/binding/ossia/port_run_postprocess.hpp>
22#include <avnd/binding/ossia/port_run_preprocess.hpp>
23#include <avnd/binding/ossia/soundfiles.hpp>
24#include <avnd/concepts/parameter.hpp>
25#include <avnd/introspection/input.hpp>
26#include <avnd/introspection/output.hpp>
27#include <fmt/format.h>
28#include <gpp/layout.hpp>
29
30#include <score_plugin_avnd_export.h>
31
32namespace gpp::qrhi
33{
34
35template <typename F>
36 requires std::is_enum_v<F>
37constexpr QRhiTexture::Format textureFormat(F f) noexcept
38{
39 if constexpr(requires { F::RGBA; } || requires { F::RGBA8; })
40 if(f == F::RGBA8)
41 return QRhiTexture::RGBA8;
42 if constexpr(requires { F::BGRA; } || requires { F::BGRA8; })
43 if(f == F::BGRA8)
44 return QRhiTexture::BGRA8;
45 if constexpr(requires { F::R8; } || requires { F::GRAYSCALE; })
46 if(f == F::R8)
47 return QRhiTexture::R8;
48#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
49 if constexpr(requires { F::RG8; })
50 if(f == F::RG8)
51 return QRhiTexture::RG8;
52#endif
53 if constexpr(requires { F::R16; })
54 if(f == F::R16)
55 return QRhiTexture::R16;
56#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
57 if constexpr(requires { F::RG16; })
58 if(f == F::RG16)
59 return QRhiTexture::RG16;
60#endif
61 if constexpr(requires { F::RED_OR_ALPHA8; })
62 if(f == F::RED_OR_ALPHA8)
63 return QRhiTexture::RED_OR_ALPHA8;
64 if constexpr(requires { F::RGBA16F; })
65 if(f == F::RGBA16F)
66 return QRhiTexture::RGBA16F;
67 if constexpr(requires { F::RGBA32F; })
68 if(f == F::RGBA32F)
69 return QRhiTexture::RGBA32F;
70 if constexpr(requires { F::R16F; })
71 if(f == F::R16F)
72 return QRhiTexture::R16F;
73 if constexpr(requires { F::R32F; })
74 if(f == F::R32F)
75 return QRhiTexture::R32F;
76#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
77 if constexpr(requires { F::RGB10A2; })
78 if(f == F::RGB10A2)
79 return QRhiTexture::RGB10A2;
80#endif
81 if constexpr(requires { F::D16; })
82 if(f == F::D16)
83 return QRhiTexture::D16;
84
85#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
86 if constexpr(requires { F::D24; })
87 if(f == F::D24)
88 return QRhiTexture::D24;
89 if constexpr(requires { F::D24S8; })
90 if(f == F::D24S8)
91 return QRhiTexture::D24S8;
92#endif
93 if constexpr(requires { F::D32F; })
94 if(f == F::D32F)
95 return QRhiTexture::D32F;
96
97 if constexpr(requires { F::BC1; })
98 if(f == F::BC1)
99 return QRhiTexture::BC1;
100 if constexpr(requires { F::BC2; })
101 if(f == F::BC2)
102 return QRhiTexture::BC2;
103 if constexpr(requires { F::BC3; })
104 if(f == F::BC3)
105 return QRhiTexture::BC3;
106 if constexpr(requires { F::BC4; })
107 if(f == F::BC4)
108 return QRhiTexture::BC4;
109 if constexpr(requires { F::BC5; })
110 if(f == F::BC5)
111 return QRhiTexture::BC5;
112 if constexpr(requires { F::BC6H; })
113 if(f == F::BC6H)
114 return QRhiTexture::BC6H;
115 if constexpr(requires { F::BC7; })
116 if(f == F::BC7)
117 return QRhiTexture::BC7;
118 if constexpr(requires { F::ETC2_RGB8; })
119 if(f == F::ETC2_RGB8)
120 return QRhiTexture::ETC2_RGB8;
121 if constexpr(requires { F::ETC2_RGB8A1; })
122 if(f == F::ETC2_RGB8A1)
123 return QRhiTexture::ETC2_RGB8A1;
124 if constexpr(requires { F::ETC2_RGB8A8; })
125 if(f == F::ETC2_RGBA8)
126 return QRhiTexture::ETC2_RGBA8;
127 if constexpr(requires { F::ASTC_4X4; })
128 if(f == F::ASTC_4x4)
129 return QRhiTexture::ASTC_4x4;
130 if constexpr(requires { F::ASTC_5X4; })
131 if(f == F::ASTC_5x4)
132 return QRhiTexture::ASTC_5x4;
133 if constexpr(requires { F::ASTC_5X5; })
134 if(f == F::ASTC_5x5)
135 return QRhiTexture::ASTC_5x5;
136 if constexpr(requires { F::ASTC_6X5; })
137 if(f == F::ASTC_6x5)
138 return QRhiTexture::ASTC_6x5;
139 if constexpr(requires { F::ASTC_6X6; })
140 if(f == F::ASTC_6x6)
141 return QRhiTexture::ASTC_6x6;
142 if constexpr(requires { F::ASTC_8X5; })
143 if(f == F::ASTC_8x5)
144 return QRhiTexture::ASTC_8x5;
145 if constexpr(requires { F::ASTC_8X6; })
146 if(f == F::ASTC_8x6)
147 return QRhiTexture::ASTC_8x6;
148 if constexpr(requires { F::ASTC_8X8; })
149 if(f == F::ASTC_8x8)
150 return QRhiTexture::ASTC_8x8;
151 if constexpr(requires { F::ASTC_10X5; })
152 if(f == F::ASTC_10x5)
153 return QRhiTexture::ASTC_10x5;
154 if constexpr(requires { F::ASTC_10X6; })
155 if(f == F::ASTC_10x6)
156 return QRhiTexture::ASTC_10x6;
157 if constexpr(requires { F::ASTC_10X8; })
158 if(f == F::ASTC_10x8)
159 return QRhiTexture::ASTC_10x8;
160 if constexpr(requires { F::ASTC_10X10; })
161 if(f == F::ASTC_10x10)
162 return QRhiTexture::ASTC_10x10;
163 if constexpr(requires { F::ASTC_12X10; })
164 if(f == F::ASTC_12x10)
165 return QRhiTexture::ASTC_12x10;
166 if constexpr(requires { F::ASTC_12X12; })
167 if(f == F::ASTC_12x12)
168 return QRhiTexture::ASTC_12x12;
169 if constexpr(requires { F::RGB; })
170 if(f == F::RGB)
171 return QRhiTexture::RGBA8; // we'll have a CPU step to go to rgb
172
173 return QRhiTexture::RGBA8;
174}
175
176template <typename F>
177constexpr QRhiTexture::Format textureFormat() noexcept
178{
179 if constexpr(requires { std::string_view{F::format()}; })
180 {
181 constexpr std::string_view fmt = F::format();
182
183 if(fmt == "rgba" || fmt == "rgba8")
184 return QRhiTexture::RGBA8;
185 else if(fmt == "bgra" || fmt == "bgra8")
186 return QRhiTexture::BGRA8;
187 else if(fmt == "r8")
188 return QRhiTexture::R8;
189#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
190 else if(fmt == "rg8")
191 return QRhiTexture::RG8;
192#endif
193 else if(fmt == "r16")
194 return QRhiTexture::R16;
195#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
196 else if(fmt == "rg16")
197 return QRhiTexture::RG16;
198#endif
199 else if(fmt == "red_or_alpha8")
200 return QRhiTexture::RED_OR_ALPHA8;
201 else if(fmt == "rgba16f")
202 return QRhiTexture::RGBA16F;
203 else if(fmt == "rgba32f")
204 return QRhiTexture::RGBA32F;
205 else if(fmt == "r16f")
206 return QRhiTexture::R16F;
207 else if(fmt == "r32")
208 return QRhiTexture::R32F;
209#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
210 else if(fmt == "rgb10a2")
211 return QRhiTexture::RGB10A2;
212#endif
213
214 else if(fmt == "d16")
215 return QRhiTexture::D16;
216
217#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
218 else if(fmt == "d24")
219 return QRhiTexture::D24;
220 else if(fmt == "d24s8")
221 return QRhiTexture::D24S8;
222#endif
223 else if(fmt == "d32f")
224 return QRhiTexture::D32F;
225
226 else if(fmt == "bc1")
227 return QRhiTexture::BC1;
228 else if(fmt == "bc2")
229 return QRhiTexture::BC2;
230 else if(fmt == "bc3")
231 return QRhiTexture::BC3;
232 else if(fmt == "bc4")
233 return QRhiTexture::BC4;
234 else if(fmt == "bc5")
235 return QRhiTexture::BC5;
236 else if(fmt == "bc6h")
237 return QRhiTexture::BC6H;
238 else if(fmt == "bc7")
239 return QRhiTexture::BC7;
240 else if(fmt == "etc2_rgb8")
241 return QRhiTexture::ETC2_RGB8;
242 else if(fmt == "etc2_rgb8a1")
243 return QRhiTexture::ETC2_RGB8A1;
244 else if(fmt == "etc2_rgb8a8")
245 return QRhiTexture::ETC2_RGBA8;
246 else if(fmt == "astc_4x4")
247 return QRhiTexture::ASTC_4x4;
248 else if(fmt == "astc_5x4")
249 return QRhiTexture::ASTC_5x4;
250 else if(fmt == "astc_5x5")
251 return QRhiTexture::ASTC_5x5;
252 else if(fmt == "astc_6x5")
253 return QRhiTexture::ASTC_6x5;
254 else if(fmt == "astc_6x6")
255 return QRhiTexture::ASTC_6x6;
256 else if(fmt == "astc_8x5")
257 return QRhiTexture::ASTC_8x5;
258 else if(fmt == "astc_8x6")
259 return QRhiTexture::ASTC_8x6;
260 else if(fmt == "astc_8x8")
261 return QRhiTexture::ASTC_8x8;
262 else if(fmt == "astc_10x5")
263 return QRhiTexture::ASTC_10x5;
264 else if(fmt == "astc_10x6")
265 return QRhiTexture::ASTC_10x6;
266 else if(fmt == "astc_10x8")
267 return QRhiTexture::ASTC_10x8;
268 else if(fmt == "astc_10x10")
269 return QRhiTexture::ASTC_10x10;
270 else if(fmt == "astc_12x10")
271 return QRhiTexture::ASTC_12x10;
272 else if(fmt == "astc_12x12")
273 return QRhiTexture::ASTC_12x12;
274 else if(fmt == "rgb")
275 return QRhiTexture::RGBA8;
276 else
277 return QRhiTexture::RGBA8;
278 }
279 else if constexpr(std::is_enum_v<typename F::format>)
280 {
281 if constexpr(requires { F::RGBA; } || requires { F::RGBA8; })
282 return QRhiTexture::RGBA8;
283 else if constexpr(requires { F::BGRA; } || requires { F::BGRA8; })
284 return QRhiTexture::BGRA8;
285 else if constexpr(requires { F::R8; } || requires { F::GRAYSCALE; })
286 return QRhiTexture::R8;
287#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
288 else if constexpr(requires { F::RG8; })
289 return QRhiTexture::RG8;
290#endif
291 else if constexpr(requires { F::R16; })
292 return QRhiTexture::R16;
293#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
294 else if constexpr(requires { F::RG16; })
295 return QRhiTexture::RG16;
296#endif
297 else if constexpr(requires { F::RED_OR_ALPHA8; })
298 return QRhiTexture::RED_OR_ALPHA8;
299 else if constexpr(requires { F::RGBA16F; })
300 return QRhiTexture::RGBA16F;
301 else if constexpr(requires { F::RGBA32F; })
302 return QRhiTexture::RGBA32F;
303 else if constexpr(requires { F::R16F; })
304 return QRhiTexture::R16F;
305 else if constexpr(requires { F::R32F; })
306 return QRhiTexture::R32F;
307#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
308 else if constexpr(requires { F::RGB10A2; })
309 return QRhiTexture::RGB10A2;
310#endif
311 else if constexpr(requires { F::D16; })
312 return QRhiTexture::D16;
313
314#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
315 else if constexpr(requires { F::D24; })
316 return QRhiTexture::D24;
317 else if constexpr(requires { F::D24S8; })
318 return QRhiTexture::D24S8;
319#endif
320 else if constexpr(requires { F::D32F; })
321 return QRhiTexture::D32F;
322
323 else if constexpr(requires { F::BC1; })
324 return QRhiTexture::BC1;
325 else if constexpr(requires { F::BC2; })
326 return QRhiTexture::BC2;
327 else if constexpr(requires { F::BC3; })
328 return QRhiTexture::BC3;
329 else if constexpr(requires { F::BC4; })
330 return QRhiTexture::BC4;
331 else if constexpr(requires { F::BC5; })
332 return QRhiTexture::BC5;
333 else if constexpr(requires { F::BC6H; })
334 return QRhiTexture::BC6H;
335 else if constexpr(requires { F::BC7; })
336 return QRhiTexture::BC7;
337 else if constexpr(requires { F::ETC2_RGB8; })
338 return QRhiTexture::ETC2_RGB8;
339 else if constexpr(requires { F::ETC2_RGB8A1; })
340 return QRhiTexture::ETC2_RGB8A1;
341 else if constexpr(requires { F::ETC2_RGB8A8; })
342 return QRhiTexture::ETC2_RGBA8;
343 else if constexpr(requires { F::ASTC_4X4; })
344 return QRhiTexture::ASTC_4x4;
345 else if constexpr(requires { F::ASTC_5X4; })
346 return QRhiTexture::ASTC_5x4;
347 else if constexpr(requires { F::ASTC_5X5; })
348 return QRhiTexture::ASTC_5x5;
349 else if constexpr(requires { F::ASTC_6X5; })
350 return QRhiTexture::ASTC_6x5;
351 else if constexpr(requires { F::ASTC_6X6; })
352 return QRhiTexture::ASTC_6x6;
353 else if constexpr(requires { F::ASTC_8X5; })
354 return QRhiTexture::ASTC_8x5;
355 else if constexpr(requires { F::ASTC_8X6; })
356 return QRhiTexture::ASTC_8x6;
357 else if constexpr(requires { F::ASTC_8X8; })
358 return QRhiTexture::ASTC_8x8;
359 else if constexpr(requires { F::ASTC_10X5; })
360 return QRhiTexture::ASTC_10x5;
361 else if constexpr(requires { F::ASTC_10X6; })
362 return QRhiTexture::ASTC_10x6;
363 else if constexpr(requires { F::ASTC_10X8; })
364 return QRhiTexture::ASTC_10x8;
365 else if constexpr(requires { F::ASTC_10X10; })
366 return QRhiTexture::ASTC_10x10;
367 else if constexpr(requires { F::ASTC_12X10; })
368 return QRhiTexture::ASTC_12x10;
369 else if constexpr(requires { F::ASTC_12X12; })
370 return QRhiTexture::ASTC_12x12;
371 else if constexpr(requires { F::RGB; })
372 return QRhiTexture::RGBA8;
373 else
374 return QRhiTexture::RGBA8;
375 }
376}
377
378template <avnd::cpu_texture Tex>
379constexpr QRhiTexture::Format textureFormat(const Tex& t) noexcept
380{
381 QRhiTexture::Format fmt{};
382 if constexpr(avnd::cpu_dynamic_format_texture<Tex>)
383 fmt = gpp::qrhi::textureFormat(t.format);
384 else
385 fmt = gpp::qrhi::textureFormat<Tex>();
386 return fmt;
387}
388
389struct DefaultPipeline
390{
391 struct layout
392 {
393 enum
394 {
395 graphics
396 };
397 struct vertex_input
398 {
399 struct
400 {
401 static constexpr auto name() { return "position"; }
402 static constexpr int location() { return 0; }
403 float data[2];
404 } pos;
405 };
406 struct vertex_output
407 {
408 };
409 struct fragment_input
410 {
411 };
412 struct bindings
413 {
414 };
415 };
416
417 static QString vertex()
418 {
419 return R"_(#version 450
420layout(location = 0) in vec2 position;
421out gl_PerVertex { vec4 gl_Position; };
422void main() {
423 gl_Position = vec4( position, 0.0, 1.0 );
424}
425)_";
426 }
427};
428
429template <typename C>
430constexpr auto usage()
431{
432 if constexpr(requires { C::vertex; })
433 return QRhiBuffer::VertexBuffer;
434 else if constexpr(requires { C::index; })
435 return QRhiBuffer::IndexBuffer;
436 else if constexpr(requires { C::ubo; })
437 return QRhiBuffer::UniformBuffer;
438 else if constexpr(requires { C::storage; })
439 return QRhiBuffer::StorageBuffer;
440 else
441 {
442 static_assert(C::unhandled);
443 throw;
444 }
445}
446
447template <typename C>
448constexpr auto buffer_type()
449{
450 if constexpr(requires { C::immutable; })
451 return QRhiBuffer::Immutable;
452 else if constexpr(requires { C::static_; })
453 return QRhiBuffer::Static;
454 else if constexpr(requires { C::dynamic; })
455 return QRhiBuffer::Dynamic;
456 else
457 {
458 static_assert(C::unhandled);
459 throw;
460 }
461}
462
463template <typename C>
464auto samples(C c)
465{
466 if constexpr(requires { C::samples; })
467 return c.samples;
468 else
469 return -1;
470}
471
472struct handle_release
473{
474 QRhi& rhi;
475 template <typename C>
476 void operator()(C command)
477 {
478 if constexpr(requires { C::deallocation; })
479 {
480 if constexpr(
481 requires { C::vertex; } || requires { C::index; } || requires { C::ubo; })
482 {
483 auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
484 buf->deleteLater();
485 }
486 else if constexpr(requires { C::sampler; })
487 {
488 auto buf = reinterpret_cast<QRhiSampler*>(command.handle);
489 buf->deleteLater();
490 }
491 else if constexpr(requires { C::texture; })
492 {
493 auto buf = reinterpret_cast<QRhiTexture*>(command.handle);
494 buf->deleteLater();
495 }
496 else
497 {
498 static_assert(C::unhandled);
499 }
500 }
501 else
502 {
503 static_assert(C::unhandled);
504 }
505 }
506};
507
508template <typename Self, typename Res>
509struct handle_dispatch
510{
511 Self& self;
512 QRhi& rhi;
513 QRhiCommandBuffer& cb;
514 QRhiResourceUpdateBatch*& res;
515 QRhiComputePipeline& pip;
516 template <typename C>
517 Res operator()(C command)
518 {
519 if constexpr(requires { C::compute; })
520 {
521 if constexpr(requires { C::dispatch; })
522 {
523 cb.dispatch(command.x, command.y, command.z);
524 return {};
525 }
526 else if constexpr(requires { C::begin; })
527 {
528 cb.beginComputePass(res);
529 res = nullptr;
530 cb.setComputePipeline(&pip);
531 cb.setShaderResources(pip.shaderResourceBindings());
532
533 return {};
534 }
535 else if constexpr(requires { C::end; })
536 {
537 cb.endComputePass(res);
538 res = nullptr;
539 rhi.finish();
540 return {};
541 }
542 else
543 {
544 static_assert(C::unhandled);
545 return {};
546 }
547 }
548 else if constexpr(requires { C::readback; })
549 {
550 // First handle the readback request
551 if constexpr(requires { C::request; })
552 {
553 if constexpr(requires { C::buffer; })
554 {
555 using ret = typename C::return_type;
556
557#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
558 auto readback = new QRhiBufferReadbackResult;
559#else
560 auto readback = new QRhiReadbackResult;
561#endif
562 self.addReadback(readback);
563
564 // this is e.g. a buffer_awaiter
565 ret user_rb{.handle = reinterpret_cast<decltype(ret::handle)>(readback)};
566
567 // TODO: do it with coroutines like this for peak asyncess
568 // ret must be a coroutine type.
569 // When the GPU completes the work, "completed" is called:
570 // this will cause the coroutine to be filled with the data
571 // readback->completed = [=] {
572 // qDebug() << "alhamdullilah he will be baked";
573 // // store "data" in the coroutine
574 // };
575
576 auto next = rhi.nextResourceUpdateBatch();
577 auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
578
579 next->readBackBuffer(buf, command.offset, command.size, readback);
580 res = next;
581
582 return user_rb;
583 }
584 else if constexpr(requires { C::texture; })
585 {
586 using ret = typename C::return_type;
587 QRhiReadbackResult readback;
588 return ret{};
589 }
590 else
591 {
592 static_assert(C::unhandled);
593 return {};
594 }
595 }
596 else if constexpr(requires { C::await; })
597 {
598 if constexpr(requires { C::buffer; })
599 {
600 using ret = typename C::return_type;
601
602#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
603 auto readback = reinterpret_cast<QRhiBufferReadbackResult*>(command.handle);
604#else
605 auto readback = reinterpret_cast<QRhiReadbackResult*>(command.handle);
606#endif
607
608 return ret{
609 .data = readback->data.data(), .size = (std::size_t)readback->data.size()};
610 }
611 else if constexpr(requires { C::texture; })
612 {
613 using ret = typename C::return_type;
614
615 auto readback = reinterpret_cast<QRhiReadbackResult*>(command.handle);
616
617 return ret{
618 .data = readback->data.data(), .size = (std::size_t)readback->data.size()};
619 }
620 }
621 }
622 else
623 {
624 static_assert(C::unhandled);
625 return {};
626 }
627
628 return {};
629 }
630};
631
632template <typename Self, typename Ret>
633struct handle_update
634{
635 Self& self;
636 QRhi& rhi;
637 QRhiResourceUpdateBatch& res;
638 std::vector<QRhiShaderResourceBinding>& srb;
639 bool& srb_touched;
640
641 template <typename C>
642 Ret operator()(C command)
643 {
644 if constexpr(requires { C::allocation; })
645 {
646 if constexpr(
647 requires { C::vertex; } || requires { C::index; })
648 {
649 auto buf = rhi.newBuffer(buffer_type<C>(), usage<C>(), command.size);
650 buf->create();
651 return reinterpret_cast<typename C::return_type>(buf);
652 }
653 else if constexpr(requires { C::sampler; })
654 {
655 auto buf = rhi.newSampler({}, {}, {}, {}, {});
656 buf->create();
657 return reinterpret_cast<typename C::return_type>(buf);
658 }
659 else if constexpr(
660 requires { C::ubo; } || requires { C::storage; })
661 {
662 auto buf = rhi.newBuffer(buffer_type<C>(), usage<C>(), command.size);
663 buf->create();
664
665 // Replace it in our bindings
666 score::gfx::replaceBuffer(srb, command.binding, buf);
667 srb_touched = true;
668 return reinterpret_cast<typename C::return_type>(buf);
669 }
670 else if constexpr(requires { C::texture; })
671 {
672 auto tex = rhi.newTexture(
673 QRhiTexture::RGBA8, QSize{command.width, command.height}, samples(command));
674 tex->create();
675
676 score::gfx::replaceTexture(srb, command.binding, tex);
677 srb_touched = true;
678 return reinterpret_cast<typename C::return_type>(tex);
679 }
680 else
681 {
682 static_assert(C::unhandled);
683 return {};
684 }
685 }
686 else if constexpr(requires { C::upload; })
687 {
688 if constexpr(requires { C::texture; })
689 {
690 QRhiTextureSubresourceUploadDescription sub(command.data, command.size);
691 res.uploadTexture(
692 reinterpret_cast<QRhiTexture*>(command.handle),
693 QRhiTextureUploadDescription{{0, 0, sub}});
694 }
695 else
696 {
697 auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
698 if constexpr(requires { C::dynamic; })
699 res.updateDynamicBuffer(buf, command.offset, command.size, command.data);
700 else if constexpr(
701 requires { C::static_; } || requires { C::immutable; })
702 res.uploadStaticBuffer(buf, command.offset, command.size, command.data);
703 else
704 {
705 static_assert(C::unhandled);
706 return {};
707 }
708 }
709 }
710 else if constexpr(requires { C::getter; })
711 {
712 if constexpr(requires { C::ubo; })
713 {
714 auto buf = self.createdUbos.at(command.binding);
715 return reinterpret_cast<typename C::return_type>(buf);
716 }
717 else
718 {
719 static_assert(C::unhandled);
720 return {};
721 }
722 }
723 else
724 {
725 handle_release{rhi}(command);
726 return {};
727 }
728 return {};
729 }
730};
731
732struct generate_shaders
733{
734 template <typename T, int N>
735 using vec = T[N];
736
737 static constexpr std::string_view field_type(float) { return "float"; }
738 static constexpr std::string_view field_type(const float (&)[2]) { return "vec2"; }
739 static constexpr std::string_view field_type(const float (&)[3]) { return "vec3"; }
740 static constexpr std::string_view field_type(const float (&)[4]) { return "vec4"; }
741
742 static constexpr std::string_view field_type(float*) { return "float"; }
743 static constexpr std::string_view field_type(vec<float, 2>*) { return "vec2"; }
744 static constexpr std::string_view field_type(vec<float, 3>*) { return "vec3"; }
745 static constexpr std::string_view field_type(vec<float, 4>*) { return "vec4"; }
746
747 static constexpr std::string_view field_type(int) { return "int"; }
748 static constexpr std::string_view field_type(const int (&)[2]) { return "ivec2"; }
749 static constexpr std::string_view field_type(const int (&)[3]) { return "ivec3"; }
750 static constexpr std::string_view field_type(const int (&)[4]) { return "ivec4"; }
751
752 static constexpr std::string_view field_type(int*) { return "int"; }
753 static constexpr std::string_view field_type(vec<int, 2>*) { return "ivec2"; }
754 static constexpr std::string_view field_type(vec<int, 3>*) { return "ivec3"; }
755 static constexpr std::string_view field_type(vec<int, 4>*) { return "ivec4"; }
756
757 static constexpr std::string_view field_type(uint32_t) { return "uint"; }
758 static constexpr std::string_view field_type(const uint32_t (&)[2]) { return "uvec2"; }
759 static constexpr std::string_view field_type(const uint32_t (&)[3]) { return "uvec3"; }
760 static constexpr std::string_view field_type(const uint32_t (&)[4]) { return "uvec4"; }
761
762 static constexpr std::string_view field_type(uint32_t*) { return "uint"; }
763 static constexpr std::string_view field_type(vec<uint32_t, 2>*) { return "uvec2"; }
764 static constexpr std::string_view field_type(vec<uint32_t, 3>*) { return "uvec3"; }
765 static constexpr std::string_view field_type(vec<uint32_t, 4>*) { return "uvec4"; }
766
767 static constexpr std::string_view field_type(avnd::xy_value auto) { return "vec2"; }
768
769 static constexpr bool field_array(float) { return false; }
770 static constexpr bool field_array(const float (&)[2]) { return false; }
771 static constexpr bool field_array(const float (&)[3]) { return false; }
772 static constexpr bool field_array(const float (&)[4]) { return false; }
773
774 static constexpr bool field_array(float*) { return true; }
775 static constexpr bool field_array(vec<float, 2>*) { return true; }
776 static constexpr bool field_array(vec<float, 3>*) { return true; }
777 static constexpr bool field_array(vec<float, 4>*) { return true; }
778
779 static constexpr bool field_array(int) { return false; }
780 static constexpr bool field_array(const int (&)[2]) { return false; }
781 static constexpr bool field_array(const int (&)[3]) { return false; }
782 static constexpr bool field_array(const int (&)[4]) { return false; }
783
784 static constexpr bool field_array(int*) { return true; }
785 static constexpr bool field_array(vec<int, 2>*) { return true; }
786 static constexpr bool field_array(vec<int, 3>*) { return true; }
787 static constexpr bool field_array(vec<int, 4>*) { return true; }
788
789 static constexpr bool field_array(uint32_t) { return false; }
790 static constexpr bool field_array(const uint32_t (&)[2]) { return false; }
791 static constexpr bool field_array(const uint32_t (&)[3]) { return false; }
792 static constexpr bool field_array(const uint32_t (&)[4]) { return false; }
793
794 static constexpr bool field_array(uint32_t*) { return true; }
795 static constexpr bool field_array(vec<uint32_t, 2>*) { return true; }
796 static constexpr bool field_array(vec<uint32_t, 3>*) { return true; }
797 static constexpr bool field_array(vec<uint32_t, 4>*) { return true; }
798
799 static constexpr bool field_array(avnd::xy_value auto) { return false; }
800
801 template <typename T>
802 static constexpr std::string_view image_qualifier()
803 {
804 if constexpr(requires { T::readonly; })
805 return "readonly";
806 else if constexpr(requires { T::writeonly; })
807 return "writeonly";
808 else
809 static_assert(T::readonly || T::writeonly);
810 }
811
812 struct write_input
813 {
814 std::string& shader;
815
816 template <typename T>
817 void operator()(const T& field)
818 {
819 shader += fmt::format(
820 "layout(location = {}) in {} {};\n", T::location(), field_type(field.data),
821 T::name());
822 }
823 };
824
825 struct write_output
826 {
827 std::string& shader;
828
829 template <typename T>
830 void operator()(const T& field)
831 {
832 if constexpr(requires { field.location(); })
833 {
834 shader += fmt::format(
835 "layout(location = {}) out {} {};\n", T::location(), field_type(field.data),
836 T::name());
837 }
838 }
839 };
840
841 struct write_binding
842 {
843 std::string& shader;
844
845 template <typename T>
846 void operator()(const T& field)
847 {
848 shader += fmt::format(
849 " {} {}{};\n", field_type(field.value), T::name(),
850 field_array(field.value) ? "[]" : "");
851 }
852 };
853
854 struct write_bindings
855 {
856 std::string& shader;
857
858 template <typename C>
859 void operator()(const C& field)
860 {
861 if constexpr(requires { C::sampler2D; })
862 {
863 shader += fmt::format(
864 "layout(binding = {}) uniform sampler2D {};\n\n", C::binding(), C::name());
865 }
866 else if constexpr(requires { C::image2D; })
867 {
868 shader += fmt::format(
869 "layout(binding = {}, {}) {} uniform image2D {};\n\n", C::binding(),
870 C::format(), image_qualifier<C>(), C::name());
871 }
872 else if constexpr(requires { C::ubo; })
873 {
874 shader += fmt::format(
875 "layout({}, binding = {}) uniform {}\n{{\n",
876 "std140" // TODO
877 ,
878 C::binding(), C::name());
879
880 boost::pfr::for_each_field(field, write_binding{shader});
881
882 shader += fmt::format("}};\n\n");
883 }
884 else if constexpr(requires { C::buffer; })
885 {
886 shader += fmt::format(
887 "layout({}, binding = {}) buffer {}\n{{\n",
888 "std140" // TODO
889 ,
890 C::binding(), C::name());
891
892 boost::pfr::for_each_field(field, write_binding{shader});
893
894 shader += fmt::format("}};\n\n");
895 }
896 }
897 };
898
899 template <typename T>
900 std::string vertex_shader(const T& lay)
901 {
902 using namespace gpp::qrhi;
903 std::string shader = "#version 450\n\n";
904
905 if constexpr(requires { lay.vertex_input; })
906 boost::pfr::for_each_field(lay.vertex_input, write_input{shader});
907 else if constexpr(requires { typename T::vertex_input; })
908 boost::pfr::for_each_field(typename T::vertex_input{}, write_input{shader});
909 else
910 boost::pfr::for_each_field(
911 DefaultPipeline::layout::vertex_input{}, write_input{shader});
912
913 if constexpr(requires { lay.vertex_output; })
914 boost::pfr::for_each_field(lay.vertex_output, write_output{shader});
915 else if constexpr(requires { typename T::vertex_output; })
916 boost::pfr::for_each_field(typename T::vertex_output{}, write_output{shader});
917
918 shader += "\n";
919
920 if constexpr(requires { lay.bindings; })
921 boost::pfr::for_each_field(lay.bindings, write_bindings{shader});
922 else if constexpr(requires { typename T::bindings; })
923 boost::pfr::for_each_field(typename T::bindings{}, write_bindings{shader});
924
925 return shader;
926 }
927
928 template <typename T>
929 std::string fragment_shader(const T& lay)
930 {
931 std::string shader = "#version 450\n\n";
932
933 if constexpr(requires { lay.fragment_input; })
934 boost::pfr::for_each_field(lay.fragment_input, write_input{shader});
935 else if constexpr(requires { typename T::fragment_input; })
936 boost::pfr::for_each_field(typename T::fragment_input{}, write_input{shader});
937
938 if constexpr(requires { lay.fragment_output; })
939 boost::pfr::for_each_field(lay.fragment_output, write_output{shader});
940 else if constexpr(requires { typename T::fragment_output; })
941 boost::pfr::for_each_field(typename T::fragment_output{}, write_output{shader});
942
943 shader += "\n";
944
945 if constexpr(requires { lay.bindings; })
946 boost::pfr::for_each_field(lay.bindings, write_bindings{shader});
947 else if constexpr(requires { typename T::bindings; })
948 boost::pfr::for_each_field(typename T::bindings{}, write_bindings{shader});
949
950 return shader;
951 }
952
953 template <typename T>
954 std::string compute_shader(const T& lay)
955 {
956 std::string fstr = "#version 450\n\n";
957
958 fstr += "layout(";
959 if constexpr(requires { T::local_size_x(); })
960 {
961 fstr += fmt::format("local_size_x = {}, ", T::local_size_x());
962 }
963 if constexpr(requires { T::local_size_y(); })
964 {
965 fstr += fmt::format("local_size_y = {}, ", T::local_size_y());
966 }
967 if constexpr(requires { T::local_size_z(); })
968 {
969 fstr += fmt::format("local_size_z = {}, ", T::local_size_z());
970 }
971
972 // Remove the last ", "
973 fstr.resize(fstr.size() - 2);
974 fstr += ") in;\n\n";
975
976 boost::pfr::for_each_field(lay.bindings, write_bindings{fstr});
977
978 return fstr;
979 }
980};
981}
982
983namespace oscr
984{
985struct GpuWorker
986{
987 template <typename T>
988 void initWorker(this auto& self, std::shared_ptr<T>& state) noexcept
989 {
990 if constexpr(avnd::has_worker<T>)
991 {
992 auto ptr = QPointer{&self};
993 auto& tq = score::TaskPool::instance();
994 using worker_type = decltype(state->worker);
995
996 auto wk_state = std::weak_ptr{state};
997 state->worker.request = [ptr, &tq, wk_state]<typename... Args>(Args&&... f) {
998 using type_of_result = decltype(worker_type::work(std::forward<Args>(f)...));
999 tq.post([... ff = std::forward<Args>(f), wk_state, ptr]() mutable {
1000 if constexpr(std::is_void_v<type_of_result>)
1001 {
1002 worker_type::work(std::forward<decltype(ff)>(ff)...);
1003 }
1004 else
1005 {
1006 // If the worker returns a std::function, it
1007 // is to be invoked back in the processor DSP thread
1008 auto res = worker_type::work(std::forward<decltype(ff)>(ff)...);
1009 if(!res || !ptr)
1010 return;
1011
1012 ossia::qt::run_async(
1013 QCoreApplication::instance(),
1014 [res = std::move(res), wk_state, ptr]() mutable {
1015 if(ptr)
1016 if(auto state = wk_state.lock())
1017 res(*state);
1018 });
1019 }
1020 });
1021 };
1022 }
1023 }
1024};
1025
1026template <typename GpuNodeRenderer, typename Node>
1027struct GpuProcessIns
1028{
1029 GpuNodeRenderer& gpu;
1030 Node& state;
1031 const score::gfx::Message& prev_mess;
1032 const score::gfx::Message& mess;
1033 const score::DocumentContext& ctx;
1034
1035 bool can_process_message(std::size_t N)
1036 {
1037 if(mess.input.size() <= N)
1038 return false;
1039
1040 if(prev_mess.input.size() == mess.input.size())
1041 {
1042 auto& prev = prev_mess.input[N];
1043 auto& next = mess.input[N];
1044 if(prev.index() == 1 && next.index() == 1)
1045 {
1046 if(ossia::get<ossia::value>(prev) == ossia::get<ossia::value>(next))
1047 {
1048 return false;
1049 }
1050 }
1051 }
1052 return true;
1053 }
1054
1055 void operator()(avnd::parameter auto& t, auto field_index)
1056 {
1057 if(!can_process_message(field_index))
1058 return;
1059
1060 if(auto val = ossia::get_if<ossia::value>(&mess.input[field_index]))
1061 {
1062 oscr::from_ossia_value(t, *val, t.value);
1063 if_possible(t.update(state));
1064 }
1065 }
1066
1067#if OSCR_HAS_MMAP_FILE_STORAGE
1068 template <avnd::raw_file_port Field, std::size_t NField>
1069 void operator()(Field& t, avnd::field_index<NField> field_index)
1070 {
1071 // FIXME we should be loading a file there
1072 using node_type = std::remove_cvref_t<decltype(gpu.node())>;
1073 using file_ports = avnd::raw_file_input_introspection<Node>;
1074
1075 if(!can_process_message(field_index))
1076 return;
1077
1078 auto val = ossia::get_if<ossia::value>(&mess.input[field_index]);
1079 if(!val)
1080 return;
1081
1082 static constexpr bool has_text = requires { decltype(Field::file)::text; };
1083 static constexpr bool has_mmap = requires { decltype(Field::file)::mmap; };
1084
1085 // First we can load it directly since execution hasn't started yet
1086 if(auto hdl = loadRawfile(*val, ctx, has_text, has_mmap))
1087 {
1088 static constexpr auto N = file_ports::field_index_to_index(NField);
1089 if constexpr(avnd::port_can_process<Field>)
1090 {
1091 // FIXME also do it when we get a run-time message from the exec engine,
1092 // OSC, etc
1093 auto func = executePortPreprocess<Field>(*hdl);
1094 const_cast<node_type&>(gpu.node())
1095 .file_loaded(
1096 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
1097 if(func)
1098 func(state);
1099 }
1100 else
1101 {
1102 const_cast<node_type&>(gpu.node())
1103 .file_loaded(
1104 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
1105 }
1106 }
1107 }
1108#endif
1109
1110 template <avnd::texture_port Field, std::size_t NField>
1111 void operator()(Field& t, avnd::field_index<NField> field_index)
1112 {
1113 using node_type = std::remove_cvref_t<decltype(gpu.node())>;
1114 auto& node = const_cast<node_type&>(gpu.node());
1115 auto val = ossia::get_if<ossia::render_target_spec>(&mess.input[field_index]);
1116 if(!val)
1117 return;
1118 node.process(NField, *val);
1119 }
1120
1121 void operator()(auto& t, auto field_index) = delete;
1122};
1123
1124struct GpuControlIns
1125{
1126 template <typename Self, typename Node_T>
1127 static void processControlIn(
1128 Self& self, Node_T& state, score::gfx::Message& renderer_mess,
1129 const score::gfx::Message& mess, const score::DocumentContext& ctx) noexcept
1130 {
1131 // Apply the controls
1132 avnd::input_introspection<Node_T>::for_all_n(
1133 avnd::get_inputs<Node_T>(state),
1134 GpuProcessIns<Self, Node_T>{self, state, renderer_mess, mess, ctx});
1135 renderer_mess = mess;
1136 }
1137};
1138
1139struct GpuControlOuts
1140{
1141 std::weak_ptr<Execution::ExecutionCommandQueue> queue;
1142 Gfx::exec_controls control_outs;
1143
1144 int64_t instance{};
1145
1146 template <typename Node_T>
1147 void processControlOut(Node_T& state) const noexcept
1148 {
1149 if(!this->control_outs.empty())
1150 {
1151 auto q = this->queue.lock();
1152 if(!q)
1153 return;
1154 auto& qq = *q;
1155 int parm_k = 0;
1156 avnd::parameter_output_introspection<Node_T>::for_all(
1157 avnd::get_outputs(state), [&]<avnd::parameter T>(const T& t) {
1158 qq.enqueue([v = oscr::to_ossia_value(t, t.value),
1159 port = control_outs[parm_k]]() mutable {
1160 std::swap(port->value, v);
1161 port->changed = true;
1162 });
1163
1164 parm_k++;
1165 });
1166 }
1167 }
1168};
1169
1170template <typename T>
1171struct SCORE_PLUGIN_AVND_EXPORT GpuNodeElements
1172{
1173 [[no_unique_address]] oscr::soundfile_storage<T> soundfiles;
1174
1175 [[no_unique_address]] oscr::midifile_storage<T> midifiles;
1176
1177#if defined(OSCR_HAS_MMAP_FILE_STORAGE)
1178 [[no_unique_address]] oscr::raw_file_storage<T> rawfiles;
1179#endif
1180
1181 template <std::size_t N, std::size_t NField>
1182 void file_loaded(
1183 auto& state, const std::shared_ptr<oscr::raw_file_data>& hdl,
1184 avnd::predicate_index<N>, avnd::field_index<NField>)
1185 {
1186 this->rawfiles.load(
1187 state, hdl, avnd::predicate_index<N>{}, avnd::field_index<NField>{});
1188 }
1189};
1190
1191struct SCORE_PLUGIN_AVND_EXPORT CustomGfxNodeBase : score::gfx::NodeModel
1192{
1193 explicit CustomGfxNodeBase(const score::DocumentContext& ctx)
1194 : score::gfx::NodeModel{}
1195 , m_ctx{ctx}
1196 {
1197 }
1198 virtual ~CustomGfxNodeBase();
1199 const score::DocumentContext& m_ctx;
1200 score::gfx::Message last_message;
1201 void process(score::gfx::Message&& msg) override;
1203};
1204struct SCORE_PLUGIN_AVND_EXPORT CustomGfxOutputNodeBase : score::gfx::OutputNode
1205{
1206 virtual ~CustomGfxOutputNodeBase();
1207
1208 score::gfx::Message last_message;
1209 void process(score::gfx::Message&& msg) override;
1210};
1211struct CustomGpuNodeBase
1213 , GpuWorker
1214 , GpuControlIns
1215 , GpuControlOuts
1216{
1217 CustomGpuNodeBase(
1218 std::weak_ptr<Execution::ExecutionCommandQueue>&& q, Gfx::exec_controls&& ctls,
1219 const score::DocumentContext& ctx)
1220 : GpuControlOuts{std::move(q), std::move(ctls)}
1221 , m_ctx{ctx}
1222 {
1223 }
1224
1225 virtual ~CustomGpuNodeBase() = default;
1226
1227 const score::DocumentContext& m_ctx;
1228 QString vertex, fragment, compute;
1229 score::gfx::Message last_message;
1230 void process(score::gfx::Message&& msg) override;
1231};
1232
1233struct SCORE_PLUGIN_AVND_EXPORT CustomGpuOutputNodeBase
1235 , GpuWorker
1236 , GpuControlIns
1237 , GpuControlOuts
1238{
1239 CustomGpuOutputNodeBase(
1240 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls&& ctls,
1241 const score::DocumentContext& ctx);
1242 virtual ~CustomGpuOutputNodeBase();
1243
1244 const score::DocumentContext& m_ctx;
1245 std::weak_ptr<score::gfx::RenderList> m_renderer{};
1246 std::shared_ptr<score::gfx::RenderState> m_renderState{};
1247 std::function<void()> m_update;
1248
1249 QString vertex, fragment, compute;
1250 score::gfx::Message last_message;
1251 void process(score::gfx::Message&& msg) override;
1253
1254 void setRenderer(std::shared_ptr<score::gfx::RenderList>) override;
1255 score::gfx::RenderList* renderer() const override;
1256
1257 void startRendering() override;
1258 void render() override;
1259 void stopRendering() override;
1260 bool canRender() const override;
1261 void onRendererChange() override;
1262
1263 void createOutput(
1264 score::gfx::GraphicsApi graphicsApi, std::function<void()> onReady,
1265 std::function<void()> onUpdate, std::function<void()> onResize) override;
1266
1267 void destroyOutput() override;
1268 std::shared_ptr<score::gfx::RenderState> renderState() const override;
1269
1270 Configuration configuration() const noexcept override;
1271};
1272
1273template <typename Node_T, typename Node>
1274void prepareNewState(std::shared_ptr<Node_T>& eff, const Node& parent)
1275{
1276 if constexpr(avnd::has_worker<Node_T>)
1277 {
1278 parent.initWorker(eff);
1279 }
1280 if constexpr(avnd::has_processor_to_gui_bus<Node_T>)
1281 {
1282 auto& process = parent.processModel;
1283 eff->send_message = [ptr = QPointer{&process}](auto&& b) mutable {
1284 // FIXME right now all the rendering is done in the UI thread, which is very MEH
1285 // this->in_edit([&process, bb = std::move(b)]() mutable {
1286
1287 if(ptr && ptr->to_ui)
1288 MessageBusSender{ptr->to_ui}(std::move(b));
1289 // });
1290 };
1291
1292 // FIXME GUI -> engine. See executor.hpp
1293 }
1294
1295 avnd::init_controls(*eff);
1296
1297 if constexpr(avnd::can_prepare<Node_T>)
1298 {
1299 if constexpr(avnd::function_reflection<&Node_T::prepare>::count == 1)
1300 {
1301 using prepare_type = avnd::first_argument<&Node_T::prepare>;
1302 prepare_type t;
1303 if_possible(t.instance = parent.instance);
1304 eff->prepare(t);
1305 }
1306 else
1307 {
1308 eff->prepare();
1309 }
1310 }
1311}
1312
1313struct port_to_type_enum
1314{
1315 template <std::size_t I, avnd::cpu_texture_port F>
1316 constexpr auto operator()(avnd::field_reflection<I, F> p)
1317 {
1318 using texture_type = std::remove_cvref_t<decltype(F::texture)>;
1319 return avnd::cpu_fixed_format_texture<texture_type> ? score::gfx::Types::Image
1320 : score::gfx::Types::Buffer;
1321 }
1322 template <std::size_t I, avnd::sampler_port F>
1323 constexpr auto operator()(avnd::field_reflection<I, F> p)
1324 {
1325 return score::gfx::Types::Image;
1326 }
1327 template <std::size_t I, avnd::image_port F>
1328 constexpr auto operator()(avnd::field_reflection<I, F> p)
1329 {
1330 return score::gfx::Types::Image;
1331 }
1332 template <std::size_t I, avnd::attachment_port F>
1333 constexpr auto operator()(avnd::field_reflection<I, F> p)
1334 {
1335 return score::gfx::Types::Image;
1336 }
1337
1338 template <std::size_t I, avnd::geometry_port F>
1339 constexpr auto operator()(avnd::field_reflection<I, F> p)
1340 {
1341 return score::gfx::Types::Geometry;
1342 }
1343 template <std::size_t I, avnd::mono_audio_port F>
1344 constexpr auto operator()(avnd::field_reflection<I, F> p)
1345 {
1346 return score::gfx::Types::Audio;
1347 }
1348 template <std::size_t I, avnd::poly_audio_port F>
1349 constexpr auto operator()(avnd::field_reflection<I, F> p)
1350 {
1351 return score::gfx::Types::Audio;
1352 }
1353 template <std::size_t I, avnd::int_parameter F>
1354 constexpr auto operator()(avnd::field_reflection<I, F> p)
1355 {
1356 return score::gfx::Types::Int;
1357 }
1358 template <std::size_t I, avnd::enum_parameter F>
1359 constexpr auto operator()(avnd::field_reflection<I, F> p)
1360 {
1361 return score::gfx::Types::Int;
1362 }
1363 template <std::size_t I, avnd::float_parameter F>
1364 constexpr auto operator()(avnd::field_reflection<I, F> p)
1365 {
1366 return score::gfx::Types::Float;
1367 }
1368 template <std::size_t I, avnd::parameter F>
1369 constexpr auto operator()(avnd::field_reflection<I, F> p)
1370 {
1371 using value_type = std::remove_cvref_t<decltype(F::value)>;
1372
1373 if constexpr(std::is_aggregate_v<value_type>)
1374 {
1375 constexpr int sz = boost::pfr::tuple_size_v<value_type>;
1376 if constexpr(sz == 2)
1377 {
1378 return score::gfx::Types::Vec2;
1379 }
1380 else if constexpr(sz == 3)
1381 {
1382 return score::gfx::Types::Vec3;
1383 }
1384 else if constexpr(sz == 4)
1385 {
1386 return score::gfx::Types::Vec4;
1387 }
1388 }
1389 return score::gfx::Types::Empty;
1390 }
1391 template <std::size_t I, typename F>
1392 constexpr auto operator()(avnd::field_reflection<I, F> p)
1393 {
1394 return score::gfx::Types::Empty;
1395 }
1396};
1397
1398template <typename Node_T>
1399inline void initGfxPorts(auto* self, auto& input, auto& output)
1400{
1401 avnd::input_introspection<Node_T>::for_all(
1402 [self, &input]<typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
1403 static constexpr auto type = port_to_type_enum{}(f);
1404 input.push_back(new score::gfx::Port{self, {}, type, {}});
1405 });
1406 avnd::output_introspection<Node_T>::for_all(
1407 [self,
1408 &output]<typename Field, std::size_t I>(avnd::field_reflection<I, Field> f) {
1409 static constexpr auto type = port_to_type_enum{}(f);
1410 output.push_back(new score::gfx::Port{self, {}, type, {}});
1411 });
1412}
1413
1414inline void
1415inplaceMirror(unsigned char* bytes, int width, int height, int bytes_per_pixel)
1416{
1417 if(width < 1 || height <= 1)
1418 return;
1419 const size_t row_size = width * bytes_per_pixel;
1420
1421 auto temp_row = (unsigned char*)alloca(row_size);
1422 auto top = bytes;
1423 auto bottom = bytes + (height - 1) * row_size;
1424
1425 while(top < bottom)
1426 {
1427 memcpy(temp_row, top, row_size);
1428 memcpy(top, bottom, row_size);
1429 memcpy(bottom, temp_row, row_size);
1430
1431 top += row_size;
1432 bottom -= row_size;
1433 }
1434}
1435}
1436
1437#endif
Root data model for visual nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:75
virtual void process(Message &&msg)
Process a message from the execution engine.
Definition Node.cpp:25
Common base class for most single-pass, simple nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:230
Base class for sink nodes (QWindow, spout, syphon, NDI output, ...)
Definition OutputNode.hpp:24
List of nodes to be rendered to an output.
Definition RenderList.hpp:19
TreeNode< DeviceExplorerNode > Node
Definition DeviceNode.hpp:74
Definition Factories.hpp:19
GraphicsApi
Available graphics APIs to use.
Definition RenderState.hpp:20
Base toolkit upon which the software is built.
Definition Application.cpp:97
STL namespace.
Definition DocumentContext.hpp:18
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:50
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:52