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
56 virtual QRhiGraphicsPipeline::CompareOp depthCompare() const noexcept
57 {
58 return QRhiGraphicsPipeline::Greater;
59 }
60
61 virtual void release(RenderList&) = 0;
62
79 virtual void initState(RenderList& renderer, QRhiResourceUpdateBatch& res);
80
83 virtual void releaseState(RenderList& renderer);
84
86 virtual void addOutputPass(
87 RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res);
88
93 virtual void removeOutputPass(RenderList& renderer, Edge& edge) = 0;
94
97 virtual void
98 addInputEdge(RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res);
99
101 virtual void removeInputEdge(RenderList& renderer, Edge& edge);
102
104 virtual bool hasOutputPassForEdge(Edge& edge) const;
105
114 virtual void seedInitialOutputs(RenderList& renderer);
115
119 virtual bool followsRenderSize() const noexcept { return false; }
120
123 virtual const Mesh* currentMesh() const noexcept { return nullptr; }
124
127 void checkForChanges()
128 {
129 // Use |= to preserve flags set externally (e.g. by reconciliation
130 // or maybeRebuild). The flag is cleared by the renderer's update()
131 // after processing, preventing infinite re-uploads.
132 materialChanged |= node.hasMaterialChanged(materialChangedIndex);
133 renderTargetSpecsChanged |= node.hasRenderTargetChanged(renderTargetSpecsChangedIndex);
134 }
135
140 {
141 node.hasRenderTargetChanged(renderTargetSpecsChangedIndex);
142 renderTargetSpecsChanged = false;
143 }
144
145 void process(int32_t port, const ossia::geometry_spec& v);
146 void process(int32_t port, const ossia::scene_spec& v);
147 virtual void process(int32_t port, const ossia::transform3d& v);
148
155 void process(int32_t port, const ossia::geometry_spec& v, const void* source_key);
156 void process(int32_t port, const ossia::scene_spec& v, const void* source_key);
157
161 const ossia::geometry_spec* findGeometryByPort(int32_t port) const
162 {
163 for(const auto& [k, v] : m_portGeometries)
164 if(k.first == port)
165 return &v;
166 return nullptr;
167 }
168
175 template <typename F>
176 void forEachSceneOnPort(int32_t port, F&& fn) const
177 {
178 for(const auto* kv : portScenesInEdgeOrder())
179 if(kv->first.first == port && kv->second.state)
180 fn(kv->second);
181 }
182
183private:
186 void rebuildMergedScene();
187
193 ossia::small_vector<const std::pair<std::pair<int32_t, const void*>, ossia::scene_spec>*, 4>
194 portScenesInEdgeOrder() const;
195
196public:
197
198 const Node& node;
199
210 ossia::geometry_spec geometry;
211
217 using PortSourceKey = std::pair<int32_t, const void*>;
218 ossia::small_flat_map<PortSourceKey, ossia::geometry_spec, 4> m_portGeometries;
219
227 ossia::scene_spec scene;
228
230 ossia::small_flat_map<PortSourceKey, ossia::scene_spec, 4> m_portScenes;
231
240 using MergeCacheKey = std::pair<const ossia::scene_state*, int64_t>;
241 ossia::small_vector<MergeCacheKey, 4> m_mergeCacheInputs;
242 ossia::scene_spec m_mergeCacheOutput;
243
248 ossia::small_flat_map<
249 PortSourceKey, std::pair<ossia::geometry_spec, ossia::scene_spec>, 4>
251
252 int32_t nodeId{-1};
253 bool materialChanged{false};
254 bool geometryChanged{false};
255 bool sceneChanged{false};
256 bool renderTargetSpecsChanged{false};
257
260 bool m_initialized{false};
261
262private:
263 int64_t materialChangedIndex{-1};
264 int64_t renderTargetSpecsChangedIndex{-1};
265};
266
267struct Pass
268{
269 // User-declared ctors (including the implicit ones spelled out here)
270 // suppress -Wmissing-field-initializers on the many call sites that
271 // brace-init this struct with three arguments.
272 Pass() = default;
273 Pass(TextureRenderTarget rt, Pipeline pi, QRhiBuffer* ubo)
274 : renderTarget{std::move(rt)}, p{pi}, processUBO{ubo} {}
275 // Compat for addons that build a pass from a bare Pipeline; such passes
276 // fetch their render target through renderer.renderTargetForOutput(edge)
277 // at draw time.
278 Pass(Pipeline pi)
279 : p{pi} {}
280
281 TextureRenderTarget renderTarget;
282 Pipeline p;
283 QRhiBuffer* processUBO{};
284
285 void release()
286 {
287 p.release();
288 if(processUBO)
289 {
290 processUBO->deleteLater();
291 processUBO = nullptr;
292 }
293 // renderTarget NOT released here — owned by RenderList
294 }
295};
296
297using PassMap = ossia::small_vector<std::pair<Edge*, Pass>, 2>;
298SCORE_PLUGIN_GFX_EXPORT
299void defaultPassesInit(
300 PassMap& passes, const std::vector<Edge*>& edges, RenderList& renderer,
301 const Mesh& mesh, const QShader& v, const QShader& f, QRhiBuffer* processUBO,
302 QRhiBuffer* matUBO, std::span<const Sampler> samplers,
303 std::span<QRhiShaderResourceBinding> additionalBindings = {});
304
305SCORE_PLUGIN_GFX_EXPORT
306void defaultPassesInit(
307 PassMap& passes, const std::vector<Edge*>& edges, RenderList& renderer,
308 const Mesh& mesh, const QShader& v, const QShader& f, QRhiBuffer* processUBO,
309 QRhiBuffer* matUBO, std::span<const Sampler> samplers,
310 const QRhiGraphicsPipeline::TargetBlend& blend,
311 std::span<QRhiShaderResourceBinding> additionalBindings = {});
312
313SCORE_PLUGIN_GFX_EXPORT
314void defaultRenderPass(
315 RenderList& renderer, const Mesh& mesh, const MeshBuffers& bufs,
316 QRhiCommandBuffer& cb, Edge& edge, PassMap& passes);
317
320SCORE_PLUGIN_GFX_EXPORT
322 const Mesh& mesh, const MeshBuffers& bufs, QRhiCommandBuffer& cb,
323 const FallbackBindingPlan& plan) noexcept;
324
325SCORE_PLUGIN_GFX_EXPORT
326void quadRenderPass(
327 RenderList& renderer, const MeshBuffers& bufs, QRhiCommandBuffer& cb, Edge& edge,
328 PassMap& passes);
329
336class SCORE_PLUGIN_GFX_EXPORT GenericNodeRenderer : public score::gfx::NodeRenderer
337{
338public:
339 GenericNodeRenderer(const NodeModel& node) noexcept
340 : NodeRenderer{node}
341 {
342 }
343
344 virtual ~GenericNodeRenderer() { }
345
347 score::gfx::Port* imageOutlet() const noexcept;
348
354 virtual std::span<const Sampler> samplersForOutputEdge(const Edge& edge) const noexcept
355 {
356 return m_samplers;
357 }
358
359 ossia::small_vector<Sampler, 8> m_samplers;
360
361 QShader m_vertexS;
362 QShader m_fragmentS;
363
368 bool m_outputPremultiplied{false};
369
370 // Pipeline
371 PassMap m_p;
372
373 // Per-renderer pipeline cache, keyed by the rp-desc's serializedFormat().
374 // QRhi guarantees a pipeline can be used with any render target whose
375 // rp-desc isCompatible with the pipeline's, and serializedFormat() is
376 // documented as the in-memory comparison key for exactly that relation —
377 // identical blobs ⇔ isCompatible. Keying by pointer instead would be
378 // ABA-unsafe: a freshly allocated rp-desc can reuse the address of one
379 // just destroyed, silently serving a pipeline built for a dead layout.
380 //
381 // Ownership: Pass::p.pipeline is NON-OWNING — the actual QRhiGraphicsPipeline
382 // lives in this cache. Pass::p.srb is still per-edge and owned by the Pass.
383 // GenericNodeRenderer::removeOutputPass and releaseState take care of
384 // nulling Pass::p.pipeline before calling Pipeline::release() so it
385 // does not try to deleteLater() a pointer we still own here.
386 ossia::small_vector<
387 std::pair<QVector<quint32>, QRhiGraphicsPipeline*>, 2>
388 m_pipelineCache;
389
390 // The vertex-binding plan every cached pipeline was built with. The cache
391 // stores bare pipelines, so without this a hit would hand the Pass an empty
392 // plan and the draw would bind the geometry's streams one-to-one against a
393 // layout that was compacted.
394 FallbackBindingPlan m_pipelinePlan;
395
396 MeshBuffers m_meshbufs;
397
398 QRhiBuffer* m_processUBO{};
399
400 DefaultShaderMaterial m_material;
401
402 const score::gfx::Mesh* m_mesh{};
404 const score::gfx::Mesh* m_initMesh{};
405 const Mesh* currentMesh() const noexcept override { return m_mesh; }
406
407 // Render loop
408 void
409 defaultMeshInit(RenderList& renderer, const Mesh& mesh, QRhiResourceUpdateBatch& res);
410 void processUBOInit(RenderList& renderer);
411 void defaultPassesInit(RenderList& renderer, const Mesh& mesh);
412 void defaultPassesInit(
413 RenderList& renderer, const Mesh& mesh, const QShader& v, const QShader& f,
414 std::span<QRhiShaderResourceBinding> additionalBindings = {});
415
416 void init(RenderList& renderer, QRhiResourceUpdateBatch& res) override;
417
418 void initState(RenderList& renderer, QRhiResourceUpdateBatch& res) override;
419 void releaseState(RenderList& renderer) override;
420 void addOutputPass(
421 RenderList& renderer, Edge& edge, QRhiResourceUpdateBatch& res) override;
422 void removeOutputPass(RenderList& renderer, Edge& edge) override;
423 bool hasOutputPassForEdge(Edge& edge) const override;
424
425 void defaultUBOUpdate(RenderList& renderer, QRhiResourceUpdateBatch& res);
426 void defaultMeshUpdate(RenderList& renderer, QRhiResourceUpdateBatch& res);
427 void update(RenderList& renderer, QRhiResourceUpdateBatch& res, Edge* edge) override;
428
429 void defaultRelease(RenderList&);
430 void release(RenderList&) override;
431
432 void defaultRenderPass(
433 RenderList&, const Mesh& mesh, QRhiCommandBuffer& commands, Edge& edge);
434
435 void defaultRenderPass(
436 RenderList&, const Mesh& mesh, QRhiCommandBuffer& commands, Edge& edge,
437 PassMap& passes);
438
439 void runRenderPass(RenderList&, QRhiCommandBuffer& commands, Edge& edge) override;
440
441 void updateInputTexture(const Port& input, QRhiTexture* tex, QRhiTexture* depthTex = nullptr) override;
442};
443
444}
Generic renderer.
Definition NodeRenderer.hpp:337
virtual std::span< const Sampler > samplersForOutputEdge(const Edge &edge) const noexcept
Definition NodeRenderer.hpp:354
const Mesh * currentMesh() const noexcept override
Definition NodeRenderer.hpp:405
Root data model for visual nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:111
Common base class for most single-pass, simple nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:325
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:240
ossia::small_flat_map< PortSourceKey, ossia::scene_spec, 4 > m_portScenes
Per-(port, source) scene storage. See m_portGeometries comment.
Definition NodeRenderer.hpp:230
ossia::scene_spec scene
The scene to use (when receiving scene_spec data).
Definition NodeRenderer.hpp:227
ossia::geometry_spec geometry
The geometry to use.
Definition NodeRenderer.hpp:210
std::pair< int32_t, const void * > PortSourceKey
Definition NodeRenderer.hpp:217
virtual const Mesh * currentMesh() const noexcept
Definition NodeRenderer.hpp:123
ossia::small_flat_map< PortSourceKey, std::pair< ossia::geometry_spec, ossia::scene_spec >, 4 > m_wrapCache
Definition NodeRenderer.hpp:250
void syncRenderTargetIndex()
Definition NodeRenderer.hpp:139
virtual bool followsRenderSize() const noexcept
Definition NodeRenderer.hpp:119
virtual QRhiGraphicsPipeline::CompareOp depthCompare() const noexcept
The depth compare this renderer's pipeline was built with.
Definition NodeRenderer.hpp:56
void forEachSceneOnPort(int32_t port, F &&fn) const
Definition NodeRenderer.hpp:176
const ossia::geometry_spec * findGeometryByPort(int32_t port) const
Definition NodeRenderer.hpp:161
List of nodes to be rendered to an output.
Definition RenderList.hpp:36
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
void drawMeshWithPlan(const Mesh &mesh, const MeshBuffers &bufs, QRhiCommandBuffer &cb, const FallbackBindingPlan &plan) noexcept
Definition NodeRenderer.cpp:38
void updateInputSamplerFilter(std::span< Sampler > samplers, const score::gfx::Node &node, const Port &input, const RenderTargetSpecs &spec)
Give the sampler initInputSamplers() created for input, one of node's input ports,...
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:2029
Definition Mesh.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:236
Definition VertexFallbackPlan.hpp:27
Definition Mesh.hpp:95
Data model for meshes.
Definition Mesh.hpp:196
Definition NodeRenderer.hpp:268
Useful abstraction for storing a graphics pipeline and associated resource bindings.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:278
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:215
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:67
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:307