4 #include <Process/ExecutionContext.hpp>
6 #include <Crousti/Concepts.hpp>
7 #include <Crousti/GpuUtils.hpp>
8 #include <Crousti/Metadatas.hpp>
9 #include <Gfx/GfxExecNode.hpp>
10 #include <Gfx/Graph/NodeRenderer.hpp>
11 #include <Gfx/Graph/RenderList.hpp>
12 #include <Gfx/Graph/Uniforms.hpp>
18 template <
typename Node_T>
19 using ComputeNodeBaseType = std::conditional_t<
20 avnd::gpu_image_output_introspection<Node_T>::size != 0, CustomGpuNodeBase,
21 CustomGpuOutputNodeBase>;
22 template <
typename Node_T>
23 using ComputeRendererBaseType = std::conditional_t<
27 template <
typename Node_T>
28 struct GpuComputeNode final : ComputeNodeBaseType<Node_T>
31 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls,
int id)
32 : ComputeNodeBaseType<Node_T>{std::move(q), std::move(ctls)}
36 using texture_inputs = avnd::gpu_image_input_introspection<Node_T>;
37 using texture_outputs = avnd::gpu_image_output_introspection<Node_T>;
39 for(std::size_t i = 0; i < texture_inputs::size; i++)
41 this->input.push_back(
44 for(std::size_t i = 0; i < texture_outputs::size; i++)
46 this->output.push_back(
50 using layout =
typename Node_T::layout;
51 static constexpr
auto lay = layout{};
53 gpp::qrhi::generate_shaders gen;
55 = QString::fromStdString(gen.compute_shader(lay) + Node_T{}.compute().data());
62 template <
typename Node_T>
63 struct GpuComputeRenderer final : ComputeRendererBaseType<Node_T>
65 using texture_inputs = avnd::gpu_image_input_introspection<Node_T>;
66 using texture_outputs = avnd::gpu_image_output_introspection<Node_T>;
67 const GpuComputeNode<Node_T>& parent;
69 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
72 ossia::time_value m_last_time{-1};
74 QRhiShaderResourceBindings* m_srb{};
75 QRhiComputePipeline* m_pipeline{};
77 bool m_createdPipeline{};
81 ossia::flat_map<int, QRhiBuffer*> createdUbos;
82 ossia::flat_map<int, QRhiTexture*> createdTexs;
84 #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
85 std::vector<QRhiBufferReadbackResult*> bufReadbacks;
86 void addReadback(QRhiBufferReadbackResult* r) { bufReadbacks.push_back(r); }
89 std::vector<QRhiReadbackResult*> texReadbacks;
90 void addReadback(QRhiReadbackResult* r) { texReadbacks.push_back(r); }
91 GpuComputeRenderer(
const GpuComputeNode<Node_T>& p)
92 : ComputeRendererBaseType<Node_T>{}
95 prepareNewState(state, parent);
101 auto it = m_rts.find(&p);
102 SCORE_ASSERT(it != m_rts.end());
106 QRhiTexture* createInput(
109 auto port = parent.input[k];
110 constexpr
auto flags = QRhiTexture::RenderTarget | QRhiTexture::UsedWithLoadStore;
111 auto texture = renderer.
state.rhi->newTexture(fmt, size, 1, flags);
112 SCORE_ASSERT(texture->create());
118 template <
typename F>
121 constexpr
auto bindingStages = QRhiShaderResourceBinding::ComputeStage;
122 if constexpr(requires { F::ubo; })
124 auto it = createdUbos.find(F::binding());
125 QRhiBuffer* buffer = it != createdUbos.end() ? it->second :
nullptr;
126 return QRhiShaderResourceBinding::uniformBuffer(
127 F::binding(), bindingStages, buffer);
129 else if constexpr(requires { F::image2D; })
131 auto tex_it = createdTexs.find(F::binding());
133 = tex_it != createdTexs.end() ? tex_it->second : &renderer.
emptyTexture();
136 requires { F::load; } && requires { F::store; })
137 return QRhiShaderResourceBinding::imageLoadStore(
138 F::binding(), bindingStages, tex, 0);
139 else if constexpr(requires { F::readonly; })
140 return QRhiShaderResourceBinding::imageLoad(F::binding(), bindingStages, tex, 0);
141 else if constexpr(requires { F::writeonly; })
142 return QRhiShaderResourceBinding::imageStore(
143 F::binding(), bindingStages, tex, 0);
145 static_assert(F::load || F::store);
147 else if constexpr(requires { F::buffer; })
149 auto it = createdUbos.find(F::binding());
150 QRhiBuffer* buf = it != createdUbos.end() ? it->second :
nullptr;
153 requires { F::load; } && requires { F::store; })
154 return QRhiShaderResourceBinding::bufferLoadStore(
155 F::binding(), bindingStages, buf);
156 else if constexpr(requires { F::load; })
157 return QRhiShaderResourceBinding::bufferLoad(F::binding(), bindingStages, buf);
158 else if constexpr(requires { F::store; })
159 return QRhiShaderResourceBinding::bufferStore(F::binding(), bindingStages, buf);
161 static_assert(F::load || F::store);
165 static_assert(F::nope);
172 auto& rhi = *renderer.
state.rhi;
174 auto srb = rhi.newShaderResourceBindings();
177 QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
179 using bindings_type = decltype(Node_T::layout::bindings);
180 boost::pfr::for_each_field(
181 bindings_type{}, [&](
auto f) { bindings.push_back(initBinding(renderer, f)); });
183 srb->setBindings(bindings.begin(), bindings.end());
189 auto& rhi = *renderer.
state.rhi;
190 auto compute = rhi.newComputePipeline();
192 compute->setShaderStage(QRhiShaderStage(QRhiShaderStage::Compute, cs));
202 template <std::
size_t Idx,
typename F>
203 requires avnd::image_port<F>
206 using bindings_type = decltype(Node_T::layout::bindings);
207 using image_type = std::decay_t<decltype(bindings_type{}.*F::image())>;
208 auto tex = createInput(
209 renderer, sampler_k++, gpp::qrhi::textureFormat<image_type>(),
210 renderer.
state.renderSize);
212 using sampler_type =
typename avnd::member_reflection<F::image()>::member_type;
213 createdTexs[sampler_type::binding()] = tex;
216 template <std::
size_t Idx,
typename F>
217 requires avnd::uniform_port<F>
220 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
223 if(createdUbos.find(ubo_type::binding()) != createdUbos.end())
226 auto ubo = renderer.
state.rhi->newBuffer(
227 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, gpp::std140_size<ubo_type>());
230 createdUbos[ubo_type::binding()] = ubo;
235 if constexpr(requires { state.init(); })
241 avnd::input_introspection<Node_T>::for_all(
242 [
this, &renderer](
auto f) { init_input(renderer, f); });
244 m_srb = initBindings(renderer);
245 m_pipeline = createComputePipeline(renderer);
246 m_pipeline->setShaderResourceBindings(m_srb);
249 if constexpr(!requires { &Node_T::update; })
251 SCORE_ASSERT(m_srb->create());
252 SCORE_ASSERT(m_pipeline->create());
253 m_createdPipeline =
true;
257 std::vector<QRhiShaderResourceBinding> tmp;
261 avnd::gpu_uniform_introspection<Node_T>::for_all(
262 avnd::get_inputs<Node_T>(state), [&]<avnd::uniform_port F>(
const F& t) {
264 typename avnd::member_reflection<F::uniform()>::member_type;
265 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
267 auto ubo = this->createdUbos.at(ubo_type::binding());
269 constexpr
int offset = gpp::std140_offset<F::uniform()>();
270 constexpr
int size =
sizeof(uniform_type::value);
271 res.updateDynamicBuffer(ubo, offset, size, &t.value);
278 if constexpr(requires { &Node_T::update; })
280 bool srb_touched{
false};
281 tmp.assign(m_srb->cbeginBindings(), m_srb->cendBindings());
282 for(
auto& promise : state.update())
284 using ret_type = decltype(promise.feedback_value);
285 gpp::qrhi::handle_update<GpuComputeRenderer, ret_type> handler{
286 *
this, *renderer.
state.rhi, res, tmp, srb_touched};
287 promise.feedback_value = visit(handler, promise.current_command);
292 if(m_createdPipeline)
294 m_srb->setBindings(tmp.begin(), tmp.end());
321 if(!m_createdPipeline)
323 SCORE_ASSERT(m_srb->create());
324 SCORE_ASSERT(m_pipeline->create());
325 m_createdPipeline =
true;
333 m_createdPipeline =
false;
336 if constexpr(requires { &Node_T::release; })
338 for(
auto& promise : state.release())
340 gpp::qrhi::handle_release handler{*r.
state.rhi};
341 visit(handler, promise.current_command);
347 for(
auto& [
id, tex] : this->createdTexs)
349 this->createdTexs.clear();
352 for(
auto& [
id, ubo] : this->createdUbos)
354 this->createdUbos.clear();
364 m_srb->deleteLater();
366 m_pipeline->deleteLater();
368 m_pipeline =
nullptr;
370 m_createdPipeline =
false;
378 QRhiResourceUpdateBatch*& res)
387 parent.processControlIn(this->state, this->parent.last_message);
391 SCORE_ASSERT(this->m_pipeline);
392 SCORE_ASSERT(this->m_pipeline->shaderResourceBindings());
393 for(
auto& promise : this->state.dispatch())
395 using ret_type = decltype(promise.feedback_value);
396 gpp::qrhi::handle_dispatch<GpuComputeRenderer, ret_type> handler{
397 *
this, *renderer.
state.rhi, cb, res, *this->m_pipeline};
398 promise.feedback_value = visit(handler, promise.current_command);
403 #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
404 for(
auto rb : this->bufReadbacks)
406 this->bufReadbacks.clear();
408 for(
auto rb : this->texReadbacks)
410 this->texReadbacks.clear();
413 parent.processControlOut(this->state);
416 void runInitialPasses(
420 runCompute(renderer, cb, res);
430 template <
typename Node_T>
434 return new GpuComputeRenderer<Node_T>{*
this};
Renderer for a given node.
Definition: NodeRenderer.hpp:11
Definition: OutputNode.hpp:11
List of nodes to be rendered to an output.
Definition: RenderList.hpp:19
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
QShader makeCompute(const RenderState &v, QString compute)
Compile a compute shader.
Definition: score-plugin-gfx/Gfx/Graph/Utils.cpp:358
TextureRenderTarget createRenderTarget(const RenderState &state, QRhiTexture *tex, int samples)
Create a render target from a texture.
Definition: score-plugin-gfx/Gfx/Graph/Utils.cpp:10
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