Unity Performance optimization for Designers Part-1

Reducing Draw calls in Unity.

Soumya Ranjan Sahoo
8 min readSep 17, 2020
Photo by Chris Liverani on Unsplash

Saving rendering cost while designing a level early on, will save you time in optimization and frustration later in game production. The better you know how and what causes these bottlenecks the easier it becomes to track down the unknowns.

In a game code, physics, assets, memory management, draw calls, etc can cause performance issues and FPS drop. If any of these issues happen that commonly affect the FPS, scene load time, memory consumption, device overheating, etc.

For this discussion, we will mainly focus on draw calls. The simple concept is if your draw call is high your GPU & CPU are busy with all those drawing and FPS goes downhill. Draw calls are easy to fix if you get the fundamentals right.

First we have to understand how to get the relevant performance numbers and what each numbers mean, then how to improve those numbers. The topics will be covered:
How to tracks the FPS and performance parameters?
What is the Baseline Target Performance to aim for?
What are the different terms?
Ways to reduce the draw calls ?
Test it until you are sure.

Note: If you already know all the concepts then you can directly skip to the “Ways to reduce the draw calls ? section.

How to track the FPS and performance parameters?

You can get the stats easily in Unity in the game view window under the stats tab. The main parameters to track is the FPS(obviously), Batches, and SetPass calls. I will explain these in a bit.

Stats tab
Profiler Rendering data

The Rendering data in the profiler gives you a more detailed overview of where your draw calls are spent and how well they are batched.

Also, you can use Frame Debugger to exactly know how the draw calls are being rendered with what objects on the display by GPU. But I will cover that in more detail in another discussion.

What is the Baseline Target Performance to aim for?

When designing a level set a performance target baseline for the scene based on the hardware you are targeting. This sets a clear target and reference for the scene you are designing. You can easily find it in the hardware vendors doc or Unity forums game dev discussing the same.
Ref: https://developer.oculus.com/documentation/unity/unity-perf/ [Baseline Rendering Target for Oculus VR]

When you are tracking these parameters for a scene some are calculated per frame. So if it is mentioned for PC VR the triangle count is 500k-1M which means it is per frame with all the draws on how many triangles are rendered in that frame.

What are these different terms?

It took me a good amount of time to understand what the terms are in Unity an how it optimizes rendering.

Draw calls

This is the call to the GPU to draw an object on the display. If there are two objects with two materials then, GPU is called twice to draw them. If one object with one material then it is one draw call for the object. One object two materials are two draw calls for the same. Refer to image (Ref1.1).

Set pass calls

While creating a Unity shader, you can have one pass or multiple passes depending on the material effect and complexity. Example: If you place an object’s one half behind the wall and the other half visible, but you want to have the hidden part to be visible as a transparent custom depth then it will have another pass so two set pass calls.
More set pass calls mean more calculations for CPU unless you reuse the materials. By default, each different material in the scene has at least one set pass call. But if one material(same parameters) has been reused on multiple objects then it’s one set pass call as the material is just calculated once. If your scene is big and has a lot of objects not optimized well then this number can easily shoot near 1000.

Batches

The concept of batching is simple and smart. Batching static objects means similar objects which share the same material will be grouped and submitted as a single object to be drawn by GPU. Rather than drawing them individually, they are drawn now as a single object So we save time on those draw calls. Yeaaa!!!

If you have 3 similar objects with the same material in view. If they are not static batched then it is 3 draw calls. Now if they are batched then unity only batches them together and then draws them in one go resulting only one draw call.
Ref : https://docs.unity3d.com/Manual/DrawCallBatching.html?_ga=2.94713564.1914214501.1598674954-7759892.1598674954 [Will delete]

Batches Vs Draw Calls

A batch is a group of draw calls that will be drawn together by the GPU. If all objects are not batched then it has at least one draw call with one batch for each. If objects can be batched together to one batch then for them the draw call is reduced and not one for each now. (Drawcall >= Batches)

So in the scene try to decrease the batch size which will ultimately decrease the number of batches. Another way to look at batch is by making groups of objects render together. So more efficiently you let unity group objects the easier it becomes for the CPU & GPU to draw them.
Ref: https://support.unity3d.com/hc/en-us/articles/207061413-Why-are-my-batches-draw-calls-so-high-What-does-that-mean-#:~:text=A%20draw%20call%20is%20a,each%20object%20inside%20the%20batch.

Note

Our main aim will be to reduce the number of batches first which will ultimately bring down the draw calls and set pass calls. Once both are down and you still see more set pass calls then you can further optimize it.

Ways to Reduce draw calls?

Before explaining the way I approach this is that all these optimizations should still maintain the quality-looks of the level I originally aimed for. My approach with all optimizations has always been incremental so that I can keep track of the looks.
Note: Important to remember is the total number of vertices per frame rendered is also dependent on the draw call.

Make texture atlas for objects

If your object has more than one material with respective textures then consider making a texture atlas and using a single material instead. For example, you have a vending machine with 4 to 5 materials then combine your textures for each material into one then UV map them correctly and use in a single material for the whole object. The draw call comes down from 5 to 1 now.

Merge objects close together

This is a very common optimization in both Unity and UE. If you have some static objects placed together nearby and they are not supposed to move or to interact in the game then you can combine them to one mesh and then use a new single combined material with a texture atlas to reduce the draw call to one. There are some tools you can use for merging to avoid manually doing this.

Make static batching

This is the one that took me some time to understand in unity. Internally unity groups and bakes the respective static batching models with shared materials into a combined mesh and renders them in one go thus saving more draw calls. It consumes comparatively more memory. Another advantage is you do not have to merge objects and they are still culled individually. If you have set up everything correctly then unity takes care of static batching automatically. To enable static batching enable the same on similar objects with shared material or the same material.
Also, enable static batching in Project Settings > Player Settings.

Static batched Ref:1.1
Stats tab

Here the left three ducks are static batched and the right three are not batched.
1(background) + 1(3 static ducks) + 3(3 non- static ducks) =5 draw calls in total.
As you can see if we batch all of them now then we will have 1 draw call for the 6 ducks rather having 6 draw calls, like one for each.

Make dynamic batching

For dynamic objects, unity does batch dynamically at run time. For this to work enables the GPU instancing in the material of the non-static object.If you try putting a GPU instanced material on a static object then unity shows warning in the inspector. This batching is always at the run time calculated by CPU before GPU draws them, so not ideal to use this where static batching can be done if your objects can be static. It might be also less intensive so better to always check the performance before taking a call.
In the below image as the objects are spawned when paused and on checking you an see all the duck objects use one drawcall.

Dynamic batched

This is limited to small meshes, as batching larger meshes dynamically is more expensive than not batching them.

Merge Similar Objects with overlapping UVS

This is a pretty cool trick which I got to know from one of my designer friend. So you basically overlap the UVs of similar objects placed together and then merge them in a blender. Then you use a single material with a texture to render all objects in the scene. This texture is mapped to the overlapping UV region for the objects. Image: [Must Image]

Reducing SetPass calls

As we discussed earlier more SetPass calls mean more calculations for CPU. In general, each object having simple material will contribute to one setpass call.
The idea is to reduce SetPass calls by reusing the same materials. Also, complex materials cause more SetPass calls per material so plan accordingly their placements if there are many.
Example:
If you have one pass call for 100 similar objects, on the other case you have one pass call for each for 100 similar objects then in the 1st case it will be obviously faster.

Finding the balance between all these methods is very important to optimize the rendering for the whole level. And as you keep implementing these and get used to, you start optimizing better by default.
Other things like postprocess, camera effects, shadows, lighting, and lightmaps will also cause in draw calls, which we will cover in some other discussion. But keep these things in mind while designing and testing a level.

Test it till you are sure what’s happening

One easy way to test each of these features is to setup an empty scene. In lighting make the Environment Lighting Source to Color instead of the skybox. Make your camera Clear Flags to Solid Colors. Disable all post process and directional light if any. Now in the stats tab, you will have only one setpass call and one batch. Now as you add objects with materials and enable the batching features and optimize it you can track the change in the values.

--

--