4 #include <Crousti/Concepts.hpp>
5 #include <Crousti/GpuUtils.hpp>
6 #include <Crousti/Metadatas.hpp>
7 #include <Gfx/Graph/NodeRenderer.hpp>
8 #include <Gfx/Graph/RenderList.hpp>
9 #include <Gfx/Graph/Uniforms.hpp>
16 template <
typename Node_T>
19 using texture_inputs = avnd::texture_input_introspection<Node_T>;
20 using texture_outputs = avnd::texture_output_introspection<Node_T>;
21 const CustomGpuNodeBase& parent;
22 std::vector<Node_T> states;
24 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
27 ossia::time_value m_last_time{-1};
29 score::gfx::PassMap m_p;
31 QRhiBuffer* m_meshBuffer{};
32 QRhiBuffer* m_idxBuffer{};
34 bool m_createdPipeline{};
37 ossia::flat_map<int, QRhiBuffer*> createdUbos;
38 ossia::flat_map<int, QRhiSampler*> createdSamplers;
39 ossia::flat_map<int, QRhiTexture*> createdTexs;
41 CustomGpuRenderer(
const CustomGpuNodeBase& p)
50 auto it = m_rts.find(&p);
51 SCORE_ASSERT(it != m_rts.end());
57 auto port = parent.input[k];
58 static constexpr
auto flags = QRhiTexture::RenderTarget;
59 auto texture = renderer.
state.rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
60 SCORE_ASSERT(texture->create());
69 static constexpr
auto bindingStages = QRhiShaderResourceBinding::VertexStage
70 | QRhiShaderResourceBinding::FragmentStage;
71 if constexpr(requires { F::ubo; })
73 auto it = createdUbos.find(F::binding());
74 QRhiBuffer* buffer = it != createdUbos.end() ? it->second :
nullptr;
75 return QRhiShaderResourceBinding::uniformBuffer(
76 F::binding(), bindingStages, buffer);
78 else if constexpr(requires { F::sampler2D; })
80 auto tex_it = createdTexs.find(F::binding());
82 = tex_it != createdTexs.end() ? tex_it->second : &renderer.
emptyTexture();
85 QRhiSampler* sampler = renderer.
state.rhi->newSampler(
86 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
87 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
89 createdSamplers[F::binding()] = sampler;
91 return QRhiShaderResourceBinding::sampledTexture(
92 F::binding(), bindingStages, tex, sampler);
96 static_assert(F::nope);
103 auto& rhi = *renderer.
state.rhi;
105 auto srb = rhi.newShaderResourceBindings();
108 QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
110 if constexpr(requires { decltype(Node_T::layout::bindings){}; })
112 using bindings_type = decltype(Node_T::layout::bindings);
113 boost::pfr::for_each_field(bindings_type{}, [&](
auto f) {
114 bindings.push_back(initBinding(renderer, f));
117 else if constexpr(requires {
sizeof(
typename Node_T::layout::bindings); })
119 using bindings_type =
typename Node_T::layout::bindings;
120 boost::pfr::for_each_field(bindings_type{}, [&](
auto f) {
121 bindings.push_back(initBinding(renderer, f));
125 srb->setBindings(bindings.begin(), bindings.end());
129 QRhiGraphicsPipeline* createRenderPipeline(
132 auto& rhi = *renderer.
state.rhi;
134 auto ps = rhi.newGraphicsPipeline();
135 ps->setName(
"createRenderPipeline");
137 QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
138 premulAlphaBlend.enable =
true;
139 premulAlphaBlend.srcColor = QRhiGraphicsPipeline::BlendFactor::SrcAlpha;
140 premulAlphaBlend.dstColor = QRhiGraphicsPipeline::BlendFactor::OneMinusSrcAlpha;
141 premulAlphaBlend.srcAlpha = QRhiGraphicsPipeline::BlendFactor::SrcAlpha;
142 premulAlphaBlend.dstAlpha = QRhiGraphicsPipeline::BlendFactor::OneMinusSrcAlpha;
143 ps->setTargetBlends({premulAlphaBlend});
145 ps->setSampleCount(1);
147 mesh.preparePipeline(*ps);
151 ps->setShaderStages({{QRhiShaderStage::Vertex, v}, {QRhiShaderStage::Fragment, f}});
153 SCORE_ASSERT(rt.renderPass);
154 ps->setRenderPassDescriptor(rt.renderPass);
164 template <std::
size_t Idx,
typename F>
165 requires avnd::sampler_port<F>
168 auto tex = createInput(renderer, sampler_k++, renderer.
state.renderSize);
170 using sampler_type =
typename avnd::member_reflection<F::sampler()>::member_type;
171 createdTexs[sampler_type::binding()] = tex;
174 template <std::
size_t Idx,
typename F>
175 requires avnd::uniform_port<F>
178 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
181 if(createdUbos.find(ubo_type::binding()) != createdUbos.end())
184 auto ubo = renderer.
state.rhi->newBuffer(
185 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, gpp::std140_size<ubo_type>());
188 createdUbos[ubo_type::binding()] = ubo;
193 if constexpr(requires { states[0].prepare(); })
195 for(
auto& state : states)
197 parent.processControlIn(
198 *
this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);
207 m_meshBuffer = mbuffer;
208 m_idxBuffer = ibuffer;
212 avnd::input_introspection<Node_T>::for_all(
213 [
this, &renderer](
auto f) { init_input(renderer, f); });
218 auto srb = initBindings(renderer);
226 states.push_back({});
227 prepareNewState(states.back(), parent);
229 auto ps = createRenderPipeline(renderer, rt);
230 ps->setShaderResourceBindings(srb);
235 if constexpr(!requires { &Node_T::update; })
237 SCORE_ASSERT(srb->create());
238 SCORE_ASSERT(ps->create());
239 m_createdPipeline =
true;
245 std::vector<QRhiShaderResourceBinding> tmp;
249 if(states.size() > 0)
251 auto& state = states[0];
253 avnd::gpu_uniform_introspection<Node_T>::for_all(
254 avnd::get_inputs<Node_T>(state), [&]<avnd::uniform_port F>(
const F& t) {
257 typename avnd::member_reflection<F::uniform()>::member_type;
258 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
260 auto ubo = this->createdUbos.at(ubo_type::binding());
262 static constexpr
int offset = gpp::std140_offset<F::uniform()>();
263 static constexpr
int size =
sizeof(uniform_type::value);
264 res.updateDynamicBuffer(ubo, offset, size, &t.value);
268 if constexpr(requires { &Node_T::update; })
273 SCORE_ASSERT(states.size() == m_p.size());
275 for(
int k = 0; k < states.size(); k++)
277 auto& state = states[k];
278 auto& pass = m_p[k].second;
280 bool srb_touched{
false};
281 tmp.assign(pass.srb->cbeginBindings(), pass.srb->cendBindings());
282 for(
auto& promise : state.update())
284 using ret_type = decltype(promise.feedback_value);
285 gpp::qrhi::handle_update<CustomGpuRenderer, 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)
295 pass.srb->setBindings(tmp.begin(), tmp.end());
298 if(!m_createdPipeline)
300 SCORE_ASSERT(pass.srb->create());
301 SCORE_ASSERT(pass.pipeline->create());
304 m_createdPipeline =
true;
311 m_createdPipeline =
false;
314 if constexpr(requires { &Node_T::release; })
316 for(
auto& state : states)
318 for(
auto& promise : state.release())
320 gpp::qrhi::handle_release handler{*r.
state.rhi};
321 visit(handler, promise.current_command);
328 m_meshBuffer =
nullptr;
331 for(
auto& [
id, tex] : this->createdTexs)
333 this->createdTexs.clear();
336 for(
auto& [
id, sampl] : this->createdSamplers)
337 sampl->deleteLater();
338 this->createdSamplers.clear();
341 for(
auto& [
id, ubo] : this->createdUbos)
343 this->createdUbos.clear();
347 for(
auto [port, rt] : m_rts)
352 for(
auto& pass : m_p)
353 pass.second.release();
356 m_meshBuffer =
nullptr;
357 m_createdPipeline =
false;
362 void runInitialPasses(
367 if(parent.last_message.token.date == m_last_time)
371 m_last_time = parent.last_message.token.date;
374 for(
auto& state : states)
376 this->parent.processControlIn(
377 *
this, state, m_last_message, this->parent.last_message, this->parent.m_ctx);
386 score::gfx::defaultRenderPass(
387 renderer, mesh, {m_meshBuffer, m_idxBuffer}, commands, edge, m_p);
390 if(!this->states.empty())
391 parent.processControlOut(this->states[0]);
395 template <
typename Node_T>
396 struct CustomGpuNode final
398 , GpuNodeElements<Node_T>
401 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls,
int id,
403 : CustomGpuNodeBase{std::move(q), std::move(ctls), ctx}
407 using texture_inputs = avnd::gpu_sampler_introspection<Node_T>;
408 using texture_outputs = avnd::gpu_attachment_introspection<Node_T>;
410 for(std::size_t i = 0; i < texture_inputs::size; i++)
412 input.push_back(
new score::gfx::Port{
this, {}, score::gfx::Types::Image, {}});
414 for(std::size_t i = 0; i < texture_outputs::size; i++)
416 output.push_back(
new score::gfx::Port{
this, {}, score::gfx::Types::Image, {}});
419 using layout =
typename Node_T::layout;
420 static constexpr
auto lay = layout{};
422 gpp::qrhi::generate_shaders gen;
423 if constexpr(requires { &Node_T::vertex; })
425 vertex = QString::fromStdString(gen.vertex_shader(lay) + Node_T{}.vertex().data());
429 vertex = gpp::qrhi::DefaultPipeline::vertex();
433 = QString::fromStdString(gen.fragment_shader(lay) + Node_T{}.fragment().data());
439 return new CustomGpuRenderer<Node_T>{*
this};
Renderer for a given node.
Definition: NodeRenderer.hpp:11
List of nodes to be rendered to an output.
Definition: RenderList.hpp:19
MeshBuffers initMeshBuffer(const Mesh &mesh, QRhiResourceUpdateBatch &res)
Create buffers for a mesh and mark them for upload.
Definition: RenderList.cpp:39
const score::gfx::Mesh & defaultTriangle() const noexcept
A triangle mesh correct for this API.
Definition: RenderList.cpp:250
RenderState & state
RenderState corresponding to this RenderList.
Definition: RenderList.hpp:89
TextureRenderTarget renderTargetForOutput(const Edge &edge) noexcept
Obtain the texture corresponding to an output port.
Definition: RenderList.cpp:164
QRhiTexture & emptyTexture() const noexcept
Texture to use when a texture is missing.
Definition: RenderList.hpp:111
Definition: Factories.hpp:19
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
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:342
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:51
Useful abstraction for storing a graphics pipeline and associated resource bindings.
Definition: score-plugin-gfx/Gfx/Graph/Utils.hpp:93
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