MaterialPropertyBlocks and Static Batching

Or, how I wasted 2 days wondering why my MaterialPropertyBlocks weren't working only to discover the fix was to disable Static Batching by unchecking a single checkbox.

NB: Written for Unity 2020.3 using the Universal Render Pipeline

Why No Work? 😭

Static Batching is where Unity automatically combines multiple meshes into a single mesh behind the scenes, and renders them all at once. This is sometimes faster, but requires them to all be rendered with the same properties.

MaterialPropertyBlocks are a way of supplying different material properties per object, e.g. a different colour, without having to create a unique material each time.

Or to put it another way. MaterialPropertyBlocks are an optimisation for supplying different properties to each object. Static Batching is an optimisation for rendering multiple objects with the same properties.

Do you see how these two approaches are not compatible?

Unity actually give the following hint, but until today I didn't understand what it meant and my eyes just glossed over it.

Picture of warning text on mesh renderer when you have an instanced material. The warning Reads "This Renderer uses static batching and instanced Shaders. When the Player is active, instancing is disable. If you want instanced Shaders at run time, disable static batching."

One thing that added to my confusion was that the MaterialPropertBlocks appeared to be applied correctly by our Edit Mode tools, it was only when entering play mode that the Static Batching kicked in and the issue became visible.

My inability to read what is infront of my eyes, plus a lack of understanding of Unity's rendering terminology is why I am here, 2 days later, kicking myself, writing this blog post.

How Fix? 👷

If you want to apply a MaterialPropertyBlock to an object, you must make sure it does not have Static Batching enabled.

You can change an object's static flags from the inspector or from code.

Disable Static Batching from the Inspector

Click the arrow next to the word Static to reveal all the different types of static. Here you can choose which types you do and don't want individually. In my case I want everything except Batching Static.

Screenshot of Static drop down in inspector with Static Batching is disabled

Disable Static Batching from code

// Setup your static flags, in my case everything except BatchingStatic

var staticFlags = ~StaticEditorFlags.BatchingStatic;

foreach (var go in myGameObjects)
{
    // Apply your static flags to each game object

    GameObjectUtility.SetStaticEditorFlags(go, staticFlags);
}

Alternative Ways To Disable Static Batching

If you want to learn more about how both static and dynamic batching work, and how and when to use them, read the docs.

Other Trivia

If you know what instanced rendering is, you may be wondering if MaterialPropertyBlocks are related to that. The answer is yes, they are related. However although MaterialPropertyBlocks are compatible with instancing, instancing is disabled by default and has to be explicitly enabled per material.

Screenshot of Enable GPU Instancing checkbox in the material inspector

AFAIK, if you are using MaterialPropertyBlocks you almost certainly want to Enable GPU Instancing, in addition to disabling Static Batching.

And with that, happy coding!