Table of Contents

System Overview


This page explains how BRG Instanced Renderer organizes and renders instances internally. Understanding these concepts will help you write effective Custom Registerers and make informed decisions about chunk layout and memory.

Prototypes and Render Signatures

A prototype is the asset identity for a type of instance — the mesh, materials, LOD meshes, LOD screen sizes, fade mode, and other visual properties extracted from a prefab. Two prefabs that share the same mesh and materials produce the same prototype.

A render signature combines a prototype with render behavior configuration — shadow cascade limits, motion vector mode, density settings, crossfade duration, rendering layer mask, and so on. This is the actual registration unit. The same prototype (same prefab) can produce multiple render signatures if registered with different render settings (e.g. different shadow cascades or density modes).

When you call RegisterRenderSignatureFromPrefab(prefab), the system extracts both the prototype data and the render config (including any BRG Prototype Extra Data settings), and returns a prototype index — a global integer ID that instances use to reference their render signature.

Deduplication uses a hash of both the asset identity and the config, so identical registrations share a single index while the same prefab with different settings gets separate indices.

Chunks

A chunk is a spatial grouping of instances with shared culling bounds. Chunks are not per-render-signature — a single chunk can contain a mix of instances from any number of different render signatures. Every instance belongs to exactly one chunk, but chunks are allowed to spatially overlap — an instance can exist within the spatial bounds of a different chunk even though it only belongs to one. There is no strict spatial partitioning; chunks are simply logical groupings with a bounding box used for culling.

Chunks are the first thing the culling system evaluates. Before testing any individual instance, the system tests the chunk's bounding box against the camera frustum and cull distance. If the entire chunk is outside the frustum or beyond the cull distance, all of its instances are skipped in one operation.

This means chunk layout directly affects performance:

  • Spatially tight chunks (instances close together) cull efficiently because the bounding box is small and likely to be entirely inside or outside the frustum.
  • Spatially large chunks produce large bounding boxes that are less likely to be culled at the chunk level, meaning more instances proceed to per-instance GPU culling. This is not a major concern — per-instance culling on the GPU is fast, and processing a few extra instances has negligible impact. Chunk-level culling is an optimization, not a requirement.

When a chunk write exceeds the maximum instances per chunk, the system automatically creates overflow chunks and splits the data across them. Overflow chunks share the same culling bounds as the primary chunk, so they are always culled together as a group. This is handled transparently — queries like GetInstanceLink() and GetTotalChunkInstanceCount() aggregate across the primary and overflow chunks automatically.

Pools

Pools are the memory layer between chunks and individual instances. Like chunks, pools are not per-render-signature — a single pool can hold instances of different signatures. Each pool is a fixed-size block of 16 instance slots in the global GPU buffer.

When instances are written to a chunk, the system allocates enough pools to hold them. For example, writing 200 instances allocates 4 pools (256 slots, 200 occupied). Pools are returned to a free list when their chunk is released, and reused by future allocations.

For guidance on pool utilization, see Important Tips.

Culling Pipeline

Every frame, each camera triggers a multi-stage culling pipeline that determines which instances are visible. The pipeline runs in two phases: a CPU pre-pass on chunks, followed by GPU compute passes on individual instances.

Burst Job Chunk Culling

For each chunk, the system tests:

  1. Frustum culling — the chunk's bounding box is tested against the camera's six frustum planes. If the box is entirely outside any plane, the chunk is culled.
  2. Distance culling — the closest point on the bounding box to the camera is compared against the chunk's cull distance. If the chunk is too far, it is culled.
  3. Baked occlusion culling — if enabled, the system queries Unity's occlusion data to check whether the chunk is hidden behind static geometry.

Chunks that survive produce a visibility mask that is uploaded to the GPU for the next stage.

GPU Chunk Culling

If HZB occlusion culling is enabled, a compute shader performs a hierarchical Z-buffer test on each chunk that passed the Burst job stage. Chunks whose bounding box is fully occluded by closer geometry are culled before any per-instance work begins.

GPU Instance Culling

A compute shader iterates over the instances in each visible chunk and applies per-instance tests:

  1. Density culling — each instance has a deterministic spatial hash. If the hash exceeds the current density scale, the instance is culled. This provides stochastic thinning that is stable across frames.
  2. Screen-size culling — the instance's projected screen size is computed from its world-space bounds and distance to the camera. If it falls below the smallest LOD threshold, it is culled entirely.
  3. LOD selection — the screen size is compared against the prototype's LOD thresholds to select which mesh to render. If crossfade is active, both the current and target LOD are emitted with a blend factor.
  4. HZB occlusion culling — if enabled, the instance's bounds are tested against a hierarchical Z-buffer (a mipmap pyramid of the depth buffer). Instances hidden behind closer geometry are culled.
  5. Shadow culling — separate shadow-specific tests compare against per-prototype cascade limits and per-group shadow caster distances.

Surviving instances are sorted into draw buckets grouped by prototype, LOD level, and render pass (main vs. shadow). The system then finalizes indirect draw arguments and dispatches them through Unity's BatchRendererGroup API.

Registration to Rendering

Putting it all together, the full pipeline from setup to screen is:

  1. Register render signatures — extract prototype data and render config from prefabs and assign prototype indices.
  2. Allocate chunks — reserve chunk slots for spatial groups of instances.
  3. Write instances — fill chunks with instance data (position, rotation, scale, prototype index). The system allocates pools, packs data into GPU buffers, and computes chunk bounds.
  4. Per-frame culling — each camera triggers the Burst job chunk cull, then the GPU instance cull. Visible instances are bucketed into indirect draw calls.
  5. Rendering — Unity's BatchRendererGroup dispatches the draw calls. Per-instance transforms and properties are read directly from GPU buffers with no CPU involvement.