CpuGeneratorNode.hpp
1 #pragma once
2 
3 #if SCORE_PLUGIN_GFX
4 #include <Crousti/GfxNode.hpp>
5 
6 namespace oscr
7 {
8 
9 template <typename Node_T>
10  requires(
11  avnd::texture_input_introspection<Node_T>::size == 0
12  && avnd::texture_output_introspection<Node_T>::size > 0)
13 struct GfxRenderer<Node_T> final : score::gfx::GenericNodeRenderer
14 {
15  using texture_outputs = avnd::texture_output_introspection<Node_T>;
16  const GfxNode<Node_T>& parent;
17  Node_T state;
18  ossia::time_value m_last_time{-1};
19 
20  GfxRenderer(const GfxNode<Node_T>& p)
21  : score::gfx::GenericNodeRenderer{p}
22  , parent{p}
23  {
24  prepareNewState(state, parent);
25  }
26 
28  renderTargetForInput(const score::gfx::Port& p) override
29  {
30  return {};
31  }
32 
33  template <typename Tex>
34  void
35  createOutput(score::gfx::RenderList& renderer, const Tex& texture_spec, QSize size)
36  {
37  auto& rhi = *renderer.state.rhi;
38  QRhiTexture* texture = &renderer.emptyTexture();
39  if(size.width() > 0 && size.height() > 0)
40  {
41  texture = rhi.newTexture(
42  gpp::qrhi::textureFormat<Tex>(), size, 1, QRhiTexture::Flag{});
43 
44  texture->create();
45  }
46 
47  auto sampler = rhi.newSampler(
48  QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
49  QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
50 
51  sampler->create();
52  this->m_samplers.push_back({sampler, texture});
53  }
54 
55  template <avnd::cpu_texture Tex>
56  QRhiTexture* updateTexture(score::gfx::RenderList& renderer, int k, const Tex& cpu_tex)
57  {
58  auto& [sampler, texture] = this->m_samplers[k];
59  if(texture)
60  {
61  auto sz = texture->pixelSize();
62  if(cpu_tex.width == sz.width() && cpu_tex.height == sz.height())
63  return texture;
64  }
65 
66  // Check the texture size
67  if(cpu_tex.width > 0 && cpu_tex.height > 0)
68  {
69  QRhiTexture* oldtex = texture;
70  QRhiTexture* newtex = renderer.state.rhi->newTexture(
71  gpp::qrhi::textureFormat<Tex>(), QSize{cpu_tex.width, cpu_tex.height}, 1,
72  QRhiTexture::Flag{});
73  newtex->create();
74  for(auto& [edge, pass] : this->m_p)
75  if(pass.srb)
76  score::gfx::replaceTexture(*pass.srb, sampler, newtex);
77  texture = newtex;
78 
79  if(oldtex && oldtex != &renderer.emptyTexture())
80  {
81  oldtex->deleteLater();
82  }
83 
84  return newtex;
85  }
86  else
87  {
88  for(auto& [edge, pass] : this->m_p)
89  if(pass.srb)
90  score::gfx::replaceTexture(*pass.srb, sampler, &renderer.emptyTexture());
91 
92  return &renderer.emptyTexture();
93  }
94  }
95 
96  void uploadOutputTexture(
97  score::gfx::RenderList& renderer, int k, avnd::cpu_texture auto& cpu_tex,
98  QRhiResourceUpdateBatch* res)
99  {
100  if(cpu_tex.changed)
101  {
102  if(auto texture = updateTexture(renderer, k, cpu_tex))
103  {
104  // Upload it
105  {
106  QRhiTextureSubresourceUploadDescription sd(
107  cpu_tex.bytes, cpu_tex.width * cpu_tex.height * 4);
108  QRhiTextureUploadDescription desc{QRhiTextureUploadEntry{0, 0, sd}};
109 
110  res->uploadTexture(texture, desc);
111  }
112 
113  cpu_tex.changed = false;
114  k++;
115  }
116  }
117  }
118 
119  void init(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
120  {
121  if constexpr(requires { state.init(); })
122  {
123  state.init();
124  }
125 
126  const auto& mesh = renderer.defaultTriangle();
127  this->defaultMeshInit(renderer, mesh, res);
128  this->processUBOInit(renderer);
129  this->m_material.init(renderer, this->node.input, this->m_samplers);
130  std::tie(this->m_vertexS, this->m_fragmentS)
131  = score::gfx::makeShaders(renderer.state, generic_texgen_vs, generic_texgen_fs);
132 
133  // Init textures for the outputs
134  avnd::cpu_texture_output_introspection<Node_T>::for_all(
135  avnd::get_outputs<Node_T>(state), [&](auto& t) {
136  createOutput(renderer, t.texture, QSize{t.texture.width, t.texture.height});
137  });
138 
139  this->defaultPassesInit(renderer, mesh);
140  }
141 
142  void update(score::gfx::RenderList& renderer, QRhiResourceUpdateBatch& res) override
143  {
144  this->defaultUBOUpdate(renderer, res);
145  }
146 
147  void release(score::gfx::RenderList& r) override
148  {
149  // Free outputs
150  for(auto& [sampl, texture] : this->m_samplers)
151  {
152  if(texture != &r.emptyTexture())
153  texture->deleteLater();
154  texture = nullptr;
155  }
156 
157  this->defaultRelease(r);
158  }
159 
160  void runInitialPasses(
161  score::gfx::RenderList& renderer, QRhiCommandBuffer& commands,
162  QRhiResourceUpdateBatch*& res, score::gfx::Edge& edge) override
163  {
164  // If we are paused, we don't run the processor implementation.
165  if(parent.last_message.token.date == m_last_time)
166  {
167  return;
168  }
169  m_last_time = parent.last_message.token.date;
170 
171  parent.processControlIn(state, this->parent.last_message);
172 
173  // Run the processor
174  state();
175 
176  // Upload output textures
177  int k = 0;
178  avnd::cpu_texture_output_introspection<Node_T>::for_all(
179  avnd::get_outputs<Node_T>(state), [&](auto& t) {
180  uploadOutputTexture(renderer, k, t.texture, res);
181  k++;
182  });
183 
184  commands.resourceUpdate(res);
185  res = renderer.state.rhi->nextResourceUpdateBatch();
186 
187  // Copy the data to the model node
188  parent.processControlOut(this->state);
189  }
190 };
191 
192 template <typename Node_T>
193  requires(avnd::texture_input_introspection<Node_T>::size == 0
194  && avnd::texture_output_introspection<Node_T>::size > 0)
195 struct GfxNode<Node_T> final
196  : CustomGfxNodeBase
197  , GpuWorker
198  , GpuControlIns
199  , GpuControlOuts
200 {
201  oscr::ProcessModel<Node_T>& processModel;
202  GfxNode(
204  std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls, int id)
205  : GpuControlOuts{std::move(q), std::move(ctls)}
206  , processModel{element}
207  {
208  this->instance = id;
209 
210  using texture_outputs = avnd::texture_output_introspection<Node_T>;
211 
212  // FIXME incorrect if we have other ports before, e.g. a float part followed by an image port
213  for(std::size_t i = 0; i < texture_outputs::size; i++)
214  {
215  this->output.push_back(
216  new score::gfx::Port{this, {}, score::gfx::Types::Image, {}});
217  }
218  }
219 
221  createRenderer(score::gfx::RenderList& r) const noexcept override
222  {
223  return new GfxRenderer<Node_T>{*this};
224  }
225 };
226 }
227 #endif
Definition: score-plugin-avnd/Crousti/ProcessModel.hpp:551
Renderer for a given node.
Definition: NodeRenderer.hpp:11
List of nodes to be rendered to an output.
Definition: RenderList.hpp:19
const score::gfx::Mesh & defaultTriangle() const noexcept
A triangle mesh correct for this API.
Definition: RenderList.cpp:247
RenderState & state
RenderState corresponding to this RenderList.
Definition: RenderList.hpp:89
QRhiTexture & emptyTexture() const noexcept
Texture to use when a texture is missing.
Definition: RenderList.hpp:111
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition: score-plugin-gfx/Gfx/Graph/Utils.cpp:334
Base toolkit upon which the software is built.
Definition: Application.cpp:90
Connection between two score::gfx::Port.
Definition: score-plugin-gfx/Gfx/Graph/Utils.hpp:64
Port of a score::gfx::Node.
Definition: score-plugin-gfx/Gfx/Graph/Utils.hpp:46
Useful abstraction for storing all the data related to a render target.
Definition: score-plugin-gfx/Gfx/Graph/Utils.hpp:109