The Problem
Unity's static batching not always work very efficient. For example, even if meshes have the same material, they will break into separate batches if:
- transform is scaled by negative value (i.e. -1 for mirroring)
- vertex format mismatch (uv channels count, vertex colors etc.)
- batch exceeded 64000 vertex limit
Also there might still be many separate draw calls hidden under the "Saved by batching" statistics, which might be a problem for low-end devices even if batches count is low.
Our levels consists of many props and constructor parts like this:
which is good for flexibility of editing but not that good for rendering.
The Solution
My solution consists of 2 parts
- Mesh Combine Groups
- Batching Preprocessor
Both scripts runs automatically at build time or in the editor before entering play mode, so it does not affect level art pipeline. For this I use [PostProcessScene(-1)] callback with negative priority, so it's called before Unity's static batching.
The first part is Mesh Combine Groups which are mainly aimed at reducing the number of draw calls by combining grouped geometry into single mesh with the same material. Level artist only need to group objects that are close to each other and assign script to the group, like this:
The second part is Batching Preprocessor, which is a bit less efficient but still useful.
- It applies transformation matrices to the meshes that have negative scale
- Splits geometry by materials if object uses multiple materials
- Checks all the meshes with the same material for vertex format mismatch and logs warnings if there are any (this usually happens by accident on model export)
- Manually creates batching groups for all the meshes sorted by materials using StaticBatchingUtility.Combine
And here's the final result of both scripts:
So it's about 40% less batches (73 > 44) and almost 10x less draw calls (1071 > 121) which gives noticeable performance boost on low-end devices.