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>
16template <
typename Node_T>
19 using texture_inputs = avnd::texture_input_introspection<Node_T>;
20 using texture_outputs = avnd::texture_output_introspection<Node_T>;
21 std::vector<std::shared_ptr<Node_T>> states;
23 ossia::small_flat_map<const score::gfx::Port*, score::gfx::TextureRenderTarget, 2>
26 ossia::time_value m_last_time{-1};
28 score::gfx::PassMap m_p;
32 bool m_createdPipeline{};
35 ossia::flat_map<int, QRhiBuffer*> createdUbos;
36 ossia::flat_map<int, QRhiSampler*> createdSamplers;
37 ossia::flat_map<int, QRhiTexture*> createdTexs;
39 const CustomGpuNodeBase& node() const noexcept
41 return static_cast<const CustomGpuNodeBase&
>(score::gfx::NodeRenderer::node);
44 CustomGpuRenderer(
const CustomGpuNodeBase& p)
52 auto it = m_rts.find(&p);
53 SCORE_ASSERT(it != m_rts.end());
59 auto& parent = node();
60 auto port = parent.input[k];
61 static constexpr auto flags = QRhiTexture::RenderTarget;
62 auto texture = renderer.
state.rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
63 SCORE_ASSERT(texture->create());
72 static constexpr auto bindingStages = QRhiShaderResourceBinding::VertexStage
73 | QRhiShaderResourceBinding::FragmentStage;
74 if constexpr(
requires { F::ubo; })
76 auto it = createdUbos.find(F::binding());
77 QRhiBuffer* buffer = it != createdUbos.end() ? it->second :
nullptr;
78 return QRhiShaderResourceBinding::uniformBuffer(
79 F::binding(), bindingStages, buffer);
81 else if constexpr(
requires { F::sampler2D; })
83 auto tex_it = createdTexs.find(F::binding());
85 = tex_it != createdTexs.end() ? tex_it->second : &renderer.
emptyTexture();
88 QRhiSampler* sampler = renderer.
state.rhi->newSampler(
89 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
90 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
92 createdSamplers[F::binding()] = sampler;
94 return QRhiShaderResourceBinding::sampledTexture(
95 F::binding(), bindingStages, tex, sampler);
99 static_assert(F::nope);
106 auto& rhi = *renderer.
state.rhi;
108 auto srb = rhi.newShaderResourceBindings();
111 QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
113 if constexpr(
requires {
decltype(Node_T::layout::bindings){}; })
115 using bindings_type =
decltype(Node_T::layout::bindings);
116 boost::pfr::for_each_field(bindings_type{}, [&](
auto f) {
117 bindings.push_back(initBinding(renderer, f));
120 else if constexpr(
requires {
sizeof(
typename Node_T::layout::bindings); })
122 using bindings_type =
typename Node_T::layout::bindings;
123 boost::pfr::for_each_field(bindings_type{}, [&](
auto f) {
124 bindings.push_back(initBinding(renderer, f));
128 srb->setBindings(bindings.begin(), bindings.end());
132 QRhiGraphicsPipeline* createRenderPipeline(
135 auto& parent = node();
136 auto& rhi = *renderer.
state.rhi;
138 auto ps = rhi.newGraphicsPipeline();
139 ps->setName(
"createRenderPipeline");
141 QRhiGraphicsPipeline::TargetBlend premulAlphaBlend;
142 premulAlphaBlend.enable =
true;
143 premulAlphaBlend.srcColor = QRhiGraphicsPipeline::BlendFactor::SrcAlpha;
144 premulAlphaBlend.dstColor = QRhiGraphicsPipeline::BlendFactor::OneMinusSrcAlpha;
145 premulAlphaBlend.srcAlpha = QRhiGraphicsPipeline::BlendFactor::SrcAlpha;
146 premulAlphaBlend.dstAlpha = QRhiGraphicsPipeline::BlendFactor::OneMinusSrcAlpha;
147 ps->setTargetBlends({premulAlphaBlend});
149 ps->setSampleCount(1);
151 mesh.preparePipeline(*ps);
155 ps->setShaderStages({{QRhiShaderStage::Vertex, v}, {QRhiShaderStage::Fragment, f}});
157 SCORE_ASSERT(rt.renderPass);
158 ps->setRenderPassDescriptor(rt.renderPass);
168 template <std::
size_t Idx,
typename F>
169 requires avnd::sampler_port<F>
172 auto tex = createInput(renderer, sampler_k++, renderer.
state.renderSize);
174 using sampler_type =
typename avnd::member_reflection<F::sampler()>::member_type;
175 createdTexs[sampler_type::binding()] = tex;
178 template <std::
size_t Idx,
typename F>
179 requires avnd::uniform_port<F>
182 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
185 if(createdUbos.find(ubo_type::binding()) != createdUbos.end())
188 auto ubo = renderer.
state.rhi->newBuffer(
189 QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, gpp::std140_size<ubo_type>());
190 ubo->setName(oscr::getUtf8Name<F>());
193 createdUbos[ubo_type::binding()] = ubo;
198 auto& parent = node();
199 if constexpr(
requires { states[0].prepare(); })
201 for(
auto& state : states)
203 parent.processControlIn(
204 *
this, *state, m_last_message, parent.last_message, parent.m_ctx);
209 if(m_meshBuffer.buffers.empty())
216 avnd::input_introspection<Node_T>::for_all(
217 [
this, &renderer](
auto f) { init_input(renderer, f); });
222 auto srb = initBindings(renderer);
230 states.push_back(std::make_shared<Node_T>());
231 prepareNewState(states.back(), parent);
233 auto ps = createRenderPipeline(renderer, rt);
234 ps->setShaderResourceBindings(srb);
239 if constexpr(!
requires { &Node_T::update; })
241 SCORE_ASSERT(srb->create());
242 SCORE_ASSERT(ps->create());
243 m_createdPipeline =
true;
249 std::vector<QRhiShaderResourceBinding> tmp;
255 if(states.size() > 0)
257 auto& state = *states[0];
259 avnd::gpu_uniform_introspection<Node_T>::for_all(
260 avnd::get_inputs<Node_T>(state), [&]<avnd::uniform_port F>(
const F& t) {
263 typename avnd::member_reflection<F::uniform()>::member_type;
264 using ubo_type =
typename avnd::member_reflection<F::uniform()>::class_type;
266 auto ubo = this->createdUbos.at(ubo_type::binding());
268#define MSVC_BUGGY_STATIC_CONSTEXPR
270#define MSVC_BUGGY_STATIC_CONSTEXPR static constexpr
272 static constexpr int offset = gpp::std140_offset<F::uniform()>();
273 static constexpr int size =
sizeof(uniform_type::value);
274 res.updateDynamicBuffer(ubo, offset, size, &t.value);
278 if constexpr(
requires { &Node_T::update; })
283 SCORE_ASSERT(states.size() == m_p.size());
285 for(
int k = 0; k < states.size(); k++)
287 auto& state = *states[k];
288 auto& pass = m_p[k].second;
290 bool srb_touched{
false};
291 tmp.assign(pass.srb->cbeginBindings(), pass.srb->cendBindings());
292 for(
auto& promise : state.update())
294 using ret_type =
decltype(promise.feedback_value);
296 *
this, *renderer.
state.rhi, res, tmp, srb_touched};
297 promise.feedback_value = visit(handler, promise.current_command);
302 if(m_createdPipeline)
305 pass.srb->setBindings(tmp.begin(), tmp.end());
308 if(!m_createdPipeline)
310 SCORE_ASSERT(pass.srb->create());
311 SCORE_ASSERT(pass.pipeline->create());
314 m_createdPipeline =
true;
321 m_createdPipeline =
false;
324 if constexpr(
requires { &Node_T::release; })
326 for(
auto& state : states)
328 for(
auto& promise : state->release())
331 visit(handler, promise.current_command);
341 for(
auto& [
id, tex] : this->createdTexs)
343 this->createdTexs.clear();
346 for(
auto& [
id, sampl] : this->createdSamplers)
347 sampl->deleteLater();
348 this->createdSamplers.clear();
351 for(
auto& [
id, ubo] : this->createdUbos)
353 this->createdUbos.clear();
357 for(
auto [port, rt] : m_rts)
362 for(
auto& pass : m_p)
363 pass.second.release();
367 m_createdPipeline =
false;
372 void runInitialPasses(
376 auto& parent = node();
378 if(parent.last_message.token.date == m_last_time)
382 m_last_time = parent.last_message.token.date;
385 for(
auto& state : states)
387 parent.processControlIn(
388 *
this, *state, m_last_message, parent.last_message, parent.m_ctx);
396 auto& parent = node();
398 score::gfx::defaultRenderPass(renderer, mesh, m_meshBuffer, commands, edge, m_p);
401 if(!this->states.empty())
402 parent.processControlOut(*this->states[0]);
406template <
typename Node_T>
407struct CustomGpuNode final
409 , GpuNodeElements<Node_T>
412 std::weak_ptr<Execution::ExecutionCommandQueue> q, Gfx::exec_controls ctls,
414 : CustomGpuNodeBase{
std::move(q),
std::move(ctls), ctx}
418 initGfxPorts<Node_T>(
this, this->input, this->output);
420 using layout =
typename Node_T::layout;
421 static constexpr auto lay = layout{};
424 if constexpr(
requires { &Node_T::vertex; })
426 vertex = QString::fromStdString(gen.vertex_shader(lay) + Node_T{}.vertex().data());
430 vertex = gpp::qrhi::DefaultPipeline::vertex();
434 = QString::fromStdString(gen.fragment_shader(lay) + Node_T{}.fragment().data());
440 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
bool requiresDepth(score::gfx::Port &p) const noexcept
Whether this list of rendering actions requires depth testing at all.
Definition RenderList.cpp:343
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:382
TextureRenderTarget renderTargetForOutput(const Edge &edge) const noexcept
Obtain the texture corresponding to an output port.
Definition RenderList.cpp:198
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:94
QRhiTexture & emptyTexture() const noexcept
Texture to use when a texture is missing.
Definition RenderList.hpp:117
Definition Factories.hpp:19
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:394
TextureRenderTarget createRenderTarget(const RenderState &state, QRhiTexture *tex, int samples, bool depth)
Create a render target from a texture.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:10
Definition GppShaders.hpp:10
Definition GppCoroutines.hpp:92
Definition GppCoroutines.hpp:253
Definition DocumentContext.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:71
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:49
Useful abstraction for storing a graphics pipeline and associated resource bindings.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:98
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:53
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:116