Instance Link Example
InstanceLinkWaveDemo is a working Custom Registerer included in the Examples/Demos folder. It moves and tints a grid of instances every frame, with an Update Mode dropdown comparing the three ways to submit those updates.
A Burst job computes the new positions and colors regardless of mode — the dropdown selects only how the results reach the GPU, so the three paths can be profiled against each other on identical data.
Per-instance color requires Enable Per-Instance Color on the Config. The
colorfield onInstanceDatais not uploaded byWriteChunk— color is set only through the staging paths below.
The Three Update Modes
| Mode | Calls used | Per-slot merge | Speed |
|---|---|---|---|
| Simple | StagePosition + StageColor per instance |
Yes | Slowest |
| Unsafe | SetPositionUnsafe + SetColorUnsafe per instance |
No | Faster |
| Batched | SetTRSBatchUnsafe + SetColorBatchUnsafe, two calls total |
No | Fastest at scale |
Simple
Per-instance Stage* calls. Repeated writes to the same slot within one frame are merged in the tracker's dictionary, so it's safe even if your code touches an instance more than once per frame. This is the correct default for gameplay code.
for (int i = 0; i < _count; i++)
{
StagePosition(_slots[i], _patches[i].position);
StageColor(_slots[i], _colors32[i]);
}
Unsafe
Per-instance Set*Unsafe calls. Skips the merge step and writes straight through to BRGRenderer. The caller must guarantee at most one write per slot per frame — two writes to the same slot race, and partial-flag TRS writes can lose components.
for (int i = 0; i < _count; i++)
{
SetPositionUnsafe(_slots[i], _patches[i].position);
SetColorUnsafe(_slots[i], _colors32[i]);
}
Batched
Two calls submit the job's entry arrays directly into the core staging buffers. Same one-write-per-slot contract as Unsafe mode, but without the per-instance call overhead — this is the mode to use when you're updating thousands of instances per frame.
SetTRSBatchUnsafe(_patches, _slots, _count);
SetColorBatchUnsafe(_colorEntries, _count);
The Burst job writes BRGRenderer.GPUTRSPatchEntry and BRGRenderer.GPUColorEntry structs directly, using BRGRegistrationTracker.kPatchPosition for the flag bits and BRGRegistrationTracker.PackColor to encode the color:
patches[i] = new BRGRenderer.GPUTRSPatchEntry
{
slotIndex = slot,
flags = BRGRegistrationTracker.kPatchPosition, // position only
position = pos
};
colorEntries[i] = new BRGRenderer.GPUColorEntry
{
slotIndex = slot,
packedColor = BRGRegistrationTracker.PackColor(color)
};
See Batched Unsafe Writes for the full API and struct layouts.
How It Works
OnInitialize
- Registers the assigned prefab via
RegisterRenderSignatureFromPrefab() - Allocates a chunk and builds a
width × depthgrid ofInstanceData, centered on the GameObject - Caches each instance's base position and its radius from the center (which drives the wave phase)
- Pads the chunk bounds by the wave amplitude so rising and falling instances aren't culled at the extremes
- Calls
WriteChunk, thenBRGSystem.RequestSnapAllViews()
OnChunkWritten
Resolves each instance's stable global slot index once the chunk write has completed on the GPU. Invalid links are compacted out so every entry in the parallel arrays maps to a live slot.
int write = 0;
for (int i = 0; i < total; i++)
{
var link = GetInstanceLink(_chunk, i);
if (!link.IsValid) continue;
_slots[write] = link.GlobalBRGSlotIndex;
_basePositions[write] = _basePositions[i];
_radii[write] = _radii[i];
write++;
}
_count = write;
This is the correct place to resolve links —
GetInstanceLinkimmediately afterWriteChunkis not safe, because the GPU write may be deferred.
OnShutdown
Completes any in-flight job, then disposes the persistent NativeArrays. The chunk itself is freed automatically by the base class.
Inspector Properties
| Property | Type | Default | Description |
|---|---|---|---|
| Update Mode | enum | Batched | Which submission path to use: Simple / Unsafe / Batched. See above. |
| Prefab | GameObject | — | Prefab to render. Must have a MeshFilter + MeshRenderer (or LODGroup). |
The remaining Grid, Wave, and Color fields just tune the demo — grid dimensions and spacing, wave amplitude/speed/frequency, and the hue range the rainbow tint spans. All are editable at runtime; changes that affect instance count trigger a rebuild, while amplitude changes only resize the chunk bounds in place.
For the simpler chunk/instance lifecycle example, see Registerer Example.