I’ll start by sharing my PC setup so you know that’s not the issue:
CPU: i7 2700k
GPU: GTX 1060
RAM: 24gb
So I have a basic scene. I have a 10’x10’ plane (imported from Blender), it is 1 quad (2 tris). I placed a collider on it with a 2k texture(w/ PBR maps). On start, I instantiate and array of the plane prefab (200x200). Total of 20,000 prefabs. They are all set to static and I combine them using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateGround : MonoBehaviour
{
public int gridSize = 10;
public GameObject plankPrefab;
private List<GameObject> allGameObjects;
void Start()
{
for (int x = 0; x < gridSize; x++) {
for (int z = 0; z < gridSize; z++) {
if (x + z != 0) {
Renderer gameObjectRenderer = plankPrefab.GetComponent<Renderer>();
Vector3 position = new Vector3(gameObjectRenderer.bounds.size.x * x, plankPrefab.transform.position.y, gameObjectRenderer.bounds.size.z * z);
GameObject newPlank = Instantiate(plankPrefab);
newPlank.transform.position = position;
newPlank.transform.parent = transform;
}
}
}
StaticBatchingUtility.Combine(gameObject);
}
void Update()
{
}
}
This is what the Profiler says (I don’t really know how to dissect this):
They’re not merged. You are still brute force drawing everything and HDRP is struggling to batch them for you (assuming the experimental batcher in the HDRP config asset file is enabled).
Also static batching is for huge chunks of geo (on non-instanced or old hardware), not for a lot of quads. The absolute worst-case scenario for static batching is what you just did.
What happens in this case is static batching will perform the same as not batching them at all, and really, static batching + dynamic batching are both redundant with HDRP anyway (it’s own batcher is by far more efficient, assuming gpu instancing).
If on HDRP:
don’t use dynamic or static batching - both are more expensive than instancing
enable the SRP batcher
merge your meshes properly
If merging meshes isn’t something you want to do, use DrawInstancedIndirect to draw 20,000 quads. It should not take long to render in that case (60fps on a decent mobile would be realistic).
Yeah although I really need Unity to start putting good practises info out better because everyone still, a decade after static batching was added, don’t realise that it performs badly with lots of little meshes. Pretty much everyone got that wrong. Also, dynamic batching consumes large amounts of CPU time that is (in my testing) always slower than just using GPU instancing tickbox on material.
In any case, there are even faster ways to do what you want but if it’s sufficient now, who cares?
I use the HDRP Lit shader and enabled GPU instancing for objects copied and scaled - rotated around, but the batches are still enormous (like 3000) and "saved by batching’ metric reports a minus -2000, is this meaning the batching is working ? The objects are in constant motion.
This is a necro of an old thread, but in any case, if the meshes are close by, you might as well merge them before hand.
Static batching does merge the meshes, but it also keeps the visibility bounding boxes and vertices for each one, so it can perform culling and turn on/off parts of the mesh.
And if you use tons of small meshes, which results in a lot of data that culling has to go through, “just render all of them” quickly becomes faster than “try to figure out if you can turn off a few”.
I just do the placement in my DCC software directly these days. (copy paste the tris).
If you already have a placement you like, you can select all meshes, export as FBX (with Unity’s FBX exporter, although not the latest one, because the latest one does not work), open in Blender/Modo/Whatever, merge them all in one mesh, then use that instead of the many meshes.
It would be nice if Unity had an easy way to merge meshes at build time, since doing the above, loses you some flexibility in easily updating the models at a later date. It’s supposedly scriptable with Mesh.CombineMeshes ( https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html ), but I have never gotten a script that uses that to work properly, but maybe it’s just me.
What happens when you use lots of small objects with static batching is it will consume much more memory, do more work etc, culling still has to be done. When you have lots of small things pre-merged it culls the one merged mesh obviously, a big CPU saving. Static batching is an old technology, I don’t see the point of it, it should be replaced by HLOD.
The stats window is broken especially with SRP. For a confirmation you need to use the frame debugger.
HDRP lit shader is already batching, it’s not using “gpu instancing” on the material. Instead it uses SRP batcher (if enabled). Both cannot run at the same time and SRP batcher is higher performance, so Unity ignores the material setting in this case.
But for speed, see drawmesh instanced indirect for fastest, then drawmesh instanced and then SRP batcher, then regular GPU instancing option in that order for meshes which should remain separate.
All of this is solving dynamic stuff though. GPUs prefer big beefy jobs with no interruptions where possible.
In addition to the frame debugger as @hippocoder mentioned, there is an SRPBatcherProfiler.cs script in a github link mentioned in this blog post which you can use to see the performance of the SRPBatcher.
In HDRP 7.3.1, the SRP Batcher is enabled by default. It can be toggled by clicking the 3 vertical dots in the upper right corner on the Inspector of the HDRP Asset, then enable the Debug window.