Loading...
Searching...
No Matches
NodeRenderer.hpp
1#pragma once
2#include <Gfx/Graph/Node.hpp>
3
4namespace score::gfx
5{
6
10class SCORE_PLUGIN_GFX_EXPORT NodeRenderer
11{
12public:
13 explicit NodeRenderer(const Node& node)
14 : node{node}
15 {
16 }
17 virtual ~NodeRenderer();
18
19 virtual TextureRenderTarget renderTargetForInput(const Port& input);
20 virtual BufferView bufferForInput(const Port& input);
21 virtual BufferView bufferForOutput(const Port& output);
22
27 virtual QRhiTexture* textureForOutput(const Port& output);
28
33 virtual void updateInputTexture(const Port& input, QRhiTexture* tex, QRhiTexture* depthTex = nullptr);
34
37 virtual void updateInputSamplerFilter(
38 const Port& input, const RenderTargetSpecs& spec);
39
42 virtual void
43 inputAboutToFinish(RenderList& renderer, const Port& p, QRhiResourceUpdateBatch*&);
44
45 virtual void init(RenderList& renderer, QRhiResourceUpdateBatch& res) = 0;
46 virtual void update(RenderList& renderer, QRhiResourceUpdateBatch& res, Edge* edge)
47 = 0;
48
49 virtual void runInitialPasses(
50 RenderList&, QRhiCommandBuffer& commands, QRhiResourceUpdateBatch*& res,
51 Edge& edge);
52
53 virtual void runRenderPass(RenderList&, QRhiCommandBuffer& commands, Edge& edge);
54
55 virtual void release(RenderList&) = 0;
56
73 virtual void initState(RenderList& renderer, QRhiResourceUpdateBatch& res);
74
77 virtual void releaseState(RenderList& renderer);
78
80 virtual void addOutputPass(
81 RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res);
82
87 virtual void removeOutputPass(RenderList& renderer, Edge& edge) = 0;
88
91 virtual void
92 addInputEdge(RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res);
93
95 virtual void removeInputEdge(RenderList& renderer, Edge& edge);
96
98 virtual bool hasOutputPassForEdge(Edge& edge) const;
99
108 virtual void seedInitialOutputs(RenderList& renderer);
109
112 void checkForChanges()
113 {
114 // Use |= to preserve flags set externally (e.g. by reconciliation
115 // or maybeRebuild). The flag is cleared by the renderer's update()
116 // after processing, preventing infinite re-uploads.
117 materialChanged |= node.hasMaterialChanged(materialChangedIndex);
118 renderTargetSpecsChanged |= node.hasRenderTargetChanged(renderTargetSpecsChangedIndex);
119 }
120
125 {
126 node.hasRenderTargetChanged(renderTargetSpecsChangedIndex);
127 renderTargetSpecsChanged = false;
128 }
129
130 void process(int32_t port, const ossia::geometry_spec& v);
131 void process(int32_t port, const ossia::scene_spec& v);
132 virtual void process(int32_t port, const ossia::transform3d& v);
133
140 void process(int32_t port, const ossia::geometry_spec& v, const void* source_key);
141 void process(int32_t port, const ossia::scene_spec& v, const void* source_key);
142
146 const ossia::geometry_spec* findGeometryByPort(int32_t port) const
147 {
148 for(const auto& [k, v] : m_portGeometries)
149 if(k.first == port)
150 return &v;
151 return nullptr;
152 }
153
160 template <typename F>
161 void forEachSceneOnPort(int32_t port, F&& fn) const
162 {
163 for(const auto& [k, v] : m_portScenes)
164 if(k.first == port && v.state)
165 fn(v);
166 }
167
168private:
171 void rebuildMergedScene();
172
173public:
174
175 const Node& node;
176
187 ossia::geometry_spec geometry;
188
194 using PortSourceKey = std::pair<int32_t, const void*>;
195 ossia::small_flat_map<PortSourceKey, ossia::geometry_spec, 4> m_portGeometries;
196
204 ossia::scene_spec scene;
205
207 ossia::small_flat_map<PortSourceKey, ossia::scene_spec, 4> m_portScenes;
208
217 using MergeCacheKey = std::pair<const ossia::scene_state*, int64_t>;
218 ossia::small_vector<MergeCacheKey, 4> m_mergeCacheInputs;
219 ossia::scene_spec m_mergeCacheOutput;
220
225 ossia::small_flat_map<
226 PortSourceKey, std::pair<ossia::geometry_spec, ossia::scene_spec>, 4>
228
229 int32_t nodeId{-1};
230 bool materialChanged{false};
231 bool geometryChanged{false};
232 bool sceneChanged{false};
233 bool renderTargetSpecsChanged{false};
234
237 bool m_initialized{false};
238
239private:
240 int64_t materialChangedIndex{-1};
241 int64_t renderTargetSpecsChangedIndex{-1};
242};
243
244struct Pass
245{
246 // User-declared ctors (including the implicit ones spelled out here)
247 // suppress -Wmissing-field-initializers on the many call sites that
248 // brace-init this struct with three arguments: the fallback plan is
249 // then default-constructed into an empty list, which is what
250 // non-fallback pipelines need. This costs aggregate initialization --
251 // call sites that want fallback_bindings assign it afterwards.
252 Pass() = default;
253 Pass(TextureRenderTarget rt, Pipeline pi, QRhiBuffer* ubo)
254 : renderTarget{std::move(rt)}, p{pi}, processUBO{ubo} {}
255 // Compat for addons that build a pass from a bare Pipeline; such passes
256 // fetch their render target through renderer.renderTargetForOutput(edge)
257 // at draw time.
258 Pass(Pipeline pi)
259 : p{pi} {}
260
261 TextureRenderTarget renderTarget;
262 Pipeline p;
263 QRhiBuffer* processUBO{};
264 // Bindings for "REQUIRED: false" VERTEX_INPUTS that had no matching
265 // upstream attribute when this pass's pipeline was built. Empty for
266 // pipelines where the shader is strict-matched (the common case).
267 // Consumed by the draw path: each slot's buffer is bound at its
268 // `binding_index` in the vertex-input array before the draw call.
269 // The buffers themselves are owned by VertexFallbackPool — the plan
270 // holds non-owning pointers.
271 FallbackBindingPlan fallback_bindings;
272
273 void release()
274 {
275 p.release();
276 if(processUBO)
277 {
278 processUBO->deleteLater();
279 processUBO = nullptr;
280 }
281 fallback_bindings.clear();
282 // renderTarget NOT released here — owned by RenderList
283 }
284};
285
286using PassMap = ossia::small_vector<std::pair<Edge*, Pass>, 2>;
287SCORE_PLUGIN_GFX_EXPORT
288void defaultPassesInit(
289 PassMap& passes, const std::vector<Edge*>& edges, RenderList& renderer,
290 const Mesh& mesh, const QShader& v, const QShader& f, QRhiBuffer* processUBO,
291 QRhiBuffer* matUBO, std::span<const Sampler> samplers,
292 std::span<QRhiShaderResourceBinding> additionalBindings = {});
293
294SCORE_PLUGIN_GFX_EXPORT
295void defaultRenderPass(
296 RenderList& renderer, const Mesh& mesh, const MeshBuffers& bufs,
297 QRhiCommandBuffer& cb, Edge& edge, PassMap& passes);
298
299SCORE_PLUGIN_GFX_EXPORT
300void quadRenderPass(
301 RenderList& renderer, const MeshBuffers& bufs, QRhiCommandBuffer& cb, Edge& edge,
302 PassMap& passes);
303
310class SCORE_PLUGIN_GFX_EXPORT GenericNodeRenderer : public score::gfx::NodeRenderer
311{
312public:
313 GenericNodeRenderer(const NodeModel& node) noexcept
314 : NodeRenderer{node}
315 {
316 }
317
318 virtual ~GenericNodeRenderer() { }
319
321 score::gfx::Port* imageOutlet() const noexcept;
322
323 ossia::small_vector<Sampler, 8> m_samplers;
324
325 QShader m_vertexS;
326 QShader m_fragmentS;
327
328 // Pipeline
329 PassMap m_p;
330
331 // Per-renderer pipeline cache, keyed by the rp-desc's serializedFormat().
332 // QRhi guarantees a pipeline can be used with any render target whose
333 // rp-desc isCompatible with the pipeline's, and serializedFormat() is
334 // documented as the in-memory comparison key for exactly that relation —
335 // identical blobs ⇔ isCompatible. Keying by pointer instead would be
336 // ABA-unsafe: a freshly allocated rp-desc can reuse the address of one
337 // just destroyed, silently serving a pipeline built for a dead layout.
338 //
339 // Ownership: Pass::p.pipeline is NON-OWNING — the actual QRhiGraphicsPipeline
340 // lives in this cache. Pass::p.srb is still per-edge and owned by the Pass.
341 // GenericNodeRenderer::removeOutputPass and releaseState take care of
342 // nulling Pass::p.pipeline before calling Pipeline::release() so it
343 // does not try to deleteLater() a pointer we still own here.
344 ossia::small_vector<
345 std::pair<QVector<quint32>, QRhiGraphicsPipeline*>, 2>
346 m_pipelineCache;
347
348 MeshBuffers m_meshbufs;
349
350 QRhiBuffer* m_processUBO{};
351
352 DefaultShaderMaterial m_material;
353
354 const score::gfx::Mesh* m_mesh{};
355
356 // Render loop
357 void
358 defaultMeshInit(RenderList& renderer, const Mesh& mesh, QRhiResourceUpdateBatch& res);
359 void processUBOInit(RenderList& renderer);
360 void defaultPassesInit(RenderList& renderer, const Mesh& mesh);
361 void defaultPassesInit(
362 RenderList& renderer, const Mesh& mesh, const QShader& v, const QShader& f,
363 std::span<QRhiShaderResourceBinding> additionalBindings = {});
364
365 void init(RenderList& renderer, QRhiResourceUpdateBatch& res) override;
366
367 void initState(RenderList& renderer, QRhiResourceUpdateBatch& res) override;
368 void releaseState(RenderList& renderer) override;
369 void addOutputPass(
370 RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res) override;
371 void removeOutputPass(RenderList& renderer, Edge& edge) override;
372 bool hasOutputPassForEdge(Edge& edge) const override;
373
374 void defaultUBOUpdate(RenderList& renderer, QRhiResourceUpdateBatch& res);
375 void defaultMeshUpdate(RenderList& renderer, QRhiResourceUpdateBatch& res);
376 void update(RenderList& renderer, QRhiResourceUpdateBatch& res, Edge* edge) override;
377
378 void defaultRelease(RenderList&);
379 void release(RenderList&) override;
380
381 void defaultRenderPass(
382 RenderList&, const Mesh& mesh, QRhiCommandBuffer& commands, Edge& edge);
383
384 void defaultRenderPass(
385 RenderList&, const Mesh& mesh, QRhiCommandBuffer& commands, Edge& edge,
386 PassMap& passes);
387
388 void runRenderPass(RenderList&, QRhiCommandBuffer& commands, Edge& edge) override;
389
390 void updateInputTexture(const Port& input, QRhiTexture* tex, QRhiTexture* depthTex = nullptr) override;
391};
392
393}
Generic renderer.
Definition NodeRenderer.hpp:311
Root data model for visual nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:74
Common base class for most single-pass, simple nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:215
Renderer for a given node.
Definition NodeRenderer.hpp:11
virtual void removeOutputPass(RenderList &renderer, Edge &edge)=0
std::pair< const ossia::scene_state *, int64_t > MergeCacheKey
Definition NodeRenderer.hpp:217
ossia::small_flat_map< PortSourceKey, ossia::scene_spec, 4 > m_portScenes
Per-(port, source) scene storage. See m_portGeometries comment.
Definition NodeRenderer.hpp:207
ossia::scene_spec scene
The scene to use (when receiving scene_spec data).
Definition NodeRenderer.hpp:204
ossia::geometry_spec geometry
The geometry to use.
Definition NodeRenderer.hpp:187
std::pair< int32_t, const void * > PortSourceKey
Definition NodeRenderer.hpp:194
ossia::small_flat_map< PortSourceKey, std::pair< ossia::geometry_spec, ossia::scene_spec >, 4 > m_wrapCache
Definition NodeRenderer.hpp:227
void syncRenderTargetIndex()
Definition NodeRenderer.hpp:124
void forEachSceneOnPort(int32_t port, F &&fn) const
Definition NodeRenderer.hpp:161
const ossia::geometry_spec * findGeometryByPort(int32_t port) const
Definition NodeRenderer.hpp:146
List of nodes to be rendered to an output.
Definition RenderList.hpp:30
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition Mesh.hpp:18
Utility to represent a shader material following score conventions.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:673
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:103
Definition VertexFallbackPlan.hpp:27
Definition Mesh.hpp:94
Data model for meshes.
Definition Mesh.hpp:179
Definition NodeRenderer.hpp:245
Useful abstraction for storing a graphics pipeline and associated resource bindings.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:132
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:82
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:56
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:152