Loading...
Searching...
No Matches
RenderList.hpp
1#pragma once
2#include <Gfx/Graph/CommonUBOs.hpp>
3#include <Gfx/Graph/GpuTiming.hpp>
4#include <Gfx/Graph/Node.hpp>
5#include <Gfx/Graph/Quirks.hpp>
6#include <Gfx/Graph/TextureKind.hpp>
7
8#include <ossia/detail/flat_map.hpp>
9#include <ossia/detail/flat_set.hpp>
10#include <ossia/detail/hash_map.hpp>
11
12#include <array>
13#include <chrono>
14#include <memory>
15
16namespace Gfx
17{
18class AssetTable;
19}
20namespace score::gfx
21{
22
23class GpuResourceRegistry;
24class OutputNode;
25class VertexFallbackPool;
35class SCORE_PLUGIN_GFX_EXPORT RenderList
36{
37 friend struct Graph;
38private:
39 QRhiCommandBuffer* m_currentCommands{};
40 std::shared_ptr<RenderState> m_state;
41
42public:
43 explicit RenderList(OutputNode& output, const std::shared_ptr<RenderState>& state);
45
49 void init();
50
56 [[nodiscard]] QRhiResourceUpdateBatch* initialBatch() const noexcept;
57
64 void setInitialBatch(QRhiResourceUpdateBatch* batch) noexcept { m_initialBatch = batch; }
65
75 void flushInitialBatch();
76
83 MeshBuffers initMeshBuffer(const Mesh& mesh, QRhiResourceUpdateBatch& res);
84
88 void update(QRhiResourceUpdateBatch& res);
89
98 void render(QRhiCommandBuffer& commands, bool force = false) noexcept;
99
103 QRhiCommandBuffer* currentCommandBuffer() const noexcept
104 {
105 return m_currentCommands;
106 }
107
110 bool rendering() const noexcept { return m_rendering; }
111
115 void release();
116
127 void releaseBuffer(QRhiBuffer* buf);
128
142 static void adoptBuffer(QRhiBuffer* buf);
144 static void dropAdoptedBuffer(QRhi& rhi, QRhiBuffer* buf);
145
156 static void releaseResource(QRhi& rhi, QRhiResource* res);
158 static int adoptedBufferCount() noexcept;
159
179 static bool isRetiredBuffer(const QRhiBuffer* buf) noexcept;
182 static bool isRetiringBuffer(const QRhiBuffer* buf) noexcept;
185 static QByteArray retiredBufferName(const QRhiBuffer* buf);
186 static int retiredBufferCount() noexcept;
191 static void noteBufferLive(const QRhiBuffer* buf) noexcept;
193 static void noteFrameCompleted() noexcept;
194
197 bool checkBindingsLive(
198 const QRhiShaderResourceBindings& srb, const char* where) const noexcept;
199
202 static bool hasRetiredBinding(const QRhiShaderResourceBindings& srb) noexcept;
203
206 static bool strictBindingsEnabled() noexcept;
207
211 static int staleBindingCount() noexcept;
212 static void resetStaleBindingCount() noexcept;
213
217 bool maybeRebuild(bool force = false);
218
242 bool resizeSwapchainSizedTargets(QSize newOutputSize, QSize newRenderSize);
243
249 TextureRenderTarget renderTargetForOutput(const Edge& edge) const noexcept;
250
258 QRhiTexture* previewSource(const Node& node, int port) const noexcept;
259
267 static const RenderList* previewHost(const Node& node, int port) noexcept;
268
271 bool rendersLive() const noexcept;
272
279 TextureRenderTarget renderTargetForInputPort(const Port& p) const noexcept;
280
281 BufferView bufferForInput(const Edge& edge) const noexcept;
282 BufferView bufferForOutput(const Edge& edge) const noexcept;
283
289 QImage adaptImage(const QImage& in);
290
294 OutputNode& output;
295
300
301 using Buffers = std::pair<const Mesh* const, MeshBuffers>;
302 Buffers acquireMesh(
303 const ossia::geometry_spec&, QRhiResourceUpdateBatch& res, const Mesh* current,
304 MeshBuffers currentbufs) noexcept;
305
306private:
309 void sweepMeshCache() noexcept;
310
311public:
312
316 std::vector<score::gfx::Node*> nodes;
317
321 std::vector<score::gfx::NodeRenderer*> renderers;
322
324 void clearRenderers();
325
329 QRhiTexture& emptyTexture() const noexcept { return *m_emptyTexture; }
330
334 QRhiTexture& emptyTexture3D() const noexcept { return *m_emptyTexture3D; }
335
339 QRhiTexture& emptyTextureCube() const noexcept { return *m_emptyTextureCube; }
340
344 QRhiTexture& emptyTextureArray() const noexcept { return *m_emptyTextureArray; }
345
350 QRhiTexture& whiteTexture() const noexcept { return *m_whiteTexture; }
351
355 QRhiTexture& emptyTextureFor(const Port& in) const noexcept;
356
363 QRhiTexture* selfFeedbackGrab(const Port& in) const noexcept;
364
371 QRhiBuffer& outputUBO() const noexcept { return *m_outputUBO; }
372
383 QRhiBuffer& outputUBO(int sampleCount) const noexcept;
384
403 GpuResourceRegistry& registry() noexcept { return *m_registry; }
404
407 void releaseRendererState(NodeRenderer& r);
408
411 void releaseOwnedTarget(TextureRenderTarget& rt);
412 const GpuResourceRegistry& registry() const noexcept { return *m_registry; }
413
421 VertexFallbackPool& vertexFallbackPool() noexcept { return *m_vertexFallbackPool; }
422
430 GpuTimings& gpuTimings() noexcept { return m_gpuTimings; }
431 const GpuTimings& gpuTimings() const noexcept { return m_gpuTimings; }
432
440 Gfx::AssetTable* assetTable() const noexcept { return m_assetTable; }
441 void setAssetTable(Gfx::AssetTable* t) noexcept { m_assetTable = t; }
442
446 const score::gfx::Mesh& defaultQuad() const noexcept;
447
451 const score::gfx::Mesh& defaultTriangle() const noexcept;
452
458 bool requiresDepth(const score::gfx::Port& p) const noexcept;
459 bool anyNodeRequiresDepth() const noexcept { return m_requiresDepth; }
460
461 int samples() const noexcept { return m_samples; }
462
463 const Quirks& quirks() const noexcept { return m_quirks; }
464
471 void isolateFollowingPasses() noexcept;
472
473 bool canRender() const noexcept { return m_ready; }
474
475 QSize renderSize(const Edge* e) const noexcept;
476
477 int64_t frame = 0;
478 // Year, month, day, seconds since midnight: the ISF DATE uniform.
479 float currentDate[4]{};
480 // Set by GfxContext::renderFrames, which then writes currentDate from its
481 // step clock, until the next GfxContext::updateGraph(): keeps DATE off the
482 // wall clock so a step renders deterministically.
483 bool dateFromStepClock{false};
484
485 void createAllInputRenderTargets();
486
494 void markBuilt() noexcept { m_built = true; m_lastSize = state.renderSize; }
495
503 [[nodiscard]] bool isBuilt() const noexcept { return m_built; }
504
513 [[nodiscard]] bool outputTargetsStale() const noexcept { return m_outputTargetsStale; }
514
518 {
519 if(m_built)
520 m_outputTargetsStale = true;
521 }
522
528 void markRequiresDepth(bool value) noexcept
529 {
530 if(value != m_requiresDepth && m_built)
531 m_outputTargetsStale = true;
532 m_requiresDepth = value;
533 }
534
542 void
543 onEdgeRemoved(Edge& edge, const ossia::hash_set<const Port*>* preserveSinks = nullptr);
544
546 void removeInputRenderTarget(const Port* port);
547
550 void ensureSelfFeedbackTargets();
551
564 QSize resolveDownstreamSize(
565 const Node* node,
566 const ossia::small_flat_map<const Port*, RenderTargetSpecs, 16>& resolvedSpecs)
567 const noexcept;
568
575 RenderTargetSpecs resolveInputRenderTargetSpecs(const Node& node, int32_t port) noexcept;
576
581 RenderTargetSpecs resolveInputRenderTargetSpecs(
582 const Node& node, int32_t port,
583 const ossia::small_flat_map<const Port*, RenderTargetSpecs, 16>& resolvedSpecs) noexcept;
584
585private:
586 void resolveAllInletSpecs(
587 ossia::small_flat_map<const Port*, RenderTargetSpecs, 16>& resolvedSpecs) noexcept;
588 QSize resolveInletSize(
589 const Port& in,
590 const ossia::small_flat_map<const Port*, RenderTargetSpecs, 16>& resolvedSpecs,
591 ossia::flat_set<const Node*>& visiting) const noexcept;
592 QSize resolveDownstreamSize(
593 const Node* node,
594 const ossia::small_flat_map<const Port*, RenderTargetSpecs, 16>& resolvedSpecs,
595 ossia::flat_set<const Node*>& visiting) const noexcept;
596
597 void renderImpl(QRhiCommandBuffer& commands, bool force);
598
599 // Rendering failures repeat every frame: report the first one per
600 // RenderList, then one in 600, instead of a line per frame.
601 int m_renderFailures{};
602 int m_nestedRenders{};
603 bool m_rendering{};
604
605 OutputUBO m_outputUBOData;
606
607
608 QRhiResourceUpdateBatch* m_initialBatch{};
609 void retireResourcesReleasedOutsideFrame() noexcept;
610
611 // Scene-graph arena store (camera / light / material / per_draws buffers).
612 // Persist-across-rebuild contract: ownership is on the OutputNode
613 // (OutputNode::m_registry), so the registry — and all its arena
614 // buffers, mesh slabs, texture-array channels, ScenePreprocessor
615 // material/env slots — survives `Graph::recreateOutputRenderList`
616 // (viewport resize / fallback rebuild). RenderList::init() either
617 // calls GpuResourceRegistry::init() once (first RL on this output)
618 // or adopts the populated state as-is (every subsequent rebuild).
619 // RenderList::release() does NOT destroy it. OutputNode::releaseRegistry()
620 // tears it down via destroyOwned() when its QRhi goes away.
621 GpuResourceRegistry* m_registry{};
622
623 // Pool of tiny shared vertex buffers used to satisfy "REQUIRED: false"
624 // VERTEX_INPUTS whose upstream geometry is missing an attribute.
625 // Same lifetime as m_registry.
626 std::unique_ptr<VertexFallbackPool> m_vertexFallbackPool;
627
628 // GPU-timing collector. Lives as long as the RenderList — outlives
629 // individual renderers so per-pass measurements survive node churn.
630 GpuTimings m_gpuTimings;
631
632 // Session-wide asset decode cache. Non-owning; GfxContext is the
633 // owner. May be null.
634 Gfx::AssetTable* m_assetTable{};
635
636 // Material
637 QRhiBuffer* m_outputUBO{};
638 // outputUBO(int): one copy per other supported sample count.
639 std::vector<std::pair<int, QRhiBuffer*>> m_outputUBOBySamples;
640 QRhiTexture* m_emptyTexture{};
641 QRhiTexture* m_emptyTexture3D{};
642 QRhiTexture* m_emptyTextureCube{};
643 QRhiTexture* m_emptyTextureArray{};
644 QRhiTexture* m_whiteTexture{};
645
649 ossia::flat_map<Mesh*, MeshBuffers> m_vertexBuffers;
650
651 ossia::flat_map<ossia::geometry_spec, Mesh*> m_customMeshCache;
652
659 ossia::small_flat_map<const Port*, TextureRenderTarget, 8> m_inputRenderTargets;
660
669 struct SelfFeedbackTarget
670 {
671 TextureRenderTarget back;
672 bool written{};
673 };
674 ossia::small_flat_map<const Port*, SelfFeedbackTarget, 2> m_selfFeedbackTargets;
675
681 ossia::small_flat_map<const Port*, QRhiTexture*, 2> m_selfFeedbackGrabs;
682
685 void renderPreviewTaps(QRhiCommandBuffer& commands, QRhiResourceUpdateBatch*& res);
686 void releasePreviewTaps();
688 struct PreviewTapTarget
689 {
690 std::weak_ptr<PreviewTap> tap;
691 TextureRenderTarget rt;
692 QRhiSampler* sampler{};
693 Pipeline pipeline;
694 MeshBuffers mesh;
695 QRhiTexture* source{};
697 TextureKind kind{};
699 std::shared_ptr<QRhiReadbackResult> readback;
701 int64_t visited{-1};
702 void release();
703 };
704 std::vector<PreviewTapTarget> m_previewTaps;
706 std::array<std::pair<QShader, QShader>, texture_kind_count> m_previewShaders;
709 struct RetiredPreviewReadback
710 {
711 std::shared_ptr<QRhiReadbackResult> readback;
712 int64_t until{};
713 };
714 std::vector<RetiredPreviewReadback> m_retiredPreviewReadbacks;
715 void retirePreviewTap(PreviewTapTarget& t);
716 std::chrono::steady_clock::time_point m_lastRender{std::chrono::steady_clock::now()};
717 ossia::small_flat_map<const Port*, QRhiTexture*, 2> m_selfFeedbackPlaceholders;
718 ossia::flat_set<const Port*> m_selfFeedbackWarned;
719
731 ossia::flat_set<const QRhiTexture*> m_targetsToClear;
732 void clearOnFirstUse(const TextureRenderTarget& rt);
733 void clearNewTargets(QRhiCommandBuffer& commands, QRhiResourceUpdateBatch& res);
734
735 void updateSelfFeedbackTargets(QRhiResourceUpdateBatch& res);
736 void updateSelfFeedbackGrabs(QRhiResourceUpdateBatch& res);
737 void removeSelfFeedbackTarget(const Port* port);
738 bool ensureSelfFeedbackTarget(const Port& in);
739
740 void rebuildOutputTargets(QRhiResourceUpdateBatch& res);
741
745 QSize m_lastSize{};
746
747 int m_minTexSize{};
748 int m_maxTexSize{};
749 int m_samples{1};
750 Quirks m_quirks;
751
752 bool m_warnedMixedDepthCompare = false;
753 bool m_requiresDepth{};
754 bool m_ready{};
755 bool m_built{};
756 bool m_outputTargetsStale{};
757 bool m_renderSizeChanged{};
758};
759
767SCORE_PLUGIN_GFX_EXPORT
768QRhiTexture::Format imageTextureFormat(const QRhi& rhi) noexcept;
769
776SCORE_PLUGIN_GFX_EXPORT
777QImage adaptImageFormat(QImage img, QRhiTexture::Format fmt);
778}
Cross-RenderList content-hash dedup for decoded asset bytes.
Definition AssetTable.hpp:71
Per-RenderList arena store for GPU-resident scene data.
Definition GpuResourceRegistry.hpp:45
Named-bucket GPU timing collector.
Definition GpuTiming.hpp:38
Root data model for visual nodes.
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:115
Renderer for a given node.
Definition NodeRenderer.hpp:11
Base class for sink nodes (QWindow, spout, syphon, NDI output, ...)
Definition OutputNode.hpp:56
List of nodes to be rendered to an output.
Definition RenderList.hpp:36
GpuResourceRegistry & registry() noexcept
Per-output GPU arena store for scene-graph source nodes.
Definition RenderList.hpp:403
bool outputTargetsStale() const noexcept
Are the output's targets waiting to be rebuilt on the next frame?
Definition RenderList.hpp:513
void markBuilt() noexcept
Mark this render list as fully built.
Definition RenderList.hpp:494
VertexFallbackPool & vertexFallbackPool() noexcept
Per-RenderList pool of neutral fallback vertex buffers for "REQUIRED: false" VERTEX_INPUTS whose upst...
Definition RenderList.hpp:421
void setInitialBatch(QRhiResourceUpdateBatch *batch) noexcept
Store a resource update batch to be submitted on the next render frame.
Definition RenderList.hpp:64
bool isBuilt() const noexcept
Is this render list currently coherent with its output's GPU objects?
Definition RenderList.hpp:503
QRhiBuffer & outputUBO() const noexcept
UBO corresponding to the output parameters:
Definition RenderList.hpp:371
bool rendering() const noexcept
Definition RenderList.hpp:110
void markRequiresDepth(bool value) noexcept
Definition RenderList.hpp:528
QRhiTexture & emptyTexture3D() const noexcept
Texture to use when a 3D (sampler3D) texture is missing.
Definition RenderList.hpp:334
QRhiTexture & emptyTextureCube() const noexcept
Texture to use when a cubemap (samplerCube) is missing.
Definition RenderList.hpp:339
void markOutputTargetsStale() noexcept
Definition RenderList.hpp:517
QRhiTexture & whiteTexture() const noexcept
Opaque white 2D texture, for an input whose missing texture must not hide what it is applied to.
Definition RenderList.hpp:350
QRhiTexture & emptyTextureArray() const noexcept
Texture to use when a 2D array (sampler2DArray) is missing.
Definition RenderList.hpp:344
Gfx::AssetTable * assetTable() const noexcept
Session-wide asset decode cache.
Definition RenderList.hpp:440
GpuTimings & gpuTimings() noexcept
Per-RenderList GPU-timing collector.
Definition RenderList.hpp:430
Definition VertexFallbackPool.hpp:43
Binds the rendering pipeline to ossia processes.
Definition AssetTable.cpp:7
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
QRhiTexture::Format imageTextureFormat(const QRhi &rhi) noexcept
Texture format to use for images uploaded from the CPU.
Definition RenderList.cpp:1946
QImage adaptImageFormat(QImage img, QRhiTexture::Format fmt)
Put an image in the byte order imageTextureFormat() asks for.
Definition RenderList.cpp:1952
TextureKind
Definition TextureKind.hpp:20
Base toolkit upon which the software is built.
Definition Application.cpp:83
STL namespace.
Definition Mesh.hpp:18
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:236
Represents a graph of renderers.
Definition score-plugin-gfx/Gfx/Graph/Graph.hpp:25
Definition Mesh.hpp:95
Data model for meshes.
Definition Mesh.hpp:196
Port of a score::gfx::Node.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:215
Global state associated to a rendering context.
Definition RenderState.hpp:40
Definition score-plugin-gfx/Gfx/Graph/Node.hpp:71
Useful abstraction for storing all the data related to a render target.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:307