Unity's draw call batching (Draw call batching)

overview

To draw an object on the screen, the engine must send a drawing command (Draw Call) to a graphics API, such as an OpenGL or Direct3D interface. Because each Draw Call, the CPU needs to prepare a large amount of data, which often causes a large CPU performance consumption. Therefore, we need to find a way to reduce the number of Draw Calls.

In general, Unity has two convenient ways to reduce Draw Calls: dynamic batch processing and static batch processing.
Dynamic batch processing : suitable for objects with a relatively small number of faces, Unity will dynamically merge the meshes of these small objects together, and then draw them all at once Static batch processing
: merge the meshes of static objects into one large mesh, and then draw them all at once

Compared with manually merging Mesh, one of the biggest advantages of Unity's batch merging is that it can still remove a single object. Of course, it also has disadvantages. For example, static batch processing will increase memory consumption and require additional storage. Dynamic batch processing requires additional CPU consumption due to the need to dynamically merge Mesh.

We can switch batch processing through Unity's settings
insert image description here

Material settings for batch processing (some points to note)

Only objects with the same shader can be batched, so if we want a better batch processing effect, we need to use the same shader for different objects as much as possible.

If the difference between our shaders is only that we need to use different textures, then we can combine these textures into a large texture to meet the needs of using the same shader. The UI atlas we generally use is handled in this way, so our UI has so many drawing objects but only needs fewer Draw Calls.

If we need to modify the material in the code, we should pay attention not to use the Renderer.material interface, which will cause a copy of the material instance, and use Renderer.sharedMaterial, so that the material ball is shared.

The drawing of shadows is quite special. Even if the materials are different, they can basically be batched together, as long as the parameter values ​​​​in the materials used in the shadow pass are the same. For example, objects use different textures, but since textures are not required in the shadow pass, their shadows can still be batched (I think Unity’s documentation is a bit sloppy, and it is not clear at all. It is likely that the person who wrote the document did not understand it. The batching of shadows is still discussed in static batching and dynamic batching. It is probably useful to say this
)

When static batching is not set, dynamic batching of shadows will be attempted, but there are still limitations such as the number of vertices, as follows:
static batch
insert image description here
After setting all these objects to static, some objects that use different ShadowPass still cannot be batched
insert image description here

Dynamic batching

Unity can dynamically batch together moving objects of the same material that meet the conditions, and this batch only needs to be turned on, without requiring us to do extra work.
Of course, something that looks this good must have a lot of limitations

Limitations of Dynamic Batching

Vertex limit

Since the objects need to be dynamically merged into a Mesh, the number of vertices of the merged objects must not be too large, otherwise the performance consumption of the merge is too large, but the gain outweighs the gain. The number of vertices does not exceed 300. The number of attributes contained in the vertices does not exceed 900. For example: if our Shader uses vertex positions, normals and a set of UVs, there will be three attributes, 900/3 = 300, so the number of vertices cannot exceed 300. If our Shader uses vertex positions, normals, UV0, UV1, cut Line, a total of five attributes, 900/5 = 180, so the number
of
vertices
cannot
exceed
180

Scale does not allow negative values

After other conditions are met, as long as the Scale is a positive value, any amount can be batched, but as long as there is any negative value, the object can no longer be merged. Therefore, in order to dynamically batch, we should try to avoid
setting negative scales.
Position and Rotation will not affect it .
We can simply do a test

  1. Create 4 cubes
    insert image description here
  2. Adjust them respectively.
    The two Y axes on the left are -1
    and the two scales on the right are both positive values, but the x and y axes are zoomed in.
    insert image description here
    Let’s look at the results of batching.
    We can see that although the negative values ​​of both scales are -1, neither of them can be batched.
    Although the other two have different scale values, they are both positive, so they can also be batched.
    insert image description here

Must be the same material instance

If it is a different material instance, even if all their parameters are exactly the same, it will not be able to dynamically batch together, so when modifying the material, you cannot use Renderer.material, but use Renderer.sharedMaterial, but the batching of shadows will be special, mainly depends on whether the parameters used are
consistent

We create a material, and then copy a few copies, so that they are exactly the same, but they cannot be batched. We see that different materials are used in FrameDebug and cannot be batched. Let’s simply write a script to test and modify the material
insert image description here
.

public class MaterialSet : MonoBehaviour
{
    
    
    public bool isSetShareMaterial;
    void Start()
    {
    
    
        var renders = GetComponentsInChildren<Renderer>();
        foreach (var render in renders)
        {
    
    
            if (isSetShareMaterial)
            {
    
    
                render.sharedMaterial.color = Random.ColorHSV();
            }
            else
            {
    
    
                render.material.color = Random.ColorHSV();
            }
        }
    }
}

When we use render.material
to modify, multiple material instances will be generated, and there will be multiple colors, but they cannot be batched.
insert image description here
insert image description here
insert image description here
insert image description here
When we use
render.sharedMaterial
, there is actually only one color in the end, but they can be dynamically batched.
insert image description here
insert image description here
insert image description here
insert image description here

Do not allow setting additional lightmap rendering parameters

If the lightmap Index and Offset/Scale are set, dynamic batching cannot be performed. This setting is more of a setting after static baking, so it is more suitable to use static batching. If it is set, dynamic batching cannot be set.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LightmapCopy : MonoBehaviour
{
    
    
    public MeshRenderer meshRenderer;
    void Start()
    {
    
    
    }

    public void OnValidate()
    {
    
    
        if (meshRenderer == null)
            return;
        var myRenderer = GetComponent<MeshRenderer>();
        myRenderer.lightmapIndex = meshRenderer.lightmapIndex;
        myRenderer.lightmapScaleOffset = meshRenderer.lightmapScaleOffset;
    }
}

After we baked, we took several objects and set them to the exact same texture settings.
After baking,
insert image description here
insert image description here
we saw that they could not be batched, which means that the lightmap parameters cannot be set.

Multi-pass Shader will interrupt batching

Almost all UnityShaders support the forward rendering mode (Forward Rendering) of multiple lights, which requires multiple passes, and the DrawCall of additional pixel lights cannot be batched. The legacy deferred rendering will disable dynamic batching, because objects need to be drawn
twice

Dynamic batching on different platforms does not necessarily have advantages

Dynamic batching is by converting all object vertices into world coordinates and then sending them to the GPU, so only when the workload is small will there be an advantage compared to drawing each object. The resources occupied by a draw call are affected by many factors, the most important being the graphics API used. For example, for game consoles or modern graphics APIs like Apple Metal, the performance consumption of a draw call is relatively small, so dynamic batching has no advantage.

Dynamic batching for other renderers

Particle Systems, Line Renderers, and Trail Renderers are not composed of Mesh, but dynamically generated components with geometry by Unity. Their dynamic batching method is different from that of Mesh. phics Device) 4. For each renderer in a batch, Unity updates the offset in the vertex buffer and then
submits a new draw
call


(To be honest, the official documentation is not well written, or I didn’t learn it well. Even if there is a Chinese document, I still don’t understand it...) When we test the consumption of graphics device calls, the slowest part of rendering a component is setting the state of the material. In contrast, submitting the offset of different renderers in the vertex buffer is quite fast
.
This method is very similar to how Unity submits draw calls when using static batching.

static batch

Static batch processing can combine meshes of any size to reduce draw calls, but it must use the same material and cannot be moved. Since there is no need to convert vertices on the CPU, static batch processing is more efficient than dynamic batch processing, but requires more memory. To use static batch processing is very simple, that is, to
insert image description here
insert image description here
check the Static on the object inspection panel (the most important one is Batching Static), but after checking the position, rotation, and size cannot be modified after running, because the Mesh is merged into a static large Mesh, so we have to make sure that these objects do not need displacement, rotation or size change.

Objects to be statically batched must use the same vertex attributes, such as vertex position, vertex normal, and objects with a set of UVs can be batched, but this kind of object cannot be batched with vertex positions, vertex normals, two sets of UVs, and vertex tangents. (I always feel that Unity's documentation is not even clear about the expression. It says the same material, why do you have to talk about these things? Is there still the same material with different vertex attributes? Then it also says the same material, really drunk)

Unity cannot statically merge objects with mirrored Transforms (this is a problem).
According to my test, regardless of whether they are mirrored or not, no matter what size they are, they can be statically merged into batches... (The more you look at the Unity documentation carefully, the more problems you feel. Is it something I did wrong, or what happened?)
insert image description here
insert image description here
insert image description here

The two Cubes mirror each other in size, but they can be statically combined into batches

Using static batch processing needs to store the merged Mesh. If several objects use the same Mesh before static batching, but after the batching, the Mesh of each object is equivalent to an extra copy, because a large Mesh will be generated. Therefore, using static batch processing is not necessarily the best method. Sometimes we have to not do static batch processing, and sacrifice some rendering performance to make the memory footprint smaller. For example, in some levels with dense trees, if these trees are statically batched, there will be serious memory usage.

The internal principle of static batch processing is to convert static objects to world coordinates and build a common vertex and index buffer for them. If we enable the Optimized Mesh Data option in PlayerSettings, Unity will remove unused vertex attributes when building vertex buffers (must be that all Shader variants are not used). There are some special keywords (Keyword) used to do this check. For example, if Unity does not detect the LIGHTMAP_ON keyword, it will delete the lightmap UV in the batch process. Unity then performs a series of simple draw calls with little state change between draw calls for visible objects in the same batch. Technically speaking, Unity does not reduce graphics API calls, but reduces the state changes between calls, and these state changes are the most expensive parts. Static batching has a limit on the number of vertices and indexes. On most platforms, it is 64k vertices and 64k indexes. The index limit on OpenGLES is 48k, and the index limit on macOS is 32k.

Currently, only Mesh Renderers, Trail Renderers, Line Renderers, Particle Systems and Sprite Renderers can be batched together, that is to say, Skinned Meshes, Cloth and other types of rendering components cannot be batched together.
Batching can only be done between the same Renderer

For transparent objects, for the sake of transparency, these objects are rendered in order from back to front. Unity will first sort them in this order, and then try to batch them, but because of the correct rendering order, there are fewer transparent objects that can be statically batched than opaque objects.

Manually merging closer objects is a better alternative to merging DrawCall. If the function allows, manually merge all objects that can be merged into a Mesh, such as a static cabinet with many drawers. These drawers can be merged with the cabinet into a Mesh. We can manually merge through other 3D software or through Mesh.CombineMeshes.

my summary

Unity’s batch batch description can only give a general idea of ​​what limitations and how to use it, but the Unity documentation doesn’t make it clear. Either they are lazy, or the people who wrote the documentation don’t know it themselves. So if you really want to optimize batch batching, you still have to practice it yourself in the project. The most important tool is to analyze it through FrameDebuger. There will be specific reasons for not batching batches, which is very practical.
insert image description here
insert image description here

Unity version

Unity2020.3(LTS)

This article

https://blog.csdn.net/ithot/article/details/122262314?spm=1001.2014.3001.5502

This article test project

https://github.com/MonkeyTomato/DrawCallBatchingTest

reference

This article is mainly based on the official documentation of Unity
https://docs.unity3d.com/Manual/DrawCallBatching.html

Guess you like

Origin blog.csdn.net/ithot/article/details/122262314