8 Race cars cause big draw calls?

Hi, everybody.

I have a big problem and I don’t know how to solve them.

I have 8 simple 3d low poly race cars in the scene.

All colors on the car (car body, tires, wheel rims, brakes, front lights) are normal diffuse colors.

When I start the scene, I change programmatically the material color of the 8 car bodies and the wheel rims.

In the scene, one car makes 19 Draw calls. When I set the other 7 race cars visible the draw calls are 152.

I don’t understand it, because the are all using the same materials. I only change the colors.

How can I optimize that? It’s a game for mobile devices.

I know, that I can combine meshes to shrink the draw calls. But not with cars. They are not static and the are moving.

Thanks for helping !

When you access a material via meshfilter.material, unity will instantiate a unique copy of that material so it can have different properties from the other versions of that material in the scene. If you access it via .sharedmaterial, it will not duplicate the material, but will cause all cars to have the same colors.

You can actually combine the meshes even though they are moving; it’s just more work. I believe a Unity2015 talk actually did this for your exact case (a racing game).

You can also use MaterialPropertyBlock to speed some of this up:

1 Like

Some ideas.
=> If one car makes 19 draw calls, 7 cars should be 19 * 7 = 133 draw calls. So first thing you should find out what exactly the extra draw calls come from.
=> Even if all cars use the same material, in fact they are different material instance. Because they have different uniform value, it is the color value in your case.
=> So in this case, you can NOT batch them together.

Possible solution:
=> Use different textures instead of different uniforms.
=> Atlas all textures into a single large texture, prepare for batch.
=> Batch all vertex of cars at runtime, maybe unity’s dynamic batch will rescue.

1 Like

@jbooth_1 : Do you have the link for the unity talk?

The draw calls coming from the objects. For example the tire object, wheel rim object etc.

I tried the “MaterialPropertyBlock” without success.

void Start () {

        GameObject car = Instantiate(opponentPrefab, new Vector3 (0, 0, 0), Quaternion.identity) as GameObject;

        MaterialPropertyBlock props = new MaterialPropertyBlock();

        // 3d car model. Objects with mesh renderer are in the children.
        MeshRenderer[] mr = car.GetComponentsInChildren<MeshRenderer>();
        foreach (MeshRenderer mrChild in mr) {
            mrChild.GetPropertyBlock(props);
            props.Clear();
            //props.AddColor("_Color", someColor);
            mrChild.SetPropertyBlock(props);

        }

    }

Draw calls before MaterialPropertyBlock: 14
After: 14

Nothing changed.

Using material property blocks is not going to fix the draw calls of a single vehicle because a) the car is already multiple materials and using the material property block doesn’t change that and b) each part that has a separate material is also considered a unique mesh so that’s going to to be separate draw call even if you set the exact same material to every part.

Shadow casting will also add additional draw calls, one for each material per shadow depth texture. For a directional light that’s one to four extra draw calls depending on the number of cascades. Spot lights are one more. Point lights are potentially 6(!) more.

Shadow receiving adds one more draw call per material.

If you have any camera post process effects that might add one or more draw calls per material again.

Let’s say you have a single cube with shadow casting and receiving turned off and with a single material index. That’s 1 draw call.

Now you have another non shadow casting / receiving cube but with a different material per face. That’s 6 more draw calls.

Now you have a copy of the previous cube, but all 6 material indices are set to the same material as the first cube. This cube looks identical to the first cube that only has 1 draw call, but because it’s actually 6 material indices it’s still 6 more draw calls.

Now let’s make a few copies of the first single material cube. If batching is working properly there will be no additional draw calls! Now if we run a script on these new boxes that changes the color of the material on these boxes to something random using renderer.material.SetColor() they will be additional draw calls. What’s more if you were to set them all to the same color they’d still each be additional draw calls. Modifying the material in Unity is “dumb” in that it’ll simply make a new material instance regardless of if there’s another material with the same values that already exists.

Using material property blocks instead of modifying the material may allow Unity to be “smart” and combine objects of the same material, property block settings and mesh. It should also be capable of combining the same material and mesh with different property blocks but I’ve never seen Unity actually do this.

You can also manually use one material on all objects of a specific color. Just make a new material in the editor with the color you want and instead of doing material.setcolor do material = material asset.

Ultimately you’ll need to figure out how to make a single car be fewer draw calls and assume every car will add that many more draw calls. I suspect something like the wheels on your first car are being batched together but subsequent vehicles are not because it’s creating unique material instances for each rim.

TLDR: A draw call is every combination of material instance and mesh material index. Batching only occurs if two (or more) objects are literally identical combinations of the same material and mesh index. And two materials with the same color are still two materials.