GpuUtils.hpp
1 #pragma once
2 
3 #if SCORE_PLUGIN_GFX
4 #include <Process/ExecutionContext.hpp>
5 
6 #include <Crousti/MessageBus.hpp>
7 #include <Gfx/GfxExecNode.hpp>
8 #include <Gfx/Graph/Node.hpp>
9 #include <Gfx/Graph/OutputNode.hpp>
10 #include <Gfx/Graph/RenderState.hpp>
11 
12 #include <QTimer>
13 
14 #include <avnd/binding/ossia/port_run_postprocess.hpp>
15 #include <avnd/binding/ossia/port_run_preprocess.hpp>
16 #include <avnd/concepts/parameter.hpp>
17 #include <fmt/format.h>
18 #include <gpp/layout.hpp>
19 
20 #include <score_plugin_avnd_export.h>
21 
22 namespace gpp::qrhi
23 {
24 template <typename F>
25  requires requires { F::format(); }
26 constexpr QRhiTexture::Format textureFormat()
27 {
28  constexpr std::string_view fmt = F::format();
29 
30  if(fmt == "rgba" || fmt == "rgba8")
31  return QRhiTexture::RGBA8;
32  else if(fmt == "bgra" || fmt == "bgra8")
33  return QRhiTexture::BGRA8;
34  else if(fmt == "r8")
35  return QRhiTexture::R8;
36 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
37  else if(fmt == "rg8")
38  return QRhiTexture::RG8;
39 #endif
40  else if(fmt == "r16")
41  return QRhiTexture::R16;
42 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
43  else if(fmt == "rg16")
44  return QRhiTexture::RG16;
45 #endif
46  else if(fmt == "red_or_alpha8")
47  return QRhiTexture::RED_OR_ALPHA8;
48  else if(fmt == "rgba16f")
49  return QRhiTexture::RGBA16F;
50  else if(fmt == "rgba32f")
51  return QRhiTexture::RGBA32F;
52  else if(fmt == "r16f")
53  return QRhiTexture::R16F;
54  else if(fmt == "r32")
55  return QRhiTexture::R32F;
56 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
57  else if(fmt == "rgb10a2")
58  return QRhiTexture::RGB10A2;
59 #endif
60 
61  else if(fmt == "d16")
62  return QRhiTexture::D16;
63 
64 #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
65  else if(fmt == "d24")
66  return QRhiTexture::D24;
67  else if(fmt == "d24s8")
68  return QRhiTexture::D24S8;
69 #endif
70  else if(fmt == "d32f")
71  return QRhiTexture::D32F;
72 
73  else if(fmt == "bc1")
74  return QRhiTexture::BC1;
75  else if(fmt == "bc2")
76  return QRhiTexture::BC2;
77  else if(fmt == "bc3")
78  return QRhiTexture::BC3;
79  else if(fmt == "bc4")
80  return QRhiTexture::BC4;
81  else if(fmt == "bc5")
82  return QRhiTexture::BC5;
83  else if(fmt == "bc6h")
84  return QRhiTexture::BC6H;
85  else if(fmt == "bc7")
86  return QRhiTexture::BC7;
87  else if(fmt == "etc2_rgb8")
88  return QRhiTexture::ETC2_RGB8;
89  else if(fmt == "etc2_rgb8a1")
90  return QRhiTexture::ETC2_RGB8A1;
91  else if(fmt == "etc2_rgb8a8")
92  return QRhiTexture::ETC2_RGBA8;
93  else if(fmt == "astc_4x4")
94  return QRhiTexture::ASTC_4x4;
95  else if(fmt == "astc_5x4")
96  return QRhiTexture::ASTC_5x4;
97  else if(fmt == "astc_5x5")
98  return QRhiTexture::ASTC_5x5;
99  else if(fmt == "astc_6x5")
100  return QRhiTexture::ASTC_6x5;
101  else if(fmt == "astc_6x6")
102  return QRhiTexture::ASTC_6x6;
103  else if(fmt == "astc_8x5")
104  return QRhiTexture::ASTC_8x5;
105  else if(fmt == "astc_8x6")
106  return QRhiTexture::ASTC_8x6;
107  else if(fmt == "astc_8x8")
108  return QRhiTexture::ASTC_8x8;
109  else if(fmt == "astc_10x5")
110  return QRhiTexture::ASTC_10x5;
111  else if(fmt == "astc_10x6")
112  return QRhiTexture::ASTC_10x6;
113  else if(fmt == "astc_10x8")
114  return QRhiTexture::ASTC_10x8;
115  else if(fmt == "astc_10x10")
116  return QRhiTexture::ASTC_10x10;
117  else if(fmt == "astc_12x10")
118  return QRhiTexture::ASTC_12x10;
119  else if(fmt == "astc_12x12")
120  return QRhiTexture::ASTC_12x12;
121  else
122  return QRhiTexture::RGBA8;
123 }
124 
125 template <typename F>
126  requires std::is_enum_v<typename F::format>
127 constexpr QRhiTexture::Format textureFormat()
128 {
129  if constexpr(
130  requires { F::RGBA; } || requires { F::RGBA8; })
131  return QRhiTexture::RGBA8;
132  else if constexpr(
133  requires { F::BGRA; } || requires { F::BGRA8; })
134  return QRhiTexture::BGRA8;
135  else if constexpr(
136  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 struct DefaultPipeline
227 {
228  struct layout
229  {
230  enum
231  {
232  graphics
233  };
234  struct vertex_input
235  {
236  struct
237  {
238  static constexpr auto name() { return "position"; }
239  static constexpr int location() { return 0; }
240  float data[2];
241  } pos;
242  };
243  struct vertex_output
244  {
245  };
246  struct fragment_input
247  {
248  };
249  struct bindings
250  {
251  };
252  };
253 
254  static QString vertex()
255  {
256  return R"_(#version 450
257 layout(location = 0) in vec2 position;
258 out gl_PerVertex { vec4 gl_Position; };
259 void main() {
260  gl_Position = vec4( position, 0.0, 1.0 );
261 }
262 )_";
263  }
264 };
265 
266 template <typename C>
267 constexpr auto usage()
268 {
269  if constexpr(requires { C::vertex; })
270  return QRhiBuffer::VertexBuffer;
271  else if constexpr(requires { C::index; })
272  return QRhiBuffer::IndexBuffer;
273  else if constexpr(requires { C::ubo; })
274  return QRhiBuffer::UniformBuffer;
275  else if constexpr(requires { C::storage; })
276  return QRhiBuffer::StorageBuffer;
277  else
278  {
279  static_assert(C::unhandled);
280  throw;
281  }
282 }
283 
284 template <typename C>
285 constexpr auto buffer_type()
286 {
287  if constexpr(requires { C::immutable; })
288  return QRhiBuffer::Immutable;
289  else if constexpr(requires { C::static_; })
290  return QRhiBuffer::Static;
291  else if constexpr(requires { C::dynamic; })
292  return QRhiBuffer::Dynamic;
293  else
294  {
295  static_assert(C::unhandled);
296  throw;
297  }
298 }
299 
300 template <typename C>
301 auto samples(C c)
302 {
303  if constexpr(requires { C::samples; })
304  return c.samples;
305  else
306  return -1;
307 }
308 
309 struct handle_release
310 {
311  QRhi& rhi;
312  template <typename C>
313  void operator()(C command)
314  {
315  if constexpr(requires { C::deallocation; })
316  {
317  if constexpr(
318  requires { C::vertex; } || requires { C::index; } || requires { C::ubo; })
319  {
320  auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
321  buf->deleteLater();
322  }
323  else if constexpr(requires { C::sampler; })
324  {
325  auto buf = reinterpret_cast<QRhiSampler*>(command.handle);
326  buf->deleteLater();
327  }
328  else if constexpr(requires { C::texture; })
329  {
330  auto buf = reinterpret_cast<QRhiTexture*>(command.handle);
331  buf->deleteLater();
332  }
333  else
334  {
335  static_assert(C::unhandled);
336  }
337  }
338  else
339  {
340  static_assert(C::unhandled);
341  }
342  }
343 };
344 
345 template <typename Self, typename Res>
346 struct handle_dispatch
347 {
348  Self& self;
349  QRhi& rhi;
350  QRhiCommandBuffer& cb;
351  QRhiResourceUpdateBatch*& res;
352  QRhiComputePipeline& pip;
353  template <typename C>
354  Res operator()(C command)
355  {
356  if constexpr(requires { C::compute; })
357  {
358  if constexpr(requires { C::dispatch; })
359  {
360  cb.dispatch(command.x, command.y, command.z);
361  return {};
362  }
363  else if constexpr(requires { C::begin; })
364  {
365  cb.beginComputePass(res);
366  res = nullptr;
367  cb.setComputePipeline(&pip);
368  cb.setShaderResources(pip.shaderResourceBindings());
369 
370  return {};
371  }
372  else if constexpr(requires { C::end; })
373  {
374  cb.endComputePass(res);
375  res = nullptr;
376  rhi.finish();
377  return {};
378  }
379  else
380  {
381  static_assert(C::unhandled);
382  return {};
383  }
384  }
385  else if constexpr(requires { C::readback; })
386  {
387  // First handle the readback request
388  if constexpr(requires { C::request; })
389  {
390  if constexpr(requires { C::buffer; })
391  {
392  using ret = typename C::return_type;
393 
394 #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
395  auto readback = new QRhiBufferReadbackResult;
396 #else
397  auto readback = new QRhiReadbackResult;
398 #endif
399  self.addReadback(readback);
400 
401  // this is e.g. a buffer_awaiter
402  ret user_rb{.handle = reinterpret_cast<decltype(ret::handle)>(readback)};
403 
404  // TODO: do it with coroutines like this for peak asyncess
405  // ret must be a coroutine type.
406  // When the GPU completes the work, "completed" is called:
407  // this will cause the coroutine to be filled with the data
408  // readback->completed = [=] {
409  // qDebug() << "alhamdullilah he will be baked";
410  // // store "data" in the coroutine
411  // };
412 
413  auto next = rhi.nextResourceUpdateBatch();
414  auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
415 
416  next->readBackBuffer(buf, command.offset, command.size, readback);
417  res = next;
418 
419  return user_rb;
420  }
421  else if constexpr(requires { C::texture; })
422  {
423  using ret = typename C::return_type;
424  QRhiReadbackResult readback;
425  return ret{};
426  }
427  else
428  {
429  static_assert(C::unhandled);
430  return {};
431  }
432  }
433  else if constexpr(requires { C::await; })
434  {
435  if constexpr(requires { C::buffer; })
436  {
437  using ret = typename C::return_type;
438 
439 #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
440  auto readback = reinterpret_cast<QRhiBufferReadbackResult*>(command.handle);
441 #else
442  auto readback = reinterpret_cast<QRhiReadbackResult*>(command.handle);
443 #endif
444 
445  return ret{
446  .data = readback->data.data(), .size = (std::size_t)readback->data.size()};
447  }
448  else if constexpr(requires { C::texture; })
449  {
450  using ret = typename C::return_type;
451 
452  auto readback = reinterpret_cast<QRhiReadbackResult*>(command.handle);
453 
454  return ret{
455  .data = readback->data.data(), .size = (std::size_t)readback->data.size()};
456  }
457  }
458  }
459  else
460  {
461  static_assert(C::unhandled);
462  return {};
463  }
464 
465  return {};
466  }
467 };
468 
469 template <typename Self, typename Ret>
470 struct handle_update
471 {
472  Self& self;
473  QRhi& rhi;
474  QRhiResourceUpdateBatch& res;
475  std::vector<QRhiShaderResourceBinding>& srb;
476  bool& srb_touched;
477 
478  template <typename C>
479  Ret operator()(C command)
480  {
481  if constexpr(requires { C::allocation; })
482  {
483  if constexpr(
484  requires { C::vertex; } || requires { C::index; })
485  {
486  auto buf = rhi.newBuffer(buffer_type<C>(), usage<C>(), command.size);
487  buf->create();
488  return reinterpret_cast<typename C::return_type>(buf);
489  }
490  else if constexpr(requires { C::sampler; })
491  {
492  auto buf = rhi.newSampler({}, {}, {}, {}, {});
493  buf->create();
494  return reinterpret_cast<typename C::return_type>(buf);
495  }
496  else if constexpr(
497  requires { C::ubo; } || requires { C::storage; })
498  {
499  auto buf = rhi.newBuffer(buffer_type<C>(), usage<C>(), command.size);
500  buf->create();
501 
502  // Replace it in our bindings
503  score::gfx::replaceBuffer(srb, command.binding, buf);
504  srb_touched = true;
505  return reinterpret_cast<typename C::return_type>(buf);
506  }
507  else if constexpr(requires { C::texture; })
508  {
509  auto tex = rhi.newTexture(
510  QRhiTexture::RGBA8, QSize{command.width, command.height}, samples(command));
511  tex->create();
512 
513  score::gfx::replaceTexture(srb, command.binding, tex);
514  srb_touched = true;
515  return reinterpret_cast<typename C::return_type>(tex);
516  }
517  else
518  {
519  static_assert(C::unhandled);
520  return {};
521  }
522  }
523  else if constexpr(requires { C::upload; })
524  {
525  if constexpr(requires { C::texture; })
526  {
527  QRhiTextureSubresourceUploadDescription sub(command.data, command.size);
528  res.uploadTexture(
529  reinterpret_cast<QRhiTexture*>(command.handle),
530  QRhiTextureUploadDescription{{0, 0, sub}});
531  }
532  else
533  {
534  auto buf = reinterpret_cast<QRhiBuffer*>(command.handle);
535  if constexpr(requires { C::dynamic; })
536  res.updateDynamicBuffer(buf, command.offset, command.size, command.data);
537  else if constexpr(
538  requires { C::static_; } || requires { C::immutable; })
539  res.uploadStaticBuffer(buf, command.offset, command.size, command.data);
540  else
541  {
542  static_assert(C::unhandled);
543  return {};
544  }
545  }
546  }
547  else if constexpr(requires { C::getter; })
548  {
549  if constexpr(requires { C::ubo; })
550  {
551  auto buf = self.createdUbos.at(command.binding);
552  return reinterpret_cast<typename C::return_type>(buf);
553  }
554  else
555  {
556  static_assert(C::unhandled);
557  return {};
558  }
559  }
560  else
561  {
562  handle_release{rhi}(command);
563  return {};
564  }
565  return {};
566  }
567 };
568 
569 struct generate_shaders
570 {
571  template <typename T, int N>
572  using vec = T[N];
573 
574  static constexpr std::string_view field_type(float) { return "float"; }
575  static constexpr std::string_view field_type(const float (&)[2]) { return "vec2"; }
576  static constexpr std::string_view field_type(const float (&)[3]) { return "vec3"; }
577  static constexpr std::string_view field_type(const float (&)[4]) { return "vec4"; }
578 
579  static constexpr std::string_view field_type(float*) { return "float"; }
580  static constexpr std::string_view field_type(vec<float, 2>*) { return "vec2"; }
581  static constexpr std::string_view field_type(vec<float, 3>*) { return "vec3"; }
582  static constexpr std::string_view field_type(vec<float, 4>*) { return "vec4"; }
583 
584  static constexpr std::string_view field_type(int) { return "int"; }
585  static constexpr std::string_view field_type(const int (&)[2]) { return "ivec2"; }
586  static constexpr std::string_view field_type(const int (&)[3]) { return "ivec3"; }
587  static constexpr std::string_view field_type(const int (&)[4]) { return "ivec4"; }
588 
589  static constexpr std::string_view field_type(int*) { return "int"; }
590  static constexpr std::string_view field_type(vec<int, 2>*) { return "ivec2"; }
591  static constexpr std::string_view field_type(vec<int, 3>*) { return "ivec3"; }
592  static constexpr std::string_view field_type(vec<int, 4>*) { return "ivec4"; }
593 
594  static constexpr std::string_view field_type(uint32_t) { return "uint"; }
595  static constexpr std::string_view field_type(const uint32_t (&)[2]) { return "uvec2"; }
596  static constexpr std::string_view field_type(const uint32_t (&)[3]) { return "uvec3"; }
597  static constexpr std::string_view field_type(const uint32_t (&)[4]) { return "uvec4"; }
598 
599  static constexpr std::string_view field_type(uint32_t*) { return "uint"; }
600  static constexpr std::string_view field_type(vec<uint32_t, 2>*) { return "uvec2"; }
601  static constexpr std::string_view field_type(vec<uint32_t, 3>*) { return "uvec3"; }
602  static constexpr std::string_view field_type(vec<uint32_t, 4>*) { return "uvec4"; }
603 
604  static constexpr std::string_view field_type(avnd::xy_value auto) { return "vec2"; }
605 
606  static constexpr bool field_array(float) { return false; }
607  static constexpr bool field_array(const float (&)[2]) { return false; }
608  static constexpr bool field_array(const float (&)[3]) { return false; }
609  static constexpr bool field_array(const float (&)[4]) { return false; }
610 
611  static constexpr bool field_array(float*) { return true; }
612  static constexpr bool field_array(vec<float, 2>*) { return true; }
613  static constexpr bool field_array(vec<float, 3>*) { return true; }
614  static constexpr bool field_array(vec<float, 4>*) { return true; }
615 
616  static constexpr bool field_array(int) { return false; }
617  static constexpr bool field_array(const int (&)[2]) { return false; }
618  static constexpr bool field_array(const int (&)[3]) { return false; }
619  static constexpr bool field_array(const int (&)[4]) { return false; }
620 
621  static constexpr bool field_array(int*) { return true; }
622  static constexpr bool field_array(vec<int, 2>*) { return true; }
623  static constexpr bool field_array(vec<int, 3>*) { return true; }
624  static constexpr bool field_array(vec<int, 4>*) { return true; }
625 
626  static constexpr bool field_array(uint32_t) { return false; }
627  static constexpr bool field_array(const uint32_t (&)[2]) { return false; }
628  static constexpr bool field_array(const uint32_t (&)[3]) { return false; }
629  static constexpr bool field_array(const uint32_t (&)[4]) { return false; }
630 
631  static constexpr bool field_array(uint32_t*) { return true; }
632  static constexpr bool field_array(vec<uint32_t, 2>*) { return true; }
633  static constexpr bool field_array(vec<uint32_t, 3>*) { return true; }
634  static constexpr bool field_array(vec<uint32_t, 4>*) { return true; }
635 
636  static constexpr bool field_array(avnd::xy_value auto) { return false; }
637 
638  template <typename T>
639  static constexpr std::string_view image_qualifier()
640  {
641  if constexpr(requires { T::readonly; })
642  return "readonly";
643  else if constexpr(requires { T::writeonly; })
644  return "writeonly";
645  else
646  static_assert(T::readonly || T::writeonly);
647  }
648 
649  struct write_input
650  {
651  std::string& shader;
652 
653  template <typename T>
654  void operator()(const T& field)
655  {
656  shader += fmt::format(
657  "layout(location = {}) in {} {};\n", T::location(), field_type(field.data),
658  T::name());
659  }
660  };
661 
662  struct write_output
663  {
664  std::string& shader;
665 
666  template <typename T>
667  void operator()(const T& field)
668  {
669  if constexpr(requires { field.location(); })
670  {
671  shader += fmt::format(
672  "layout(location = {}) out {} {};\n", T::location(), field_type(field.data),
673  T::name());
674  }
675  }
676  };
677 
678  struct write_binding
679  {
680  std::string& shader;
681 
682  template <typename T>
683  void operator()(const T& field)
684  {
685  shader += fmt::format(
686  " {} {}{};\n", field_type(field.value), T::name(),
687  field_array(field.value) ? "[]" : "");
688  }
689  };
690 
691  struct write_bindings
692  {
693  std::string& shader;
694 
695  template <typename C>
696  void operator()(const C& field)
697  {
698  if constexpr(requires { C::sampler2D; })
699  {
700  shader += fmt::format(
701  "layout(binding = {}) uniform sampler2D {};\n\n", C::binding(), C::name());
702  }
703  else if constexpr(requires { C::image2D; })
704  {
705  shader += fmt::format(
706  "layout(binding = {}, {}) {} uniform image2D {};\n\n", C::binding(),
707  C::format(), image_qualifier<C>(), C::name());
708  }
709  else if constexpr(requires { C::ubo; })
710  {
711  shader += fmt::format(
712  "layout({}, binding = {}) uniform {}\n{{\n",
713  "std140" // TODO
714  ,
715  C::binding(), C::name());
716 
717  boost::pfr::for_each_field(field, write_binding{shader});
718 
719  shader += fmt::format("}};\n\n");
720  }
721  else if constexpr(requires { C::buffer; })
722  {
723  shader += fmt::format(
724  "layout({}, binding = {}) buffer {}\n{{\n",
725  "std140" // TODO
726  ,
727  C::binding(), C::name());
728 
729  boost::pfr::for_each_field(field, write_binding{shader});
730 
731  shader += fmt::format("}};\n\n");
732  }
733  }
734  };
735 
736  template <typename T>
737  std::string vertex_shader(const T& lay)
738  {
739  using namespace gpp::qrhi;
740  std::string shader = "#version 450\n\n";
741 
742  if constexpr(requires { lay.vertex_input; })
743  boost::pfr::for_each_field(lay.vertex_input, write_input{shader});
744  else if constexpr(requires { typename T::vertex_input; })
745  boost::pfr::for_each_field(typename T::vertex_input{}, write_input{shader});
746  else
747  boost::pfr::for_each_field(
748  DefaultPipeline::layout::vertex_input{}, write_input{shader});
749 
750  if constexpr(requires { lay.vertex_output; })
751  boost::pfr::for_each_field(lay.vertex_output, write_output{shader});
752  else if constexpr(requires { typename T::vertex_output; })
753  boost::pfr::for_each_field(typename T::vertex_output{}, write_output{shader});
754 
755  shader += "\n";
756 
757  if constexpr(requires { lay.bindings; })
758  boost::pfr::for_each_field(lay.bindings, write_bindings{shader});
759  else if constexpr(requires { typename T::bindings; })
760  boost::pfr::for_each_field(typename T::bindings{}, write_bindings{shader});
761 
762  return shader;
763  }
764 
765  template <typename T>
766  std::string fragment_shader(const T& lay)
767  {
768  std::string shader = "#version 450\n\n";
769 
770  if constexpr(requires { lay.fragment_input; })
771  boost::pfr::for_each_field(lay.fragment_input, write_input{shader});
772  else if constexpr(requires { typename T::fragment_input; })
773  boost::pfr::for_each_field(typename T::fragment_input{}, write_input{shader});
774 
775  if constexpr(requires { lay.fragment_output; })
776  boost::pfr::for_each_field(lay.fragment_output, write_output{shader});
777  else if constexpr(requires { typename T::fragment_output; })
778  boost::pfr::for_each_field(typename T::fragment_output{}, write_output{shader});
779 
780  shader += "\n";
781 
782  if constexpr(requires { lay.bindings; })
783  boost::pfr::for_each_field(lay.bindings, write_bindings{shader});
784  else if constexpr(requires { typename T::bindings; })
785  boost::pfr::for_each_field(typename T::bindings{}, write_bindings{shader});
786 
787  return shader;
788  }
789 
790  template <typename T>
791  std::string compute_shader(const T& lay)
792  {
793  std::string fstr = "#version 450\n\n";
794 
795  fstr += "layout(";
796  if constexpr(requires { T::local_size_x(); })
797  {
798  fstr += fmt::format("local_size_x = {}, ", T::local_size_x());
799  }
800  if constexpr(requires { T::local_size_y(); })
801  {
802  fstr += fmt::format("local_size_y = {}, ", T::local_size_y());
803  }
804  if constexpr(requires { T::local_size_z(); })
805  {
806  fstr += fmt::format("local_size_z = {}, ", T::local_size_z());
807  }
808 
809  // Remove the last ", "
810  fstr.resize(fstr.size() - 2);
811  fstr += ") in;\n\n";
812 
813  boost::pfr::for_each_field(lay.bindings, write_bindings{fstr});
814 
815  return fstr;
816  }
817 };
818 }
819 
820 namespace oscr
821 {
822 struct GpuWorker
823 {
824  template <typename Node>
825  static void initWorker(Node& state) noexcept
826  {
827  if constexpr(avnd::has_worker<Node>)
828  {
829  using worker_type = decltype(state.worker);
830 
831  state.worker.request = [&state]<typename... Args>(Args&&... f) {
832  using type_of_result = decltype(worker_type::work(std::forward<Args>(f)...));
833  if constexpr(std::is_void_v<type_of_result>)
834  {
835  worker_type::work(std::forward<Args>(f)...);
836  }
837  else
838  {
839  // If the worker returns a std::function, it
840  // is to be invoked back in the processor DSP thread
841  auto res = worker_type::work(std::forward<Args>(f)...);
842  if(!res)
843  return;
844  res(state);
845  }
846  };
847  }
848  }
849 };
850 struct GpuControlIns
851 {
852  template <typename Node_T>
853  static void processControlIn(Node_T& state, const score::gfx::Message& mess) noexcept
854  {
855  // Apply the controls
856  avnd::parameter_input_introspection<Node_T>::for_all_n2(
857  avnd::get_inputs<Node_T>(state),
858  [&](avnd::parameter auto& t, auto pred_index, auto field_index) {
859  if(mess.input.size() > field_index)
860  {
861  if(auto val = ossia::get_if<ossia::value>(&mess.input[field_index]))
862  {
863  oscr::from_ossia_value(t, *val, t.value);
864  if_possible(t.update(state));
865  }
866  }
867  });
868  }
869 };
870 
871 struct GpuControlOuts
872 {
873  std::weak_ptr<Execution::ExecutionCommandQueue> queue;
874  Gfx::exec_controls control_outs;
875 
876  int instance{};
877 
878  template <typename Node_T>
879  void processControlOut(Node_T& state) const noexcept
880  {
881  if(!this->control_outs.empty())
882  {
883  auto q = this->queue.lock();
884  if(!q)
885  return;
886  auto& qq = *q;
887  int parm_k = 0;
888  avnd::parameter_output_introspection<Node_T>::for_all(
889  avnd::get_outputs(state), [&]<avnd::parameter T>(const T& t) {
890  qq.enqueue([v = oscr::to_ossia_value(t, t.value),
891  port = control_outs[parm_k]]() mutable {
892  std::swap(port->value, v);
893  port->changed = true;
894  });
895 
896  parm_k++;
897  });
898  }
899  }
900 };
901 
902 struct SCORE_PLUGIN_AVND_EXPORT CustomGfxNodeBase : score::gfx::NodeModel
903 {
904  virtual ~CustomGfxNodeBase();
905 
906  score::gfx::Message last_message;
907  void process(score::gfx::Message&& msg) override;
908 };
909 struct SCORE_PLUGIN_AVND_EXPORT CustomGfxOutputNodeBase : score::gfx::OutputNode
910 {
911  virtual ~CustomGfxOutputNodeBase();
912 
913  score::gfx::Message last_message;
914  void process(score::gfx::Message&& msg) override;
915 };
916 struct CustomGpuNodeBase
918  , GpuWorker
919  , GpuControlIns
920  , GpuControlOuts
921 {
922  CustomGpuNodeBase(
923  std::weak_ptr<Execution::ExecutionCommandQueue>&& q, Gfx::exec_controls&& ctls)
924  : GpuControlOuts{std::move(q), std::move(ctls)}
925  {
926  }
927 
928  virtual ~CustomGpuNodeBase() = default;
929 
930  QString vertex, fragment, compute;
931  score::gfx::Message last_message;
932  void process(score::gfx::Message&& msg) override;
933 };
934 
935 struct SCORE_PLUGIN_AVND_EXPORT CustomGpuOutputNodeBase
937  , GpuWorker
938  , GpuControlIns
939  , GpuControlOuts
940 {
941  CustomGpuOutputNodeBase(
942  std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls&& ctls);
943  virtual ~CustomGpuOutputNodeBase();
944 
945  std::weak_ptr<score::gfx::RenderList> m_renderer{};
946  std::shared_ptr<score::gfx::RenderState> m_renderState{};
947  std::function<void()> m_update;
948 
949  QString vertex, fragment, compute;
950  score::gfx::Message last_message;
951  void process(score::gfx::Message&& msg) override;
952 
953  void setRenderer(std::shared_ptr<score::gfx::RenderList>) override;
954  score::gfx::RenderList* renderer() const override;
955 
956  void startRendering() override;
957  void render() override;
958  void stopRendering() override;
959  bool canRender() const override;
960  void onRendererChange() override;
961 
962  void createOutput(
963  score::gfx::GraphicsApi graphicsApi, std::function<void()> onReady,
964  std::function<void()> onUpdate, std::function<void()> onResize) override;
965 
966  void destroyOutput() override;
967  std::shared_ptr<score::gfx::RenderState> renderState() const override;
968 
969  Configuration configuration() const noexcept override;
970 };
971 
972 template <typename Node_T, typename Node>
973 void prepareNewState(Node_T& eff, const Node& parent)
974 {
975  if constexpr(avnd::has_worker<Node_T>)
976  {
977  parent.initWorker(eff);
978  }
979  if constexpr(avnd::has_processor_to_gui_bus<Node_T>)
980  {
981  auto& process = parent.processModel;
982  eff.send_message = [&process](auto&& b) mutable {
983  // FIXME right now all the rendering is done in the UI thread, which is very MEH
984  // this->in_edit([&process, bb = std::move(b)]() mutable {
985 
986  if(process.to_ui)
987  MessageBusSender{process.to_ui}(std::move(b));
988  // });
989  };
990 
991  // FIXME GUI -> engine. See executor.hpp
992  }
993 
994  avnd::init_controls(eff);
995 
996  if constexpr(avnd::can_prepare<Node_T>)
997  {
998  using prepare_type = avnd::first_argument<&Node_T::prepare>;
999  prepare_type t;
1000  if_possible(t.instance = parent.instance);
1001  eff.prepare(t);
1002  }
1003 }
1004 }
1005 
1006 #endif
Root data model for visual nodes.
Definition: score-plugin-gfx/Gfx/Graph/Node.hpp:60
virtual void process(Message &&msg)
Process a message from the execution engine.
Definition: Node.cpp:24
Common base class for most single-pass, simple nodes.
Definition: score-plugin-gfx/Gfx/Graph/Node.hpp:180
Base class for sink nodes (QWindow, spout, syphon, NDI output, ...)
Definition: OutputNode.hpp:23
void process(Message &&msg) override
Process a message from the execution engine.
Definition: Node.cpp:337
List of nodes to be rendered to an output.
Definition: RenderList.hpp:19
GraphicsApi
Available graphics APIs to use.
Definition: RenderState.hpp:17
Definition: score-plugin-gfx/Gfx/Graph/Node.hpp:51