How Toca Boca built a high-performance, scalable rendering backend

BERTRAND GUAY-PAQUET / TOCA BOCASenior Graphics Engineer
Mar 20, 2026
Toca Boca

To meet the performance demands of our ambitious mobile 3D multiplayer game, Toca Boca Days – especially on lower-end devices – we needed to innovate. The game (now on hold) featured expansive levels and supported up to 32 players with highly customizable avatars.

In late 2022, we launched our Batch Renderer backend system, built on Unity’s BatchRendererGroup API. This new system delivered significant performance gains, which we’ve continued refining.

In this article, I’ll share insights into the system’s architecture as we prepare to use it in our upcoming game.

Data architecture overview

At a high level, our system divides the data for a GameObject and its Renderer component into three layers.

Layered data architecture
Layered data architecture

In the first layer, each Renderer component corresponds to a single culling instance. This layer contains all the data required to determine visibility based on the camera’s culling planes, layer mask, and any parent LOD group.

The second layer represents each Renderer’s material(s), where every Renderer-material pair forms a draw instance. Draw instances store rendering-related data, such as the mesh and material.

The third layer consists of BatchRendererGroup batches (hence the name Batch Renderer) and their instances. Each batch has its own GraphicsBuffer containing built-in per-instance properties, such as the ObjectToWorld matrix, along with an optional set of per-instance material properties.

A Renderer’s set of per-instance material properties determines its batch type.

Toca Boca Days | Toca Boca
Lots of books with per-instance material property values

Our system stores all CPU-side data in a compact, data-oriented structure of arrays (SoA) layout. It relies on blittable types within native containers wherever possible, enabling the use of high-performance Burst jobs for tasks such as instance culling and draw command generation. This architecture allows the system to efficiently handle large numbers of instances.

Per-instance material properties

To assign per-instance material properties to a Renderer, we add a component that references a “property set” ScriptableObject asset. Property sets should be limited in number, as each defines a separate batch type – instances from different batches aren’t rendered together.

For easier authoring, we provide a component to set initial instanced property values, along with a custom Inspector window. Without per-instance properties, these values would need to be set on unique materials, reducing batching efficiency.

Per-instance material properties inspector
Per-instance material properties inspector

Skinning and skeletons

In Toca Boca Days, avatars featured extensive character customization. To support varied clothing and accessories, we split the skinned portions of each avatar’s mesh into 17 Skinned Mesh Renderer components (e.g., left upper arm). Most clothing items add additional skinned Renderer components, which can quickly accumulate as player counts increase.

Toca Boca Days | Toca Boca
Toca Boca Days avatars

Because our avatars are complex, we avoid using them for shadow maps, which would be expensive. Instead, we use blob-like shadows, and in most cases, avatars are rendered only once per frame. This means we cannot amortize Unity’s built-in vertex skinning cost across multiple draw commands for a single mesh.

We instead handle skinning directly in the vertex shader. All skinning matrices are stored in a single global GraphicsBuffer, and each Renderer receives an offset into this buffer to adjust per-vertex bone indices and fetch their corresponding skinning matrices.

The skeleton offset is stored as BatchRendererGroup instance data, allowing us to batch draw calls across characters that share the same mesh (e.g., all right upper arms together), improving performance.

Calculating skinning matrices normally requires renderer count × average bone count per renderer computations. To reduce this, we combine two strategies:

Ensure all skinned Renderer components in an avatar use consistent bone indices – for example, bone index 2 always refers to the chest, even for a right-foot Renderer that doesn’t reference it.

Preprocess meshes to identify unique sets of bone indices and bind pose matrices, ideally resulting in a single set per avatar.

Skeleton rig pre-processor component
Skeleton rig pre-processor component

With this approach, we can consolidate the skinning of all an avatar’s Skinned Mesh Renderer components into a single “animation skeleton” and “renderer skeleton” pair.

Animation and renderer skeletons (showing two renderer skeletons)
Animation and renderer skeletons (showing two renderer skeletons)

On each frame, our skeleton manager performs the following steps:

Animation extraction: Animation jobs pull the current bone transforms from the animation skeleton via the Playables API (Mecanim).

Pose computation: Burst jobs generate animated pose matrices from the bone hierarchy and extracted transforms.

Skinning matrix assembly: Burst jobs combine animated pose and bind pose matrices into skinning matrices.

GPU upload: The new skinning matrices are uploaded to the global GraphicsBuffer using a compute shader.

This process is complex but highly efficient, reducing skinning matrices by a factor of two for a naked avatar and by a factor of four or more when clothing is applied.

Looking ahead

Developing a custom rendering backend for Toca Boca Days and now modernizing it for Unity 6.3, has proven worthwhile. The upgrade from Unity 2022.3 was smooth, and our Batch Renderer system continues to deliver strong performance for games with extensive character customization and per-instance material properties.

Having full control over draw command generation via the BatchRendererGroup API enables tailored culling strategies – such as occlusion portals and custom occlusion culling – and animation optimizations, like throttling skinning matrix updates based on camera proximity and importance.

Unity’s extensibility, combined with performant out-of-the-box solutions like Universal Render Pipeline (URP) and the GPU Resident Drawer (with source access), allowed us to achieve our performance goals while maintaining a highly optimized, custom rendering pipeline.

Toca Boca Days | Toca Boca
The Batch Renderer will be back

Toca Boca is developing a yet-to-be-announced game; follow the studio on Instagram, TikTok and YouTube for updates. Explore more stories from mobile Unity developers on our mobile solutions page and our Resources hub.