Loading...
Searching...
No Matches

List of nodes to be rendered to an output. More...

Detailed Description

List of nodes to be rendered to an output.

This references all the score::gfx::Node that have an effect on a given output, and manages all the matching renderers, as well as a few shared data, such as output-specific UBOs, shared textures and buffers, etc.

The score::gfx::Graph creates one RenderList per OutputNode in the graph.

Public Types

using Buffers = std::pair< const Mesh *const, MeshBuffers >
 

Public Member Functions

 RenderList (OutputNode &output, const std::shared_ptr< RenderState > &state)
 
void init ()
 Initialize data for this renderer.
 
QRhiResourceUpdateBatch * initialBatch () const noexcept
 Initial resource update batch.
 
void setInitialBatch (QRhiResourceUpdateBatch *batch) noexcept
 Store a resource update batch to be submitted on the next render frame.
 
void flushInitialBatch ()
 Submit the pending initial batch before destroying resources it may name.
 
MeshBuffers initMeshBuffer (const Mesh &mesh, QRhiResourceUpdateBatch &res)
 Create buffers for a mesh and mark them for upload.
 
void update (QRhiResourceUpdateBatch &res)
 Update / upload this RenderList's shared data.
 
void render (QRhiCommandBuffer &commands, bool force=false) noexcept
 Render every node in order.
 
QRhiCommandBuffer * currentCommandBuffer () const noexcept
 
bool rendering () const noexcept
 
void release ()
 Release GPU resources owned by this render list.
 
void releaseBuffer (QRhiBuffer *buf)
 Retire a buffer this render list's node owns.
 
bool checkBindingsLive (const QRhiShaderResourceBindings &srb, const char *where) const noexcept
 
bool maybeRebuild (bool force=false)
 Check if the render size has changed in order to rebuild the pipelines.
 
bool resizeSwapchainSizedTargets (QSize newOutputSize, QSize newRenderSize)
 Fast-path for pure viewport resize.
 
TextureRenderTarget renderTargetForOutput (const Edge &edge) const noexcept
 Obtain the texture corresponding to an output port.
 
QRhiTexture * previewSource (const Node &node, int port) const noexcept
 The texture a preview of port of node reads in this list.
 
bool rendersLive () const noexcept
 
TextureRenderTarget renderTargetForInputPort (const Port &p) const noexcept
 Look up the render target for a given input port.
 
BufferView bufferForInput (const Edge &edge) const noexcept
 
BufferView bufferForOutput (const Edge &edge) const noexcept
 
QImage adaptImage (const QImage &in)
 Adapts an image to the GPU limits / format.
 
Buffers acquireMesh (const ossia::geometry_spec &, QRhiResourceUpdateBatch &res, const Mesh *current, MeshBuffers currentbufs) noexcept
 
void clearRenderers ()
 Clear the renderers so that they get reinitialized on the next frame.
 
QRhiTexture & emptyTexture () const noexcept
 Texture to use when a texture is missing (2D)
 
QRhiTexture & emptyTexture3D () const noexcept
 Texture to use when a 3D (sampler3D) texture is missing.
 
QRhiTexture & emptyTextureCube () const noexcept
 Texture to use when a cubemap (samplerCube) is missing.
 
QRhiTexture & emptyTextureArray () const noexcept
 Texture to use when a 2D array (sampler2DArray) is missing.
 
QRhiTexture & whiteTexture () const noexcept
 Opaque white 2D texture, for an input whose missing texture must not hide what it is applied to.
 
QRhiTexture & emptyTextureFor (const Port &in) const noexcept
 Empty texture of the view type an image input port samples.
 
QRhiTexture * selfFeedbackGrab (const Port &in) const noexcept
 Texture a GrabsFromSource port fed by its own node samples: the previous-frame copy, or an empty texture of the port's kind when the output is a depth or multisampled texture, which cannot be copied. nullptr when the port is not self-fed.
 
QRhiBuffer & outputUBO () const noexcept
 UBO corresponding to the output parameters:
 
QRhiBuffer & outputUBO (int sampleCount) const noexcept
 The output UBO for a pass rendering at sampleCount samples.
 
GpuResourceRegistry & registry () noexcept
 Per-output GPU arena store for scene-graph source nodes.
 
void releaseRendererState (NodeRenderer &r)
 
void releaseOwnedTarget (TextureRenderTarget &rt)
 
const GpuResourceRegistry & registry () const noexcept
 
VertexFallbackPool & vertexFallbackPool () noexcept
 Per-RenderList pool of neutral fallback vertex buffers for "REQUIRED: false" VERTEX_INPUTS whose upstream geometry does not provide a matching attribute.
 
GpuTimings & gpuTimings () noexcept
 Per-RenderList GPU-timing collector.
 
const GpuTimings & gpuTimings () const noexcept
 
Gfx::AssetTable * assetTable () const noexcept
 Session-wide asset decode cache.
 
void setAssetTable (Gfx::AssetTable *t) noexcept
 
const score::gfx::Mesh & defaultQuad () const noexcept
 A quad mesh correct for this API.
 
const score::gfx::Mesh & defaultTriangle () const noexcept
 A triangle mesh correct for this API.
 
bool requiresDepth (const score::gfx::Port &p) const noexcept
 Whether this list of rendering actions requires depth testing at all.
 
bool anyNodeRequiresDepth () const noexcept
 
int samples () const noexcept
 
const Quirks & quirks () const noexcept
 
void isolateFollowingPasses () noexcept
 Put the render passes that follow in a command buffer of their own.
 
bool canRender () const noexcept
 
QSize renderSize (const Edge *e) const noexcept
 
void createAllInputRenderTargets ()
 
void markBuilt () noexcept
 Mark this render list as fully built.
 
bool isBuilt () const noexcept
 Is this render list currently coherent with its output's GPU objects?
 
bool outputTargetsStale () const noexcept
 Are the output's targets waiting to be rebuilt on the next frame?
 
void markOutputTargetsStale () noexcept
 
void markRequiresDepth (bool value) noexcept
 
void onEdgeRemoved (Edge &edge, const ossia::hash_set< const Port * > *preserveSinks=nullptr)
 
void removeInputRenderTarget (const Port *port)
 Remove the render target for a specific input port.
 
void ensureSelfFeedbackTargets ()
 
QSize resolveDownstreamSize (const Node *node, const ossia::small_flat_map< const Port *, RenderTargetSpecs, 16 > &resolvedSpecs) const noexcept
 Resolve the downstream render target size for a node.
 
RenderTargetSpecs resolveInputRenderTargetSpecs (const Node &node, int32_t port) noexcept
 The render target specs of a texture inlet, with its size resolved.
 
RenderTargetSpecs resolveInputRenderTargetSpecs (const Node &node, int32_t port, const ossia::small_flat_map< const Port *, RenderTargetSpecs, 16 > &resolvedSpecs) noexcept
 As above, taking the inlets already in resolvedSpecs as resolved: resolving every inlet sinks first then costs one step per cable.
 

Static Public Member Functions

static void adoptBuffer (QRhiBuffer *buf)
 Borrow a buffer another node owns (a slot with owned == false).
 
static void dropAdoptedBuffer (QRhi &rhi, QRhiBuffer *buf)
 rhi is the QRhi the caller renders with, which created buf.
 
static void releaseResource (QRhi &rhi, QRhiResource *res)
 Free a resource that a pending resource update batch may still name.
 
static int adoptedBufferCount () noexcept
 Adoptions currently held, process-wide. Tests assert on this.
 
static bool isRetiredBuffer (const QRhiBuffer *buf) noexcept
 Buffers this render list has retired, for the bind-time liveness check.
 
static bool isRetiringBuffer (const QRhiBuffer *buf) noexcept
 
static QByteArray retiredBufferName (const QRhiBuffer *buf)
 
static int retiredBufferCount () noexcept
 
static void noteBufferLive (const QRhiBuffer *buf) noexcept
 
static void noteFrameCompleted () noexcept
 Advance the shared frame sequence and prune old retirements.
 
static bool hasRetiredBinding (const QRhiShaderResourceBindings &srb) noexcept
 
static bool strictBindingsEnabled () noexcept
 
static int staleBindingCount () noexcept
 
static void resetStaleBindingCount () noexcept
 
static const RenderList * previewHost (const Node &node, int port) noexcept
 The list that reads back the previews of port of node.
 

Public Attributes

OutputNode & output
 Output node to which this RenderList is rendering to.
 
RenderState & state
 RenderState corresponding to this RenderList.
 
std::vector< score::gfx::Node * > nodes
 Nodes present in this RenderList, in order.
 
std::vector< score::gfx::NodeRenderer * > renderers
 Renderers - one per node.
 
int64_t frame = 0
 
float currentDate [4] {}
 
bool dateFromStepClock {false}
 

Friends

struct Graph
 

Member Function Documentation

◆ adaptImage()

QImage score::gfx::RenderList::adaptImage ( const QImage &  in)

Adapts an image to the GPU limits / format.

e.g. Y direction, texture size limits...

◆ adoptBuffer()

void score::gfx::RenderList::adoptBuffer ( QRhiBuffer *  buf)
static

Borrow a buffer another node owns (a slot with owned == false).

Every adoption is a reference in a process-wide registry: the pointer is unique per QRhi and the owner is routinely on another render list, so the registry is shared the same way the retirement registry is. The consumer calls dropAdoptedBuffer() when its slot stops naming the buffer – it re-adopts, falls back to its own allocation, or releases its state – and an owner that has already released the buffer by then is what frees it.

This is what makes the owned=false contract enforced rather than assumed: a buffer with a live adoption cannot be freed, whatever the owner does.

◆ assetTable()

Gfx::AssetTable * score::gfx::RenderList::assetTable ( ) const
inlinenoexcept

Session-wide asset decode cache.

Set by Graph::createRenderList from GfxContext's AssetTable. May be null on test RenderLists or after teardown. Consumers must guard.

◆ checkBindingsLive()

bool score::gfx::RenderList::checkBindingsLive ( const QRhiShaderResourceBindings &  srb,
const char *  where 
) const
noexcept

True when no buffer bound by srb has been retired. Warns naming each offender. Reads only the binding list, never the buffer objects.

◆ currentCommandBuffer()

QRhiCommandBuffer * score::gfx::RenderList::currentCommandBuffer ( ) const
inlinenoexcept

The command buffer render() is currently recording into, or null outside a frame. Strategies that must record raw-API commands (the Vulkan host-import upload) need it at update() time, where only the resource batch is passed.

◆ ensureSelfFeedbackTargets()

void score::gfx::RenderList::ensureSelfFeedbackTargets ( )

Give every image input port fed by its own node a second render target, so the node never draws into the texture it samples.

◆ flushInitialBatch()

void score::gfx::RenderList::flushInitialBatch ( )

Submit the pending initial batch before destroying resources it may name.

A QRhiResourceUpdateBatch stores raw QRhiBuffer / QRhiTexture pointers until it is committed, and QRhiResource::deleteLater() outside a frame deletes immediately. Graph mutations run between frames, so any path that tears down renderer resources while this batch is outstanding must call this first; otherwise the next render() submits updates naming freed resources.

◆ gpuTimings()

GpuTimings & score::gfx::RenderList::gpuTimings ( )
inlinenoexcept

Per-RenderList GPU-timing collector.

Renderers wrap their begin/endPass regions in ScopedGpuTimer to attribute the CB-wide lastCompletedGpuTime to the named pass. The result is one frame stale — see GpuTiming.hpp for details.

◆ hasRetiredBinding()

bool score::gfx::RenderList::hasRetiredBinding ( const QRhiShaderResourceBindings &  srb)
staticnoexcept

Same question without the warning or the counter: for callers that are about to repair the binding rather than report it.

◆ initialBatch()

QRhiResourceUpdateBatch * score::gfx::RenderList::initialBatch ( ) const
noexcept

Initial resource update batch.

See QRhiResourceUpdateBatch::merge for documentation on this

◆ initMeshBuffer()

MeshBuffers score::gfx::RenderList::initMeshBuffer ( const Mesh &  mesh,
QRhiResourceUpdateBatch &  res 
)

Create buffers for a mesh and mark them for upload.

The meshes used by the nodes are cached (as most are just rendering on a full-screen triangle, which we can reuse).

◆ isBuilt()

bool score::gfx::RenderList::isBuilt ( ) const
inlinenoexcept

Is this render list currently coherent with its output's GPU objects?

False until the list has been built, and between a forced invalidation and the maybeRebuild() that runs on the next render frame; nothing outside the rebuild may ask a renderer for a render target then.

◆ isolateFollowingPasses()

void score::gfx::RenderList::isolateFollowingPasses ( )
noexcept

Put the render passes that follow in a command buffer of their own.

For passes whose sample count is not ours to choose and which may be empty. Must be called outside a pass.

◆ isRetiredBuffer()

bool score::gfx::RenderList::isRetiredBuffer ( const QRhiBuffer *  buf)
staticnoexcept

Buffers this render list has retired, for the bind-time liveness check.

A consumer that adopted a producer's buffer with owned=false keeps a raw pointer. If the producer retires it and the consumer never re-reads, the pointer stays in the consumer's shader resource bindings: the rebuild is gated on a hash of the pointers, and a pointer that goes dead without changing hashes the same. Qt cannot catch it either – its own generation check reads m_id off the resource, so a freed resource cannot report it.

Recording the retirement gives a liveness oracle that does not dereference the pointer, which is what makes the failure deterministic instead of dependent on whether the freed chunk has been reused yet. True once the buffer has actually been freed. deleteLater() keeps the object alive until the end of the frame that retired it, so a binding made in that same frame still dereferences a live object: only a use in a LATER frame is a use-after-free. Counting the same-frame case made the oracle over-report by roughly an order of magnitude.

◆ isRetiringBuffer()

bool score::gfx::RenderList::isRetiringBuffer ( const QRhiBuffer *  buf)
staticnoexcept

True from the moment of retirement, same frame included: the slot should stop pointing at it even while the object is still alive.

◆ markBuilt()

void score::gfx::RenderList::markBuilt ( )
inlinenoexcept

Mark this render list as fully built.

Prevents maybeRebuild() from unnecessarily tearing down and recreating all resources on the first render frame after createRenderList() has already fully initialized everything.

◆ markOutputTargetsStale()

void score::gfx::RenderList::markOutputTargetsStale ( )
inlinenoexcept

The output's targets are rebuilt on the next frame, with the passes drawing into them; the renderers keep their state.

◆ markRequiresDepth()

void score::gfx::RenderList::markRequiresDepth ( bool  value)
inlinenoexcept

Set the "any node requires depth" flag computed from the node graph. Either direction makes the output's targets stale: a rise leaves them without depth, a fall leaves every sink carrying a depth attachment nothing reads – 33 MB per sink at 4K, for the rest of the session. Only the output and the passes drawing into it are rebuilt, not the renderers.

◆ noteBufferLive()

void score::gfx::RenderList::noteBufferLive ( const QRhiBuffer *  buf)
staticnoexcept

Forget a retirement recorded at this address: the caller holds a buffer it created and has not released, so the allocator handed the freed address back and the retirement no longer describes the object there. Without this a recycled address reads as a use-after-free.

◆ onEdgeRemoved()

void score::gfx::RenderList::onEdgeRemoved ( Edge &  edge,
const ossia::hash_set< const Port * > *  preserveSinks = nullptr 
)

Notify that an edge was removed. Notifies renderers, releases RT if unused.

Parameters
preserveSinksOptional set of sink Ports that should keep their input render target even if this edge was their only feed. Used by batched edge updates (see GfxContext::incrementalEdgeUpdate) so that inserting a filter between two nodes doesn't destroy and immediately re-allocate the same RT when the old and new edges share a sink port.

◆ outputTargetsStale()

bool score::gfx::RenderList::outputTargetsStale ( ) const
inlinenoexcept

Are the output's targets waiting to be rebuilt on the next frame?

True between an output resize or a change of the depth requirement and the next render frame. The output node may already have replaced its QRhiTextureRenderTarget / QRhiRenderPassDescriptor, so a pass drawing into the output must not be built in that window; the frame builds them all.

◆ outputUBO() [1/2]

QRhiBuffer & score::gfx::RenderList::outputUBO ( ) const
inlinenoexcept

UBO corresponding to the output parameters:

  • Render size
  • Per-API adjustments and globals

◆ outputUBO() [2/2]

QRhiBuffer & score::gfx::RenderList::outputUBO ( int  sampleCount) const
noexcept

The output UBO for a pass rendering at sampleCount samples.

Identical to outputUBO() but for MSAA_SAMPLES, which is the pass's own sample count: a pass whose target is not at samples() (a raw raster OUTPUT declaring SAMPLES, a single-sampled per-mip / face / layer iteration) must not read the render list's (shader board U24). A count <= 0 (unknown, e.g. a swap-chain wrapper) or one the device does not support gives outputUBO().

◆ previewHost()

const RenderList * score::gfx::RenderList::previewHost ( const Node &  node,
int  port 
)
staticnoexcept

The list that reads back the previews of port of node.

The first output of the patch rendering the node that can sample the port and has rendered lately (rendersLive), else the preview hub if it renders the node, else none.

◆ previewSource()

QRhiTexture * score::gfx::RenderList::previewSource ( const Node &  node,
int  port 
) const
noexcept

The texture a preview of port of node reads in this list.

What the node's renderer exposes for the port, else the target it renders into for one of its edges, one it renders alone into if there is one. Null when there is none to sample.

◆ registry()

GpuResourceRegistry & score::gfx::RenderList::registry ( )
inlinenoexcept

Per-output GPU arena store for scene-graph source nodes.

Returns a reference to the registry that owns the Camera / Light / Material / PerDraw arena buffers. Source nodes (Camera, Light, PBRMesh, …) allocate a slot from this registry at construction and write their packed bytes into it at their own update().

Persist-across-rebuild contract: the registry is owned by the OutputNode (OutputNode::m_registry) and survives RenderList rebuilds — the same registry pointer is observed by both the pre- and post-rebuild RenderList for a given OutputNode. Consumers that cache the registry pointer (e.g. ScenePreprocessor's m_registry) can compare it against the new RL's registry on init() and skip the cache wipe when it is unchanged.

Valid between init() and release().

◆ releaseBuffer()

void score::gfx::RenderList::releaseBuffer ( QRhiBuffer *  buf)

Retire a buffer this render list's node owns.

The free is deferred while any consumer still holds the buffer through adoptBuffer(): the owner's release then only marks it orphaned, and the last dropAdoptedBuffer() frees it. A consumer that borrowed the handle therefore keeps binding a live object – the last contents its producer wrote – until it re-reads the producer's geometry and lets go, instead of binding a freed one.

◆ releaseOwnedTarget()

void score::gfx::RenderList::releaseOwnedTarget ( TextureRenderTarget &  rt)

Frees a render target of this list or of one of its renderers, first making the registry forget its texture.

◆ releaseRendererState()

void score::gfx::RenderList::releaseRendererState ( NodeRenderer &  r)

Releases a renderer's state, first making the registry forget its port textures, which may be published as dynamic textures.

◆ releaseResource()

void score::gfx::RenderList::releaseResource ( QRhi &  rhi,
QRhiResource *  res 
)
static

Free a resource that a pending resource update batch may still name.

QRhiResource::deleteLater() deletes at once when no frame is being recorded, while the initial batch built by init() is only submitted on the first frame. Outside a frame the resource is kept until a render list on the same QRhi records or submits its next batch, or is released, or the QRhi is destroyed. rhi is the QRhi that created res: before Qt 6.5 a resource cannot name it.

◆ render()

void score::gfx::RenderList::render ( QRhiCommandBuffer &  commands,
bool  force = false 
)
noexcept

Render every node in order.

Never throws: a node that fails to build its pipeline, or any other exception raised while recording, is caught and reported here rather than escaping into the output node's frame bracket and, from there, into QCoreApplication::notify.

◆ rendering()

bool score::gfx::RenderList::rendering ( ) const
inlinenoexcept

Whether render() is on the stack for this list. A render() entered while it is returns without recording anything.

◆ rendersLive()

bool score::gfx::RenderList::rendersLive ( ) const
noexcept

Whether this list rendered a frame lately. A hidden window or a slow output does not: its previews move to the hub meanwhile.

◆ renderTargetForInputPort()

TextureRenderTarget score::gfx::RenderList::renderTargetForInputPort ( const Port &  p) const
noexcept

Look up the render target for a given input port.

All input render targets are created centrally before any node init() so that they are available regardless of initialization order.

◆ renderTargetForOutput()

TextureRenderTarget score::gfx::RenderList::renderTargetForOutput ( const Edge &  edge) const
noexcept

Obtain the texture corresponding to an output port.

This is done by looking for the render target which corresponds to a given port.

◆ requiresDepth()

bool score::gfx::RenderList::requiresDepth ( const score::gfx::Port &  p) const
noexcept

Whether this list of rendering actions requires depth testing at all.

e.g. it's not needed if we're just doing some generative shaders.

◆ resizeSwapchainSizedTargets()

bool score::gfx::RenderList::resizeSwapchainSizedTargets ( QSize  newOutputSize,
QSize  newRenderSize 
)

Fast-path for pure viewport resize.

Update state.outputSize to newOutputSize and state.renderSize to newRenderSize, and mark the output's targets stale: the next render frame rebuilds the output renderer and the passes drawing into it, then the rt_changed block recreates the input render targets whose resolved size follows the output and re-initialises only the renderers reading them, and those allocating at the render size (followsRenderSize). Every other renderer keeps its GPU state (feedback textures, simulation buffers), which a release() + init() of the whole list would wipe on every resize of the output.

The two sizes are distinct: the output node owns the render size (the /rendersize override, ScreenNode::setRenderSize), the platform owns the output size (the swapchain). Callers pass both from the RenderState the output node has already updated.

Returns true on success. Returns false, and the caller must fall back to recreateOutputRenderList, when:

  • either size is invalid
  • renderers vector is empty (RL not yet initialised)

◆ resolveDownstreamSize()

QSize score::gfx::RenderList::resolveDownstreamSize ( const Node *  node,
const ossia::small_flat_map< const Port *, RenderTargetSpecs, 16 > &  resolvedSpecs 
) const
noexcept

Resolve the downstream render target size for a node.

Returns the largest width and the largest height across the texture inlets this node renders into: the output node counts as the render size, an inlet with an explicit size as that size, and an inlet without one as the size it resolves to in turn, down the chain. Used as fallback when a node's input port has no explicit render target size. Empty when the node feeds no texture inlet of this render list.

Entries of resolvedSpecs take precedence over the recursive resolution.

◆ resolveInputRenderTargetSpecs()

RenderTargetSpecs score::gfx::RenderList::resolveInputRenderTargetSpecs ( const Node &  node,
int32_t  port 
)
noexcept

The render target specs of a texture inlet, with its size resolved.

The inlet's own settings; without an explicit size, the size inherited from downstream (resolveDownstreamSize), else the render size.

◆ retiredBufferName()

QByteArray score::gfx::RenderList::retiredBufferName ( const QRhiBuffer *  buf)
static

Name the buffer carried at retirement, while it was still alive: reading name() off a retired buffer is the dereference this check exists to avoid.

◆ setInitialBatch()

void score::gfx::RenderList::setInitialBatch ( QRhiResourceUpdateBatch *  batch)
inlinenoexcept

Store a resource update batch to be submitted on the next render frame.

Used by incremental edge additions that happen after the first render frame (when the original m_initialBatch has already been consumed).

◆ staleBindingCount()

int score::gfx::RenderList::staleBindingCount ( )
staticnoexcept

How many stale bindings checkBindingsLive has seen, process-wide. Tests assert on this: the render loop swallows the strict-mode exception and carries on, so a violation is invisible from the frame result alone.

◆ strictBindingsEnabled()

bool score::gfx::RenderList::strictBindingsEnabled ( )
staticnoexcept

True when SCORE_GFX_STRICT_BINDINGS is set: a failed liveness check then throws instead of only warning.

◆ vertexFallbackPool()

VertexFallbackPool & score::gfx::RenderList::vertexFallbackPool ( )
inlinenoexcept

Per-RenderList pool of neutral fallback vertex buffers for "REQUIRED: false" VERTEX_INPUTS whose upstream geometry does not provide a matching attribute.

Valid between init() and release(). See VertexFallbackPool.hpp.


The documentation for this class was generated from the following files: