Table of Contents

Class EntityInstancing

Namespace
Stride.CommunityToolkit.Rendering.Instancing
Assembly
Stride.CommunityToolkit.dll

Draws many entities in a single draw call by collecting their world matrices every frame.

public class EntityInstancing : InstancingUserArray, IInstancing
Inheritance
EntityInstancing
Implements
Derived
Inherited Members

Examples

game.AddInstancingSupport();

var instancing = new EntityInstancing();
var master = new Entity("Master") { new ModelComponent(model), new InstancingComponent { Type = instancing } };
master.Scene = scene;

foreach (var entity in crowd) instancing.AddInstance(entity);

Remarks

This is a faster alternative to Stride's InstancingEntityTransform. Both do the same job - a master entity carries the ModelComponent and an InstancingComponent, while the instances contribute only their transforms - but this one keeps direct TransformComponent references, gathers and inverts in one parallel pass, reuses its arrays, and removes instances in constant time.

Instances are registered explicitly with AddInstance(Entity) rather than by adding an InstanceComponent, because the engine's registration hooks are internal to Stride.Engine. Instance entities must not have a ModelComponent of their own, or they are drawn twice - once individually and once by the master.

Use BepuEntityInstancing from Stride.CommunityToolkit.Bepu instead when the instances are physics bodies: it skips the whole update while every body is asleep, which is where most of the saving comes from for settled scenes.

Properties

AssumeRigidTransforms

Gets or sets a value indicating whether instance transforms are rigid - rotation and translation only, with no scale or shear. Defaults to true.

public bool AssumeRigidTransforms { get; set; }

Property Value

bool

Remarks

A rigid inverse is roughly an order of magnitude cheaper than a general 4x4 inverse and is exact for physics bodies, which never scale. Set to false if instances can be scaled or sheared, which switches to a SIMD-accelerated general inverse. Leaving it true for scaled instances produces incorrect lighting, because the inverse matrices are what the shader uses to transform normals.

BoundingBox

public override BoundingBox BoundingBox { get; }

Property Value

BoundingBox

GatheredMatrices

Gets the world matrices of the registered instances, in registration order.

protected ReadOnlySpan<Matrix> GatheredMatrices { get; }

Property Value

ReadOnlySpan<Matrix>

Remarks

Only the first InstanceCount entries are valid.

LastUpdateMilliseconds

Gets how long the last Update() took. Intended for diagnostics and on-screen counters.

public double LastUpdateMilliseconds { get; }

Property Value

double

ModelTransformUsage

public override ModelTransformUsage ModelTransformUsage { get; }

Property Value

ModelTransformUsage

Remarks

Instance matrices are already in world space, so the master's own transform is ignored.

ParallelThreshold

Gets or sets the instance count from which the gather runs in parallel. Defaults to 2048.

public int ParallelThreshold { get; set; }

Property Value

int

Remarks

Below this count it runs sequentially, because forking to the thread pool costs more than the work it spreads - and Stride's instancing processor already dispatches masters in parallel. The default is where the two met on the benchmark machine: sequential is six times faster at 256 instances, they draw level at 2048, and parallel is three times faster by 8192. Machines with different core counts will cross over elsewhere, so tune this if it matters; set it to MaxValue to always stay sequential.

RegisteredInstanceCount

Gets the number of registered instances, which is not necessarily the number drawn: disabled or skipped frames aside, InstanceCount is what the renderer uses.

public int RegisteredInstanceCount { get; }

Property Value

int

StructureDirty

Gets a value indicating whether the registered instances changed since the last update.

protected bool StructureDirty { get; }

Property Value

bool

Remarks

CanSkipUpdate() implementations must not skip while this is true.

UpdateSkippedLastFrame

Gets a value indicating whether the last update was skipped because nothing had moved.

public bool UpdateSkippedLastFrame { get; }

Property Value

bool

Remarks

Always false here; see BepuEntityInstancing.

Methods

AddInstance(Entity)

Registers an entity as an instance. Its TransformComponent is captured once, so the entity must not be reparented into a different transform component afterwards.

public bool AddInstance(Entity entity)

Parameters

entity Entity

The entity to draw as an instance. Must not carry a ModelComponent.

Returns

bool

true if it was added; false if it was already registered.

CanSkipUpdate()

Determines whether this frame's gather can be skipped because no instance has moved since the last one.

protected virtual bool CanSkipUpdate()

Returns

bool

true to reuse the previous frame's matrices, inverses and bounding box verbatim. The base implementation always returns false, because without physics there is no cheap way to know whether a transform changed.

Remarks

Called only when the registered instances are unchanged, so implementations need not check StructureDirty. It must be cheaper than the gather it avoids, and must never return true when a transform has in fact changed - the frame would render stale positions.

Clear()

Unregisters every instance.

public void Clear()

OnInstanceAdded(Entity)

Called after an instance is appended, for derived classes keeping parallel data.

protected virtual void OnInstanceAdded(Entity entity)

Parameters

entity Entity

The newly registered entity, now at index RegisteredInstanceCount - 1.

OnInstanceRemoved(int, int)

Called after an instance is removed by swapping the last one into its place, for derived classes keeping parallel data.

protected virtual void OnInstanceRemoved(int index, int lastIndex)

Parameters

index int

The index that was vacated and has just been overwritten.

lastIndex int

The index the surviving instance came from, now past the end.

Remarks

Derived data must mirror this exactly: copy lastIndex to index when they differ, then drop lastIndex.

OnInstancesCleared()

Called after every instance is unregistered, for derived classes keeping parallel data.

protected virtual void OnInstancesCleared()

RemoveInstance(Entity)

Unregisters an entity. Removing the entity from the scene does not unregister it, unlike the engine's InstanceComponent.

public bool RemoveInstance(Entity entity)

Parameters

entity Entity

The entity to stop drawing.

Returns

bool

true if it was registered.

Update()

Called by Stride's instancing processor once per frame, possibly on a worker thread.

public override void Update()