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>
18template <
typename Node_T>
19using ComputeNodeBaseType = std::conditional_t<
20 avnd::gpu_image_output_introspection<Node_T>::size != 0, CustomGpuNodeBase,
21 CustomGpuOutputNodeBase>;
22template <
typename Node_T>
23using ComputeRendererBaseType = std::conditional_t<
27template <
typename Node_T>
28struct GpuComputeNode final : ComputeNodeBaseType<Node_T>
31 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls,
int id,
33 : ComputeNodeBaseType<Node_T>{std::move(q), std::move(ctls), ctx}
37 initGfxPorts<Node_T>(
this, this->input, this->output);
38 using layout =
typename Node_T::layout;
39 static constexpr auto lay = layout{};
41 gpp::qrhi::generate_shaders gen;
43 = QString::fromStdString(gen.compute_shader(lay) + Node_T{}.compute().data());
50template <
typename Node_T>
51struct GpuComputeRenderer final : ComputeRendererBaseType<Node_T>
53 using texture_inputs = avnd::gpu_image_input_introspection<Node_T>;
54 using texture_outputs = avnd::gpu_image_output_introspection<Node_T>;
57 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
60 ossia::time_value m_last_time{-1};
62 QRhiShaderResourceBindings* m_srb{};
63 QRhiComputePipeline* m_pipeline{};
65 bool m_createdPipeline{};
69 ossia::flat_map<int, QRhiBuffer*> createdUbos;
70 ossia::flat_map<int, QRhiTexture*> createdTexs;
72#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
73 std::vector<QRhiBufferReadbackResult*> bufReadbacks;
74 void addReadback(QRhiBufferReadbackResult* r) { bufReadbacks.push_back(r); }
77 std::vector<QRhiReadbackResult*> texReadbacks;
78 void addReadback(QRhiReadbackResult* r) { texReadbacks.push_back(r); }
80 const GpuComputeNode<Node_T>& node() const noexcept
82 return static_cast<const GpuComputeNode<Node_T>&
>(score::gfx::NodeRenderer::node);
85 GpuComputeRenderer(
const GpuComputeNode<Node_T>& p)
86 : ComputeRendererBaseType<Node_T>{p}
88 prepareNewState(state, p);
94 auto it = m_rts.find(&p);
95 SCORE_ASSERT(it != m_rts.end());
99 QRhiTexture* createInput(
102 auto& parent = node();
103 auto port = parent.input[k];
104 static constexpr auto flags
105 = QRhiTexture::RenderTarget | QRhiTexture::UsedWithLoadStore;
106 auto texture = renderer.
state.rhi->newTexture(fmt, size, 1, flags);
107 SCORE_ASSERT(texture->create());
113 template <
typename F>
116 static constexpr auto bindingStages = QRhiShaderResourceBinding::ComputeStage;
117 if constexpr(
requires { F::ubo; })
119 auto it = createdUbos.find(F::binding());
120 QRhiBuffer* buffer = it != createdUbos.end() ? it->second :
nullptr;
121 return QRhiShaderResourceBinding::uniformBuffer(
122 F::binding(), bindingStages, buffer);
124 else if constexpr(
requires { F::image2D; })
126 auto tex_it = createdTexs.find(F::binding());
128 = tex_it != createdTexs.end() ? tex_it->second : &renderer.
emptyTexture();
131 requires { F::load; } &&
requires { F::store; })
132 return QRhiShaderResourceBinding::imageLoadStore(
133 F::binding(), bindingStages, tex, 0);
134 else if constexpr(
requires { F::readonly; })
135 return QRhiShaderResourceBinding::imageLoad(F::binding(), bindingStages, tex, 0);
136 else if constexpr(
requires { F::writeonly; })
137 return QRhiShaderResourceBinding::imageStore(
138 F::binding(), bindingStages, tex, 0);
140 static_assert(F::load || F::store);
142 else if constexpr(
requires { F::buffer; })
144 auto it = createdUbos.find(F::binding());
145 QRhiBuffer* buf = it != createdUbos.end() ? it->second :
nullptr;
148 requires { F::load; } &&
requires { F::store; })
149 return QRhiShaderResourceBinding::bufferLoadStore(
150 F::binding(), bindingStages, buf);
151 else if constexpr(
requires { F::load; })
152 return QRhiShaderResourceBinding::bufferLoad(F::binding(), bindingStages, buf);
153 else if constexpr(
requires { F::store; })
154 return QRhiShaderResourceBinding::bufferStore(F::binding(), bindingStages, buf);
156 static_assert(F::load || F::store);
160 static_assert(F::nope);
167 auto& rhi = *renderer.
state.rhi;
169 auto srb = rhi.newShaderResourceBindings();
172 QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
174 using bindings_type =
decltype(Node_T::layout::bindings);
175 boost::pfr::for_each_field(
176 bindings_type{}, [&](
auto f) { bindings.push_back(initBinding(renderer, f)); });
178 srb->setBindings(bindings.begin(), bindings.end());
184 auto& parent = node();
185 auto& rhi = *renderer.
state.rhi;
186 auto compute = rhi.newComputePipeline();
188 compute->setShaderStage(QRhiShaderStage(QRhiShaderStage::Compute, cs));
198 template <std::
size_t Idx,
typename F>
199 requires avnd::image_port<F>
202 using bindings_type =
decltype(Node_T::layout::bindings);
203 using image_type = std::decay_t<
decltype(bindings_type{}.*F::image())>;
204 auto tex = createInput(
205 renderer, sampler_k++, gpp::qrhi::textureFormat<image_type>(),
206 renderer.
state.renderSize);
208 using sampler_type =
typename avnd::member_reflection<F::image()>::member_type;
209 createdTexs[sampler_type::binding()] = tex;
212 template <std::
size_t Idx,
typename F>
213 requires avnd::uniform_port<F>
216 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
219 if(createdUbos.find(ubo_type::binding()) != createdUbos.end())
222 auto ubo = renderer.
state.rhi->newBuffer(
223 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, gpp::std140_size<ubo_type>());
226 createdUbos[ubo_type::binding()] = ubo;
231 auto& parent = node();
232 if constexpr(
requires { state.prepare(); })
234 parent.processControlIn(
235 *
this, state, m_last_message, parent.last_message, parent.m_ctx);
240 avnd::input_introspection<Node_T>::for_all(
241 [
this, &renderer](
auto f) { init_input(renderer, f); });
243 m_srb = initBindings(renderer);
244 m_pipeline = createComputePipeline(renderer);
245 m_pipeline->setShaderResourceBindings(m_srb);
248 if constexpr(!
requires { &Node_T::update; })
250 SCORE_ASSERT(m_srb->create());
251 SCORE_ASSERT(m_pipeline->create());
252 m_createdPipeline =
true;
256 std::vector<QRhiShaderResourceBinding> tmp;
262 avnd::gpu_uniform_introspection<Node_T>::for_all(
263 avnd::get_inputs<Node_T>(state), [&]<avnd::uniform_port F>(
const F& t) {
265 typename avnd::member_reflection<F::uniform()>::member_type;
266 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
268 auto ubo = this->createdUbos.at(ubo_type::binding());
270 static constexpr int offset = gpp::std140_offset<F::uniform()>();
271 static constexpr int size =
sizeof(uniform_type::value);
272 res.updateDynamicBuffer(ubo, offset, size, &t.value);
279 if constexpr(
requires { &Node_T::update; })
281 bool srb_touched{
false};
282 tmp.assign(m_srb->cbeginBindings(), m_srb->cendBindings());
283 for(
auto& promise : state.update())
285 using ret_type =
decltype(promise.feedback_value);
286 gpp::qrhi::handle_update<GpuComputeRenderer, ret_type> handler{
287 *
this, *renderer.
state.rhi, res, tmp, srb_touched};
288 promise.feedback_value = visit(handler, promise.current_command);
293 if(m_createdPipeline)
295 m_srb->setBindings(tmp.begin(), tmp.end());
322 if(!m_createdPipeline)
324 SCORE_ASSERT(m_srb->create());
325 SCORE_ASSERT(m_pipeline->create());
326 m_createdPipeline =
true;
334 m_createdPipeline =
false;
337 if constexpr(
requires { &Node_T::release; })
339 for(
auto& promise : state.release())
341 gpp::qrhi::handle_release handler{*r.
state.rhi};
342 visit(handler, promise.current_command);
348 for(
auto& [
id, tex] : this->createdTexs)
350 this->createdTexs.clear();
353 for(
auto& [
id, ubo] : this->createdUbos)
355 this->createdUbos.clear();
365 m_srb->deleteLater();
367 m_pipeline->deleteLater();
369 m_pipeline =
nullptr;
371 m_createdPipeline =
false;
379 QRhiResourceUpdateBatch*& res)
381 auto& parent = node();
389 parent.processControlIn(
390 *
this, this->state, m_last_message, parent.last_message, parent.m_ctx);
394 SCORE_ASSERT(this->m_pipeline);
395 SCORE_ASSERT(this->m_pipeline->shaderResourceBindings());
396 for(
auto& promise : this->state.dispatch())
398 using ret_type =
decltype(promise.feedback_value);
399 gpp::qrhi::handle_dispatch<GpuComputeRenderer, ret_type> handler{
400 *
this, *renderer.
state.rhi, cb, res, *this->m_pipeline};
401 promise.feedback_value = visit(handler, promise.current_command);
406#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
407 for(
auto rb : this->bufReadbacks)
409 this->bufReadbacks.clear();
411 for(
auto rb : this->texReadbacks)
413 this->texReadbacks.clear();
416 parent.processControlOut(this->state);
419 void runInitialPasses(
423 runCompute(renderer, cb, res);
433template <
typename Node_T>
437 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:112
Definition Factories.hpp:19
QShader makeCompute(const RenderState &v, QString compute)
Compile a compute shader.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:399
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
Definition DocumentContext.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:66
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:50
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:48
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:111