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