Batch of Unity3D UGUI series


1. What is UGUI batching

1.1 Preparations

Before the official start, let's do a preparatory work: create a new scene, then delete the parallel light that comes with it, and change the Clear Flags of the camera to Solid Color.
initial scene
At this time, open the Stats panel in the Game view, and you can see that the number of Batches is 1.
(How to look at the parameters on the Stats panel, and what is Batches, please refer to the previously written blog " Stats Statistics Panel for Unity3D Client Project Optimization Summary " " Static Batching for Unity3D Client Project Optimization Summary ")
Initial scene Stats panel

1.2 Batch processing

Before talking about the batching of UGUI, let's take a look at what batch processing is.
Before we talk about batch processing, let's take a look at how an ordinary 3D model is rendered.
① First, the CPU side prepares the grid, textures and Shaders used for this model, and then the GPU loads the grids, textures, and Shaders into the video memory.
②Then the CPU sets the rendering state.
What is setting the rendering state? It is the CPU setting which Shader to use when rendering this grid, and which textures to use. In step ①, we may prepare a lot of Shaders (such as Shader1, Shader2, Shader3), and many textures (texture 1, texture 2, texture 3). The function of setting the rendering state is to tell the GPU, and then you render The Shader used in this grid is Shader1, not Shader2 and Shader3, and the texture used is texture 1 instead of texture 2 and texture 3.
That is to say, the CPU is the boss, and the GPU is the younger brother. The boss says that you will use this Shader and this texture when you render the model next time, and then the younger brother will follow the boss’s request when he starts to work.
③ After the CPU has set the rendering state, the GPU has not officially started rendering the model, but waits for the CPU to give orders, and the CPU tells the GPU that "you can render this model", and then the GPU starts to actually render the model according to the Shader and textures set in ② model, and pass the rendered result layer to the screen. The process of the CPU telling the GPU "can render this model" or this command is called Draw Call (the Batches we see on the Stats panel are actually the number of Draw Call calls).
As can be seen from the above process, each 3D model should go through a complete step ①②③ to be rendered. That is to say, if a model is to be rendered, Draw Call should be called once. For example, if there are 3000 models in our scene, then the Draw Call should be 3000, but when we look at the Stats panel, we will find that there are not so many Draw Calls (the Batches value on the Stats panel). Why is this so? Because Unity does batch processing.
Batch processing is to combine the grids of 3D models that use the same material (Shader) and the same texture during rendering to form a large grid, and then call Draw Call again to directly render this large grid.
Therefore, it should be noted that only models with the same material and the same texture can be batch processed . If the material or texture used by a model is different from other models, then the CPU has to perform step ② to set the rendering state separately, and then you have to Carry out step ③ to call Draw Call separately.

1.3 Significance of batch processing

It can be seen from the above analysis that the significance of batch processing is to reduce the calls of Draw Call.
Because the CPU needs to prepare data and set the rendering state before calling Draw Call, and preparing data and setting the rendering state is particularly time-consuming! If there are too many Draw Calls, the CPU will spend a lot of time preparing data and setting the rendering state, causing performance problems.
For example, we moved a folder containing 1024 small 1kb files much slower than moving a 1Mb file. Because the computer has a lot of extra operations when moving files, moving multiple small files is more time-consuming than moving one large file.
Then rendering can also be understood in this way, two options:
① CPU asks GPU to render 1000 small triangles
② CPU first merges these 1000 small triangles into a large grid, and then asks GPU to render this large grid
Which is faster? Of course it is ②. Because ① the CPU needs to notify the GPU 1000 times, and it takes time to prepare data and set the rendering state each time, while ② the CPU only needs to notify the GPU once, and only needs to prepare data once and set the rendering state once. That is, the Draw Call of ① is 1000, and the Draw Call of ② is 1.
For ordinary 3D models, Unity internally performs static batch processing and dynamic batch processing.
The advantages, disadvantages and limitations of static batch processing and dynamic batch processing can be viewed in the previous blog, " Stats Statistics Panel of Unity3D Client Project Optimization Summary " " Static Batching of Unity3D Client Project Optimization Summary ".

1.4 Batch of UGUI

From the above we know how a 3D model is rendered. Is there any difference between UGUI rendering and 3D model rendering?
The answer is no different.
The UGUI control is also a grid in essence . The only difference from the 3D model is that the grid of the 3D model is modeled and built in 3D Max or Maya, while the grid of the UGUI control is automatically created in the control code code. of.
For example, if we create an Image and a Text, and select the rendering of the Scene view as a wireframe, we can see that both the Image and the Text are grids.
Grid for Image and Text
You may wonder how the grid of Text is just a rectangle, how to render such complex characters? In fact, the font we use on Text is essentially an atlas. Rendering a word is just rendering the picture on the atlas corresponding to this word. It is not much different from ordinary Image rendering in essence. The difference is that there are additional modules to process it. The font atlas of Text corresponds to the character.
Since the controls of UGUI are all grids, it should be possible to perform batch processing, right? Yes! Batch processing of UGUI controls is called batching of UGUI.
The batching of UGUI is to merge the grids of UI controls that meet the batching rules under a certain Canvas into a large grid, and then merge these grids together, call Draw Call once, and then submit a GPU for drawing.
So how do you meet the batch rules? According to the definition of batch processing, batch processing can be performed as long as the materials and textures used by the two meshes are the same.
However, there are other rules for UGUI batching. It is not enough just to satisfy the same material and texture . What kind of rules are there? We will have a section on this later.

2 Use of analysis tools

2.1 Use of Frame Debugger

So the question is, can the default Image and Text we created be batched together?
What are the basic conditions for batching? The material (Shader) and the texture must be the same, so let's see if the default Image and Text just created are satisfied.
The Shader used by both is the default UI/Default.
Default Shader for Image and Text
Are the stickers the same? Our Image here does not have a specified texture, and it cannot be seen directly in the Inspector panel. You have to go to the Frame Debugger to see it.
The function of the Frame Debugger is to facilitate us to see how the frame of the screen is drawn step by step when we click Enable. That is to say, through the Frame Debugger, we can know what was drawn first, then what was drawn, and what was drawn last.
The Frame Debugger panel path is located in Window 》Analysis 》Frame Debugger. (The Unity version I use is 2019.3.15, the lower version of Unity path may be different)
Frame Debugger and Profiler panel paths
Open the Frame Debugger window.
Frame Debugger window
Then click Enable, Frame Debugger will show how the current screen is drawn step by step.
Basic use of Frame Debugger
The left side of the Frame Debugger is a tree structure. From top to bottom, it indicates the order of drawing content. The upper ones are drawn first, and the lower ones are drawn later. The root node of the tree is generally Camera.Render, which indicates how the picture seen by a certain camera is drawn step by step.
Since the Render Mode of UGUI's Canvas here is Screen Space - Overlay mode, this mode is to draw the content of UGUI after all cameras are drawn, so there is also a UGUI.Rendering.RenderOverlays under Camera.Render. UGUI.Rendering.RenderOverlays indicates how UGUI is drawn step by step.
FrameDebugger basic tree structure
Select an item, and the Game view will display the current screen of the currently selected item.
We click on Drawing under Camera.Rendering, and we can see that the Game view turns into a solid color. Why is this happening? We look at the tree structure in turn and find that Clear (color+Z+stencil) is actually executed at the end. Clear means clear. The contents in the brackets are the color buffer, depth buffer and stencil buffer. That is to say, under the step of Drawing, the color buffer, depth buffer, and stencil buffer are cleared. Since it clears the color buffer, the whole frame becomes the color set by our camera.
clear screen
After Clear is completed, then Camera.ImageEffects is executed, which means the post-processing of the camera screen, that is, after all the content seen by the camera is drawn, the image of the camera is processed, and the post-processing of the screen can achieve some special effects.
We create a camera, which has HDR and MSAA effects turned on by default, so there will be one more Camera.ImageEffects step here. (What exactly are HDR and MSAA, I won’t go into details here)
insert image description here
If we turn off HDR and MSAA in Camera, Camera.ImageEffects will no longer be called. If you don't use HDR and MSAA, you can turn them off, which is also an optimized place.
Effect comparison before and after HDR MSAA is turned off
After the camera is drawn, the UGUI is then drawn (UGUI.Rendering.RenderOverlays).
When drawing UGUI, the stencil buffer is first cleared. Above we see that Camera.Rener has cleared the stencil buffer in turn. Why is UGUI cleared again here? It is because UGUI's Mask (mask) control will use the template buffer to achieve the mask effect. (Of course, we do not recommend using Mask to implement masking, because it will add at least two Draw Calls, which we will talk about later.)
The drawing process of UGUI
After clearing the stencil buffer, we start to draw our UGUI control. Remember the essence of UGUI we said above is a grid? So drawing our Image and Text here is actually two Draw Mesh (drawing grid). The rendering engine does not care whether you are a picture or a text. From the perspective of the rendering engine, all UGUI controls are all grids.
We click on the first Draw Mesh, and we can see that the Image is drawn first, and the Shader and its textures used to draw this Image are displayed on the right. We can hold down Ctrl and click the texture box after _MainTex to preview the texture used by this Image.
FrameDebugger draws the Image first
Then we click on the second Draw Mesh to see the drawn Text. But why the size of the font texture seen from the Frame Debugger is 0×0, to be honest, I don’t understand it at all.
UGUI draws Text for the second time
After the Text is drawn, our entire scene is drawn.
Careful students may ask, isn’t our Text on the Image in the scene? Should we draw the Text first and then the Image? Why is the Image drawn first in the Frame Debugger? This involves the batching rules of UGUI, don't worry, we will specifically talk about this issue below.
It can be seen from the Frame Debugger that Image and Text are drawn separately, that is to say, they are not batched. The reason is also very simple, because the texture used by Image is Unity White, and the texture used by Text is Font Texture.

2.2 Use of Profiler-UI

Frame Debugger shows how the picture we see is drawn step by step, and we can indirectly know whether the UI we made has been batched. Of course, in addition to the Frame Debugger, we can also use the UI module in the Profier to more intuitively understand whether our UI has been batched.
The shortcut key to open Profier is Ctrl+7, and the menu path is the same as that of Frame Debugger, which is Windows》Analysis》Profiler.
The specific operation is as follows. (ps: The Unity version I use is Unity2019.3.15, and there seems to be no UI module in the Profiler of Unity5)
Basic use of Profier-UI
Let's take a look at how to look at the analysis results.
Here we mainly look at the following columns: Objcet, Batch Breaking Reason, GameObjects and preview view.
The Object column shows the order of batch processing. Each batch will have a number, from small to large, and the smaller the number, the first to draw. For example, Batch 0 is drawn before Batch 1. As for how this number comes from, we will talk about it later when we talk about the batch combination rules.
Batch Breaking Reason shows the reason why the batch was interrupted; GameObjects shows which objects are batched and batched each time. It can be seen from here that there is only Image in the first batch (Batch 0), and only Text in the second batch (Batch 1). Why can't Image and Text be processed in the same batch? Looking at Batch Breaking Reason, we can know that the reason is Different Textrure, which means that the different textures cause the Image and Text to fail to be batched together, which is the same as the analysis of the Frame Debugger.
The overall framework of Profiler-UI
After mastering the basic use of Frame Debugger and Profier UI modules, let's take a look at what the rules of UGUI batching are.

3 UGUI batch rules

3.1 First experience of UGUI combined batch

Let's first intuitively feel the batching of UGUI. As shown in the figure, on top of the preparation work in 1 (create a new scene, then delete the light, set the Clear Flags of the camera to Solid Color) and create 3 default images, where Image and Imge (1) overlap).
First experience of UGUI batch rules
Then we look at the Stats panel, the Batches value is 2 ( if the camera's MSAA is not turned off, it will have a Batches ), indicating that the 3 Images only called Draw Call once, that is, the 3 Images were batched together.
UGUI batch first experience Stats
Then we look at the Profier-UI again, we can see that the 3 images are batched together, and the 3 images are drawn at one time. This is the batch of UGUI.
UGUI batch first experience Profiler-UI

3.2 The first experience of UGUI batch being interrupted

Then, we create a Text between Image and Image(1) that overlaps with Image(1).
UGUI batch first experience to create Text
At this point, if we look at the Stats panel again, we will find that the Batches value has changed to 4.
The UGUI batch was interrupted for the first time to experience the Stats panel
We know above that because the textures used for rendering of the Text control and the Image control are different, the two cannot be batched together. But in the scene above, Image, Image (1) and Image (2) can be batched together, and a Text is added, and the Batches value should also be 3 (one that comes with the camera + 3 images + A Text), but why does the Stats panel show 4, one more than we analyzed?
Then we go to take a look at Profier-UI.
It can be seen that Image, Image (1), and Image (2) are not combined into batches, only Image and Image (2) are combined into batches, and Image (1) is a separate batch.
That is to say, Image, Image (1), and Image (2) could be batched together, but because there is an extra Text under Image (1), Image (1) cannot be combined with Image and Image (2) approved.
In other words, Text interrupted the combination of Image, Image (1), and Image (2)!
UGUI batch was interrupted first experience
Then why does this Text interrupt their batching, and how should we solve the problem of interrupted batching?
To answer these two questions, we must first figure out the batch rules of UGUI.

3.3 Detailed Explanation of UGUI Batch Rules

3.3.1 Batching rules

The basic condition for two UI controls to be batched is that the shaders and textures used by the two controls must be exactly the same. For example, as seen above, although the default shaders used by Text and Image are both UI/Default, the textures used by the two are different, so Text and Image are destined to be unable to be batched together. The same material and texture are just basic conditions, there are other rules. The complete batching process (rules) in UGUI is as follows.
First of all, we need to clarify that sub-Canvas can be nested under Canvas in UGUI, but the batching is based on Canvas ( not including sub-Canvas ) (sub-Canvas will be another batch). In addition, the operation of batching is done in sub-threads.
①Since batching is based on Canvas, the first step is to find out all Canvas, and then remove the Canvas that do not need to be rendered (the transparency is 0, the length and width are 0, under the RectMask2D control, and outside the RectMask2D area )
② Then calculate the depth value Depth of each UI control under the Canvas ( it should be noted that there is also a depth in the property of Image, the two are not the same thing )
③The calculation rules of Depth are as follows:

  • Traverse all UI elements under Canvas in order from top to bottom in Hierarchy
  • For the current UI element CurrentUI
    i. If CurrentUI does not render, then Depth = -1
    ii. If CurrentUI is to be rendered, but no other UI elements below CurrentUI intersect with it , then Depth = 0
    iii. If CurrentUI is to be rendered, there is only one UI below The element (LowerUI) intersects with it, and CurrentUI and LowerUI can be batched (materials and textures are exactly the same), then CurrentUI.Depth = LowerUI.Depth; if the two cannot be batched, CurrentUI.Depth= ​​LowerUI.Depth + 1 iv.
    If CurrentUI needs to be rendered, and there are n elements below it that intersect with it, then follow step iii to calculate n Depths (Depth_1, Depth_2, Depth_3...), and then take the maximum value of CurrentUI.Depth, that is, CurrentUI.Depth = max(Depth_1, Depth_2, Depth_3, ...)
    The meaning of "below" and "intersection" in the above steps should be clarified. These two concepts are very important.
    The UI below the CurrentUI refers to the elements above the CurrentUI in the Hierarchy panel.
    The meaning of the UI below CurrentUI
    The intersection of two UI elements means that the grids of the two elements intersect (there are overlapping parts). It must be noted that the Rect areas of the two elements do not intersect.
    The meaning of two elements intersecting
    When calculating the intersection, because it is necessary to traverse all UI elements and the calculated underlying UI elements (square complexity), the method of grouping and calculating the bounding box rectangle is used in the source code to speed up the calculation, that is, 16 UI elements are calculated as a group of Group Grid Rect , when checking whether it intersects with the underlying UI element, first calculate whether it intersects with the underlying Group, and if it intersects, then make a judgment with the elements in the Group.
    ④ After the Depth of each UI is calculated, it is sorted according to Depth, material ID, texture ID, and RendererOrder (that is, the order of the queue at the UI level, that is, the order on the Hierarchy panel) (the priority of the conditions is descending in order, and they are all sorted from small to large) ). Then remove the UI elements with Depth = -1 to get the queue of UI elements before Batch. This queue is called VisiableList .
    The above paragraph may not be clear in some places, explain the order:
  • First sort by Depth in ascending order
  • After Depth is sorted, elements with the same Depth are sorted by material ID from small to large
  • After the material ID is sorted, elements with the same material ID are sorted by texture ID from small to large
  • After the textrure ID is sorted, the elements with the same texture ID are finally sorted in the order on the Hierarchy (the higher the Hierarchy, the more in front of the queue)

⑤ After obtaining the VisiableList, judge whether the adjacent elements in the VisiableList can be batched (same material and texture). It should be noted that whether the Depth is the same is no longer considered here. As long as the two elements are adjacent and have the same material and texture, even if the Depth of the two elements is different, the two elements can be batched together. The grids are then merged batch by batch and submitted to the GPU for rendering.
In addition, it should be noted that batching is to merge the grids of multiple UIs under the same Canvas, if any element's material, grid vertex, position (Transform) or even color is in the Dynamic creation or deletion of UI elements will cause the Canvas to recalculate the batch (it should be noted that only this Canvas will be affected, and the child Canvas or parent Canvas and other Canvas will not be recalculated), and a new grid will be regenerated. The process of computing the generated mesh is called rebuild. Therefore, this is why the UI advocates the separation of dynamic and static (different Canvas is used for the dynamic part and the static part), and the level is minimized (there are more levels, and recalculation is more time-consuming).

The rules of batching are clarified, but it takes practice to fully understand it. I have specifically selected a few examples here, and following along should greatly deepen my understanding.
Before we start, we need to know how to obtain material Id and texture Id, which is actually very simple, just GetInstanceID()do it directly.

// materialId
image.material.GetInstanceID()
// textureId
image.mainTexture.GetInstanceID()

    
    
     
     
  • 1
  • 2
  • 3
  • 4

3.3.2 Batching rule example 1

Batch rule example 1
As shown in the figure, the materials used by the three images are UI/Default, the texture Id of the textures used by Image1 and Imge3 = 13188, and the texture Id of Image2 = -1136.
Now, please analyze the batching situation and rendering order of the three of them (which image should be rendered first, and which image should be rendered next)?
① First, calculate the Depth of the three pictures separately (remember how Depth is calculated? Forget to go to the above to see).

  • There is no other UI under Image1, so the Depth of Image1 = 0
    (here I would like to remind again, the image has a depth field, the depth we calculated for the batch and the depth field of the image are not the same thing, although the variable name used is the same but Not the same thing, don't get confused)
  • There is Image1 under Image2, but Image1 does not intersect with Image2, so Image2's Depth = 0
  • There are Imag1 and Image2 under Imge3, respectively calculate the Depth of Imge3 when there is only one element under Image3, and then take the maximum value.
    Image3 does not intersect with Imge1, so Image3's Depth = Image1's Depth = 0;
    Image3 intersects with Imge2, so Image3's Depth = Image2's Depth + 1 = 1;
    then take its maximum value, so Image3's Depth = 1.
UI Batched Depth
Image1 0
Image2 0
Image3 1

② Then sort in ascending order according to Depth, material Id, texture Id, hierarchy sort order to get VisiableList.

  • First sort by Depth, the order is Image1》Image2》Imge3;
  • Then sort by material Id, since the materials of the three images are the same (that is, the material Id is the same), the order remains unchanged, still Image1》Image2》Imge3;
  • Then sort by texture Id, because Image2.textureId(-1136) < Image1.textureId(13188), so Image1 and Image2 need to be exchanged, the order is Image2》Image1》Imge3; so VisiableList = {Image2, Imgae1, Image3
    } .

③Finally, judge whether adjacent elements can be batched, and calculate the number of batches.
Image2 and Image1 have the same material but different textures, so Image2 and Image1 cannot be batched together;
Image1 and Image3 have the same material and texture, so Image1 and Image3 can be batched together (It should be noted here that although the Depth of Image1 and Image3 are different, but At this point, this problem is no longer considered);
that is to say, Image2 is drawn separately, and Image1 and Image3 are drawn together in batches.
Let's go to the Profiler UI to see if our analysis is correct.
Example 1 Profiler UI
We can see that our analysis is correct.

3.3.3 Batching rule example 2

insert image description here
As shown in the figure, the materials used by the three images are UI/Default, the texture Id of the textures used by Image1 and Imge3 = -1136, and the texture Id of Image2 = 13188.
Now, please analyze the batching situation and rendering order of the three of them (which image should be rendered first, and which image should be rendered next)?
① First, calculate the Depth of the three pictures respectively.

  • There is no other UI below Image1, so Depth of Image1 = 0
  • There is no other UI under Image2, so the Depth of Image2 = 0
  • There are Imag1 and Image2 under Imge3, respectively calculate the Depth of Imge3 when there is only one element under Image3, and then take the maximum value.
    Image3 does not intersect with Imge1, so Image3's Depth = Image1's Depth = 0;
    Image3 intersects with Imge2, so Image3's Depth = Image2's Depth + 1 = 1;
    then take its maximum value, so Image3's Depth = 1.
UI Batched Depth
Image1 0
Image2 0
Image3 1

② Then sort in ascending order according to Depth, material Id, texture Id, hierarchy sort order to get VisiableList.

  • First sort by Depth, the order is Image1》Image2》Imge3;
  • Then sort by material Id, since the materials of the three images are the same (that is, the material Id is the same), the order remains unchanged, still Image1》Image2》Imge3;
  • Then sort by texture Id. Since Image2.textureId(13188) > Image1.textureId(-1138), the order remains unchanged, still Image1》Image2》Imge3; so
    VisiableList = {Image1, Imgae2, Image3}.

③Finally, judge whether adjacent elements can be batched, and calculate the number of batches.
Image1 and Image2 have the same material but different textures, so Image1 and Image2 cannot be batched together;
Image2 and Image3 have the same material but different textures, so Image2 and Image3 cannot be batched together;
that is to say, Image1, Image2, and Image3 are drawn separately, a total of three batches, the drawing sequence is Image1, Image2, Image3.
Let's go to the Profiler UI to see if our analysis is correct.
Example 2 Profiler UI
We can see that our analysis is correct.

4 optimization

After knowing the principle of batching, we will know how to optimize the UI.

  • use atlas
  • Dynamic and static separation (the dynamic part and the static part use different Canvas respectively)
  • If Text can be replaced by a picture, use a picture instead
  • Avoid frequent deletion/addition of UI objects, changes in UI hierarchy will cause Canvas to be updated (rebuild)
  • Avoid excessive number of UI elements and complex hierarchy affecting Batch update speed
  • Try not to use Mask (it uses stencil buffer internally, which will cause at least 2 additional Draw Calls)

Of course, the above are only part of it.
Also, try not to use Outline, Tiled Sprite (these two will generate many more vertices), uncheck Raycast that does not need to respond to click events, etc., but these have little to do with UGUI batching. As for why they can For optimization, we have to start with the source code of UGUI, let's talk about it when we have time.

Finally, attach the test project.
Link: https://pan.baidu.com/s/1Git6Qhr0Y8Lef8z7dtwddg
Extraction code: xfk3

The blogger's blog link for this article.

5 reference articles

ps: The content of the first three articles is actually a bit problematic. You can compare it with this article and experiment to see which one is correct. Criticism and correction are welcome.

</article>


1. What is UGUI batching

1.1 Preparations

Before the official start, let's do a preparatory work: create a new scene, then delete the parallel light that comes with it, and change the Clear Flags of the camera to Solid Color.
initial scene
At this time, open the Stats panel in the Game view, and you can see that the number of Batches is 1.
(How to look at the parameters on the Stats panel, and what is Batches, please refer to the previously written blog " Stats Statistics Panel for Unity3D Client Project Optimization Summary " " Static Batching for Unity3D Client Project Optimization Summary ")
Initial scene Stats panel

1.2 Batch processing

Before talking about the batching of UGUI, let's take a look at what batch processing is.
Before we talk about batch processing, let's take a look at how an ordinary 3D model is rendered.
① First, the CPU side prepares the grid, textures and Shaders used for this model, and then the GPU loads the grids, textures, and Shaders into the video memory.
②Then the CPU sets the rendering state.
What is setting the rendering state? It is the CPU setting which Shader to use when rendering this grid, and which textures to use. In step ①, we may prepare a lot of Shaders (such as Shader1, Shader2, Shader3), and many textures (texture 1, texture 2, texture 3). The function of setting the rendering state is to tell the GPU, and then you render The Shader used in this grid is Shader1, not Shader2 and Shader3, and the texture used is texture 1 instead of texture 2 and texture 3.
That is to say, the CPU is the boss, and the GPU is the younger brother. The boss says that you will use this Shader and this texture when you render the model next time, and then the younger brother will follow the boss’s request when he starts to work.
③ After the CPU has set the rendering state, the GPU has not officially started rendering the model, but waits for the CPU to give orders, and the CPU tells the GPU that "you can render this model", and then the GPU starts to actually render the model according to the Shader and textures set in ② model, and pass the rendered result layer to the screen. The process of the CPU telling the GPU "can render this model" or this command is called Draw Call (the Batches we see on the Stats panel are actually the number of Draw Call calls).
As can be seen from the above process, each 3D model should go through a complete step ①②③ to be rendered. That is to say, if a model is to be rendered, Draw Call should be called once. For example, if there are 3000 models in our scene, then the Draw Call should be 3000, but when we look at the Stats panel, we will find that there are not so many Draw Calls (the Batches value on the Stats panel). Why is this so? Because Unity does batch processing.
Batch processing is to combine the grids of 3D models that use the same material (Shader) and the same texture during rendering to form a large grid, and then call Draw Call again to directly render this large grid.
Therefore, it should be noted that only models with the same material and the same texture can be batch processed . If the material or texture used by a model is different from other models, then the CPU has to perform step ② to set the rendering state separately, and then you have to Carry out step ③ to call Draw Call separately.

1.3 Significance of batch processing

It can be seen from the above analysis that the significance of batch processing is to reduce the calls of Draw Call.
Because the CPU needs to prepare data and set the rendering state before calling Draw Call, and preparing data and setting the rendering state is particularly time-consuming! If there are too many Draw Calls, the CPU will spend a lot of time preparing data and setting the rendering state, causing performance problems.
For example, we moved a folder containing 1024 small 1kb files much slower than moving a 1Mb file. Because the computer has a lot of extra operations when moving files, moving multiple small files is more time-consuming than moving one large file.
Then rendering can also be understood in this way, two options:
① CPU asks GPU to render 1000 small triangles
② CPU first merges these 1000 small triangles into a large grid, and then asks GPU to render this large grid
Which is faster? Of course it is ②. Because ① the CPU needs to notify the GPU 1000 times, and it takes time to prepare data and set the rendering state each time, while ② the CPU only needs to notify the GPU once, and only needs to prepare data once and set the rendering state once. That is, the Draw Call of ① is 1000, and the Draw Call of ② is 1.
For ordinary 3D models, Unity internally performs static batch processing and dynamic batch processing.
The advantages, disadvantages and limitations of static batch processing and dynamic batch processing can be viewed in the previous blog, " Stats Statistics Panel of Unity3D Client Project Optimization Summary " " Static Batching of Unity3D Client Project Optimization Summary ".

1.4 Batch of UGUI

From the above we know how a 3D model is rendered. Is there any difference between UGUI rendering and 3D model rendering?
The answer is no different.
The UGUI control is also a grid in essence . The only difference from the 3D model is that the grid of the 3D model is modeled and built in 3D Max or Maya, while the grid of the UGUI control is automatically created in the control code code. of.
For example, if we create an Image and a Text, and select the rendering of the Scene view as a wireframe, we can see that both the Image and the Text are grids.
Grid for Image and Text
You may wonder how the grid of Text is just a rectangle, how to render such complex characters? In fact, the font we use on Text is essentially an atlas. Rendering a word is just rendering the picture on the atlas corresponding to this word. It is not much different from ordinary Image rendering in essence. The difference is that there are additional modules to process it. The font atlas of Text corresponds to the character.
Since the controls of UGUI are all grids, it should be possible to perform batch processing, right? Yes! Batch processing of UGUI controls is called batching of UGUI.
The batching of UGUI is to merge the grids of UI controls that meet the batching rules under a certain Canvas into a large grid, and then merge these grids together, call Draw Call once, and then submit a GPU for drawing.
So how do you meet the batch rules? According to the definition of batch processing, batch processing can be performed as long as the materials and textures used by the two meshes are the same.
However, there are other rules for UGUI batching. It is not enough just to satisfy the same material and texture . What kind of rules are there? We will have a section on this later.

2 Use of analysis tools

2.1 Use of Frame Debugger

So the question is, can the default Image and Text we created be batched together?
What are the basic conditions for batching? The material (Shader) and the texture must be the same, so let's see if the default Image and Text just created are satisfied.
The Shader used by both is the default UI/Default.
Default Shader for Image and Text
Are the stickers the same? Our Image here does not have a specified texture, and it cannot be seen directly in the Inspector panel. You have to go to the Frame Debugger to see it.
The function of the Frame Debugger is to facilitate us to see how the frame of the screen is drawn step by step when we click Enable. That is to say, through the Frame Debugger, we can know what was drawn first, then what was drawn, and what was drawn last.
The Frame Debugger panel path is located in Window 》Analysis 》Frame Debugger. (The Unity version I use is 2019.3.15, the lower version of Unity path may be different)
Frame Debugger and Profiler panel paths
Open the Frame Debugger window.
Frame Debugger window
Then click Enable, Frame Debugger will show how the current screen is drawn step by step.
Basic use of Frame Debugger
The left side of the Frame Debugger is a tree structure. From top to bottom, it indicates the order of drawing content. The upper ones are drawn first, and the lower ones are drawn later. The root node of the tree is generally Camera.Render, which indicates how the picture seen by a certain camera is drawn step by step.
Since the Render Mode of UGUI's Canvas here is Screen Space - Overlay mode, this mode is to draw the content of UGUI after all cameras are drawn, so there is also a UGUI.Rendering.RenderOverlays under Camera.Render. UGUI.Rendering.RenderOverlays indicates how UGUI is drawn step by step.
FrameDebugger basic tree structure
Select an item, and the Game view will display the current screen of the currently selected item.
We click on Drawing under Camera.Rendering, and we can see that the Game view turns into a solid color. Why is this happening? We look at the tree structure in turn and find that Clear (color+Z+stencil) is actually executed at the end. Clear means clear. The contents in the brackets are the color buffer, depth buffer and stencil buffer. That is to say, under the step of Drawing, the color buffer, depth buffer, and stencil buffer are cleared. Since it clears the color buffer, the whole frame becomes the color set by our camera.
clear screen
After Clear is completed, then Camera.ImageEffects is executed, which means the post-processing of the camera screen, that is, after all the content seen by the camera is drawn, the image of the camera is processed, and the post-processing of the screen can achieve some special effects.
We create a camera, which has HDR and MSAA effects turned on by default, so there will be one more Camera.ImageEffects step here. (What exactly are HDR and MSAA, I won’t go into details here)
insert image description here
If we turn off HDR and MSAA in Camera, Camera.ImageEffects will no longer be called. If you don't use HDR and MSAA, you can turn them off, which is also an optimized place.
Effect comparison before and after HDR MSAA is turned off
After the camera is drawn, the UGUI is then drawn (UGUI.Rendering.RenderOverlays).
When drawing UGUI, the stencil buffer is first cleared. Above we see that Camera.Rener has cleared the stencil buffer in turn. Why is UGUI cleared again here? It is because UGUI's Mask (mask) control will use the template buffer to achieve the mask effect. (Of course, we do not recommend using Mask to implement masking, because it will add at least two Draw Calls, which we will talk about later.)
The drawing process of UGUI
After clearing the stencil buffer, we start to draw our UGUI control. Remember the essence of UGUI we said above is a grid? So drawing our Image and Text here is actually two Draw Mesh (drawing grid). The rendering engine does not care whether you are a picture or a text. From the perspective of the rendering engine, all UGUI controls are all grids.
We click on the first Draw Mesh, and we can see that the Image is drawn first, and the Shader and its textures used to draw this Image are displayed on the right. We can hold down Ctrl and click the texture box after _MainTex to preview the texture used by this Image.
FrameDebugger draws the Image first
Then we click on the second Draw Mesh to see the drawn Text. But why the size of the font texture seen from the Frame Debugger is 0×0, to be honest, I don’t understand it at all.
UGUI draws Text for the second time
After the Text is drawn, our entire scene is drawn.
Careful students may ask, isn’t our Text on the Image in the scene? Should we draw the Text first and then the Image? Why is the Image drawn first in the Frame Debugger? This involves the batching rules of UGUI, don't worry, we will specifically talk about this issue below.
It can be seen from the Frame Debugger that Image and Text are drawn separately, that is to say, they are not batched. The reason is also very simple, because the texture used by Image is Unity White, and the texture used by Text is Font Texture.

2.2 Use of Profiler-UI

Frame Debugger shows how the picture we see is drawn step by step, and we can indirectly know whether the UI we made has been batched. Of course, in addition to the Frame Debugger, we can also use the UI module in the Profier to more intuitively understand whether our UI has been batched.
The shortcut key to open Profier is Ctrl+7, and the menu path is the same as that of Frame Debugger, which is Windows》Analysis》Profiler.
The specific operation is as follows. (ps: The Unity version I use is Unity2019.3.15, and there seems to be no UI module in the Profiler of Unity5)
Basic use of Profier-UI
Let's take a look at how to look at the analysis results.
Here we mainly look at the following columns: Objcet, Batch Breaking Reason, GameObjects and preview view.
The Object column shows the order of batch processing. Each batch will have a number, from small to large, and the smaller the number, the first to draw. For example, Batch 0 is drawn before Batch 1. As for how this number comes from, we will talk about it later when we talk about the batch combination rules.
Batch Breaking Reason shows the reason why the batch was interrupted; GameObjects shows which objects are batched and batched each time. It can be seen from here that there is only Image in the first batch (Batch 0), and only Text in the second batch (Batch 1). Why can't Image and Text be processed in the same batch? Looking at Batch Breaking Reason, we can know that the reason is Different Textrure, which means that the different textures cause the Image and Text to fail to be batched together, which is the same as the analysis of the Frame Debugger.
The overall framework of Profiler-UI
After mastering the basic use of Frame Debugger and Profier UI modules, let's take a look at what the rules of UGUI batching are.

3 UGUI batch rules

3.1 First experience of UGUI combined batch

Let's first intuitively feel the batching of UGUI. As shown in the figure, on top of the preparation work in 1 (create a new scene, then delete the light, set the Clear Flags of the camera to Solid Color) and create 3 default images, where Image and Imge (1) overlap).
First experience of UGUI batch rules
Then we look at the Stats panel, the Batches value is 2 ( if the camera's MSAA is not turned off, it will have a Batches ), indicating that the 3 Images only called Draw Call once, that is, the 3 Images were batched together.
UGUI batch first experience Stats
Then we look at the Profier-UI again, we can see that the 3 images are batched together, and the 3 images are drawn at one time. This is the batch of UGUI.
UGUI batch first experience Profiler-UI

3.2 The first experience of UGUI batch being interrupted

Then, we create a Text between Image and Image(1) that overlaps with Image(1).
UGUI batch first experience to create Text
At this point, if we look at the Stats panel again, we will find that the Batches value has changed to 4.
The UGUI batch was interrupted for the first time to experience the Stats panel
We know above that because the textures used for rendering of the Text control and the Image control are different, the two cannot be batched together. But in the scene above, Image, Image (1) and Image (2) can be batched together, and a Text is added, and the Batches value should also be 3 (one that comes with the camera + 3 images + A Text), but why does the Stats panel show 4, one more than we analyzed?
Then we go to take a look at Profier-UI.
It can be seen that Image, Image (1), and Image (2) are not combined into batches, only Image and Image (2) are combined into batches, and Image (1) is a separate batch.
That is to say, Image, Image (1), and Image (2) could be batched together, but because there is an extra Text under Image (1), Image (1) cannot be combined with Image and Image (2) approved.
In other words, Text interrupted the combination of Image, Image (1), and Image (2)!
UGUI batch was interrupted first experience
Then why does this Text interrupt their batching, and how should we solve the problem of interrupted batching?
To answer these two questions, we must first figure out the batch rules of UGUI.

3.3 Detailed Explanation of UGUI Batch Rules

3.3.1 Batching rules

The basic condition for two UI controls to be batched is that the shaders and textures used by the two controls must be exactly the same. For example, as seen above, although the default shaders used by Text and Image are both UI/Default, the textures used by the two are different, so Text and Image are destined to be unable to be batched together. The same material and texture are just basic conditions, there are other rules. The complete batching process (rules) in UGUI is as follows.
First of all, we need to clarify that sub-Canvas can be nested under Canvas in UGUI, but the batching is based on Canvas ( not including sub-Canvas ) (sub-Canvas will be another batch). In addition, the operation of batching is done in sub-threads.
①Since batching is based on Canvas, the first step is to find out all Canvas, and then remove the Canvas that do not need to be rendered (the transparency is 0, the length and width are 0, under the RectMask2D control, and outside the RectMask2D area )
② Then calculate the depth value Depth of each UI control under the Canvas ( it should be noted that there is also a depth in the property of Image, the two are not the same thing )
③The calculation rules of Depth are as follows:

  • Traverse all UI elements under Canvas in order from top to bottom in Hierarchy
  • For the current UI element CurrentUI
    i. If CurrentUI does not render, then Depth = -1
    ii. If CurrentUI is to be rendered, but no other UI elements below CurrentUI intersect with it , then Depth = 0
    iii. If CurrentUI is to be rendered, there is only one UI below The element (LowerUI) intersects with it, and CurrentUI and LowerUI can be batched (materials and textures are exactly the same), then CurrentUI.Depth = LowerUI.Depth; if the two cannot be batched, CurrentUI.Depth= ​​LowerUI.Depth + 1 iv.
    If CurrentUI needs to be rendered, and there are n elements below it that intersect with it, then follow step iii to calculate n Depths (Depth_1, Depth_2, Depth_3...), and then take the maximum value of CurrentUI.Depth, that is, CurrentUI.Depth = max(Depth_1, Depth_2, Depth_3, ...)
    The meaning of "below" and "intersection" in the above steps should be clarified. These two concepts are very important.
    The UI below the CurrentUI refers to the elements above the CurrentUI in the Hierarchy panel.
    The meaning of the UI below CurrentUI
    The intersection of two UI elements means that the grids of the two elements intersect (there are overlapping parts). It must be noted that the Rect areas of the two elements do not intersect.
    The meaning of two elements intersecting
    When calculating the intersection, because it is necessary to traverse all UI elements and the calculated underlying UI elements (square complexity), the method of grouping and calculating the bounding box rectangle is used in the source code to speed up the calculation, that is, 16 UI elements are calculated as a group of Group Grid Rect , when checking whether it intersects with the underlying UI element, first calculate whether it intersects with the underlying Group, and if it intersects, then make a judgment with the elements in the Group.
    ④ After the Depth of each UI is calculated, it is sorted according to Depth, material ID, texture ID, and RendererOrder (that is, the order of the queue at the UI level, that is, the order on the Hierarchy panel) (the priority of the conditions is descending in order, and they are all sorted from small to large) ). Then remove the UI elements with Depth = -1 to get the queue of UI elements before Batch. This queue is called VisiableList .
    The above paragraph may not be clear in some places, explain the order:
  • First sort by Depth in ascending order
  • After Depth is sorted, elements with the same Depth are sorted by material ID from small to large
  • After the material ID is sorted, elements with the same material ID are sorted by texture ID from small to large
  • After the textrure ID is sorted, the elements with the same texture ID are finally sorted in the order on the Hierarchy (the higher the Hierarchy, the more in front of the queue)

⑤ After obtaining the VisiableList, judge whether the adjacent elements in the VisiableList can be batched (same material and texture). It should be noted that whether the Depth is the same is no longer considered here. As long as the two elements are adjacent and have the same material and texture, even if the Depth of the two elements is different, the two elements can be batched together. The grids are then merged batch by batch and submitted to the GPU for rendering.
In addition, it should be noted that batching is to merge the grids of multiple UIs under the same Canvas, if any element's material, grid vertex, position (Transform) or even color is in the Dynamic creation or deletion of UI elements will cause the Canvas to recalculate the batch (it should be noted that only this Canvas will be affected, and the child Canvas or parent Canvas and other Canvas will not be recalculated), and a new grid will be regenerated. The process of computing the generated mesh is called rebuild. Therefore, this is why the UI advocates the separation of dynamic and static (different Canvas is used for the dynamic part and the static part), and the level is minimized (there are more levels, and recalculation is more time-consuming).

The rules of batching are clarified, but it takes practice to fully understand it. I have specifically selected a few examples here, and following along should greatly deepen my understanding.
Before we start, we need to know how to obtain material Id and texture Id, which is actually very simple, just GetInstanceID()do it directly.

// materialId
image.material.GetInstanceID()
// textureId
image.mainTexture.GetInstanceID()

    
    
  
  
  • 1
  • 2
  • 3
  • 4

3.3.2 Batching rule example 1

Batch rule example 1
As shown in the figure, the materials used by the three images are UI/Default, the texture Id of the textures used by Image1 and Imge3 = 13188, and the texture Id of Image2 = -1136.
Now, please analyze the batching situation and rendering order of the three of them (which image should be rendered first, and which image should be rendered next)?
① First, calculate the Depth of the three pictures separately (remember how Depth is calculated? Forget to go to the above to see).

  • There is no other UI under Image1, so the Depth of Image1 = 0
    (here I would like to remind again, the image has a depth field, the depth we calculated for the batch and the depth field of the image are not the same thing, although the variable name used is the same but Not the same thing, don't get confused)
  • There is Image1 under Image2, but Image1 does not intersect with Image2, so Image2's Depth = 0
  • There are Imag1 and Image2 under Imge3, respectively calculate the Depth of Imge3 when there is only one element under Image3, and then take the maximum value.
    Image3 does not intersect with Imge1, so Image3's Depth = Image1's Depth = 0;
    Image3 intersects with Imge2, so Image3's Depth = Image2's Depth + 1 = 1;
    then take its maximum value, so Image3's Depth = 1.
UI Batched Depth
Image1 0
Image2 0
Image3 1

② Then sort in ascending order according to Depth, material Id, texture Id, hierarchy sort order to get VisiableList.

  • First sort by Depth, the order is Image1》Image2》Imge3;
  • Then sort by material Id, since the materials of the three images are the same (that is, the material Id is the same), the order remains unchanged, still Image1》Image2》Imge3;
  • Then sort by texture Id, because Image2.textureId(-1136) < Image1.textureId(13188), so Image1 and Image2 need to be exchanged, the order is Image2》Image1》Imge3; so VisiableList = {Image2, Imgae1, Image3
    } .

③Finally, judge whether adjacent elements can be batched, and calculate the number of batches.
Image2 and Image1 have the same material but different textures, so Image2 and Image1 cannot be batched together;
Image1 and Image3 have the same material and texture, so Image1 and Image3 can be batched together (It should be noted here that although the Depth of Image1 and Image3 are different, but At this point, this problem is no longer considered);
that is to say, Image2 is drawn separately, and Image1 and Image3 are drawn together in batches.
Let's go to the Profiler UI to see if our analysis is correct.
Example 1 Profiler UI
We can see that our analysis is correct.

3.3.3 Batching rule example 2

insert image description here
As shown in the figure, the materials used by the three images are UI/Default, the texture Id of the textures used by Image1 and Imge3 = -1136, and the texture Id of Image2 = 13188.
Now, please analyze the batching situation and rendering order of the three of them (which image should be rendered first, and which image should be rendered next)?
① First, calculate the Depth of the three pictures respectively.

  • There is no other UI below Image1, so Depth of Image1 = 0
  • There is no other UI under Image2, so the Depth of Image2 = 0
  • There are Imag1 and Image2 under Imge3, respectively calculate the Depth of Imge3 when there is only one element under Image3, and then take the maximum value.
    Image3 does not intersect with Imge1, so Image3's Depth = Image1's Depth = 0;
    Image3 intersects with Imge2, so Image3's Depth = Image2's Depth + 1 = 1;
    then take its maximum value, so Image3's Depth = 1.
UI Batched Depth
Image1 0
Image2 0
Image3 1

② Then sort in ascending order according to Depth, material Id, texture Id, hierarchy sort order to get VisiableList.

  • First sort by Depth, the order is Image1》Image2》Imge3;
  • Then sort by material Id, since the materials of the three images are the same (that is, the material Id is the same), the order remains unchanged, still Image1》Image2》Imge3;
  • Then sort by texture Id. Since Image2.textureId(13188) > Image1.textureId(-1138), the order remains unchanged, still Image1》Image2》Imge3; so
    VisiableList = {Image1, Imgae2, Image3}.

③Finally, judge whether adjacent elements can be batched, and calculate the number of batches.
Image1 and Image2 have the same material but different textures, so Image1 and Image2 cannot be batched together;
Image2 and Image3 have the same material but different textures, so Image2 and Image3 cannot be batched together;
that is to say, Image1, Image2, and Image3 are drawn separately, a total of three batches, the drawing sequence is Image1, Image2, Image3.
Let's go to the Profiler UI to see if our analysis is correct.
Example 2 Profiler UI
We can see that our analysis is correct.

4 optimization

After knowing the principle of batching, we will know how to optimize the UI.

  • use atlas
  • Dynamic and static separation (the dynamic part and the static part use different Canvas respectively)
  • If Text can be replaced by a picture, use a picture instead
  • Avoid frequent deletion/addition of UI objects, changes in UI hierarchy will cause Canvas to be updated (rebuild)
  • Avoid excessive number of UI elements and complex hierarchy affecting Batch update speed
  • Try not to use Mask (it uses stencil buffer internally, which will cause at least 2 additional Draw Calls)

Of course, the above are only part of it.
Also, try not to use Outline, Tiled Sprite (these two will generate many more vertices), uncheck Raycast that does not need to respond to click events, etc., but these have little to do with UGUI batching. As for why they can For optimization, we have to start with the source code of UGUI, let's talk about it when we have time.

Finally, attach the test project.
Link: https://pan.baidu.com/s/1Git6Qhr0Y8Lef8z7dtwddg
Extraction code: xfk3

The blogger's blog link for this article.

5 reference articles

ps: The content of the first three articles is actually a bit problematic. You can compare it with this article and experiment to see which one is correct. Criticism and correction are welcome.

Guess you like

Origin blog.csdn.net/m1234567q/article/details/130491310