RenderBatcher class
#include <nCine/Graphics/RenderBatcher.h>
Merges compatible render commands into fewer draw calls.
Scans a sorted queue of RenderCommand objects and groups runs that share the same material sort key and primitive type into single batched commands, copying their per-instance uniform blocks, vertices and indices into shared memory. Reduces the number of draw calls issued each frame.
Public types
- struct DirectBatch
- Batched command filled with instances directly rather than collected from commands, see BeginDirectBatch().
Constructors, destructors, conversion operators
Public functions
- void CreateBatches(const SmallVectorImpl<RenderCommand*>& srcQueue, SmallVectorImpl<RenderCommand*>& destQueue)
- Collects consecutive compatible commands into batched commands.
- void Reset()
- Marks all managed buffers as free for reuse in the next frame.
-
auto BeginDirectBatch(RenderCommand& refCommand,
std::
uint32_t maxInstances, DirectBatch& batch, RenderCommand* batchCommand = nullptr) -> bool - Sets up a batched command that the caller fills with instances directly.
- void EndDirectBatch(DirectBatch& batch)
- Finishes a batch started by BeginDirectBatch() for the instances written into it.
Function documentation
void nCine:: RenderBatcher:: CreateBatches(const SmallVectorImpl<RenderCommand*>& srcQueue,
SmallVectorImpl<RenderCommand*>& destQueue)
Collects consecutive compatible commands into batched commands.
| Parameters | |
|---|---|
| srcQueue | Sorted source command queue |
| destQueue | Destination queue that receives batched and pass-through commands |
Commands that cannot be batched, or runs shorter than the minimum batch size, are passed through to the destination queue unchanged.
The two bounds come from different places and mean different things. The maximum is what the batched shader was compiled for — a batch may not index past its instance array — so a backend that publishes IntValues::GetBatchSize() as well. The minimum is only a worthwhile-ness threshold: a batch submits 6 * its own size in vertices and its instance block is allocated from the sizes actually accumulated, so a batch below the maximum draws and costs only what it holds. Tying the two together would leave every run shorter than the maximum, and every remainder past a multiple of it, drawn one command at a time.
bool nCine:: RenderBatcher:: BeginDirectBatch(RenderCommand& refCommand,
std:: uint32_t maxInstances,
DirectBatch& batch,
RenderCommand* batchCommand = nullptr)
Sets up a batched command that the caller fills with instances directly.
| Returns | false with batch left empty if batching is disabled or the shader has no batched variant that draws without vertex data — the caller then submits a command per instance as before |
|---|
For a producer that emits many instances of one material in a row — the glyphs of a string above all — and would otherwise build a render command for each of them, only for CreateBatches() to copy all of them into a batch again. Each of those commands is cold by the time it is sorted and collected, and on a console with a few kilobytes of data cache that is most of what a glyph costs.
The command draws the batched variant of the shader of refCommand with its material state and layer, exactly like a batch that CreateBatches() collected from commands like refCommand. An instance is laid out like the instance block of refCommand, and it holds what that block would hold for a command of its own once committed — the model matrix with the depth of the layer included. The caller writes instance i at Instances + i * Stride, for as many as DirectBatch::maxInstances, fewer if the batched shader or the uniform buffer holds fewer. It can add the command to its render queue right away, EndDirectBatch() then finishes it for the instances written so far and can be called again after more were added.
The memory comes from the same per-frame buffers as the batches of CreateBatches(), so the batch is valid until the end of the frame. The command comes from the same pool as theirs, unless the caller passes batchCommand, a command of its own that it keeps from frame to frame - which saves looking one up in the pool and setting up its material from scratch every time.