Unity UI(UGUI)-Part 1 : Overview

Reference: UWA UI optimization video stream  (Chinese), Unity UI best practice, Unity UI source code

In this serial of posts, I will mainly focus on explaining how to optimize Unity UI, so the reader should have a basic understanding of Unity UI. These involves a lot of guess and assumption, because to entirely mater the UI, people need to read all the UI source code and know the C++ native code behind it. All corrections and discussion to my assumptions are welcomed!

Getting Started 

UI is an easily ignored part but has huge impact on the performance. According to survey of UWA, UI normally takes up around 20% of the CPU usage while graphics rendering takes up around 40%

Overview

Generally speaking, Canvas is the unit that is used by Unity to render UI element. So the behavior of one canvas will not affect other canvases.

Image you are developing the UI for Unity Engine. First you need the struct to hold the parameters of the UI elements and send those to Graphics side to render. This is the VertexHelper.cs class Unity actually uses. It records the vertex positions ,uvs, normals, etc.

</pre>
public class CanvasUpdateRegistry
{
private static CanvasUpdateRegistry s_Instance;

private bool m_PerformingLayoutUpdate;
private bool m_PerformingGraphicUpdate;

private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();
<pre>

Now you have a basic structure to hold the information of UI elements. You can just send these information to the Graphics system to render. Everything is gonna work until some UI elements need to be moved. For example, if An image is moved to overlap with another image, we should tell graphics system these information. Graphics system cannot make such calculation according to this post:

Canvases are responsible for combining their constituent geometry into batches, generating the appropriate render commands and sending these to Unity’s Graphics system.

This then involve an important process: rebatch or batch build or rebuild.

Rebuild

There are two possible directions to code the rebuild process.

One is checking which UI elements are needed to be updated in every frame and then do the rebuild.

The second is maintaining a queue. Every frame only rebuild those elements in the queue.

Apparently, second solutions sounds much better especially when you have a lot of UI elements. In fact, the first is the implementation of NGUI; the second is the implementation of UGUI.

The relevant code is in CanvasUpdateRegistry.cs.


public class CanvasUpdateRegistry
{
private static CanvasUpdateRegistry s_Instance;

private bool m_PerformingLayoutUpdate;
private bool m_PerformingGraphicUpdate;

private readonly IndexedSet m_LayoutRebuildQueue = new IndexedSet();
private readonly IndexedSet m_GraphicRebuildQueue = new IndexedSet();

There are 3 steps to rebuild UI elements in PerformUpdate() function:

1.Dirty Layout components are requested to rebuild their layouts, via the ICanvasElement.Rebuild method.

This step basically finish all the calculation of Layout components’ position and sort the order of layout list according to depth. And finally do some calculation according to other UI layout element such as Content Size Fitter, Aspect Ratio Fitter or Layout Groups.

 2. Any registered Clipping components (such as Masks) are requested to cull any clipped components. This is done via ClippingRegistry.Cull.

I didn’t do much research on this one. Here, every elements implementing IClipper will perform the clipping. For example, RectMask2D.cs. In PerformClipping() function, I guess it will recalculate visibility.

3.Dirty Graphic components are requested to rebuild their graphical elements.

If the vertex data has been marked as dirty (e.g. when the component’s RectTransform has changed size), then the mesh is rebuilt.

If the material data has been marked dirty (e.g. when the component’s material or texture has been changed, then the attached Canvas Renderer’s material will be updated.

When will rebuild

In CanvasUpdateRegistry.cs . The related function is setDirty(),SetVerticesDirty(),SetMaterialDirty(), SetAllDirty.

By searching these functions we should have an overview when will canvas perform rebuild.

Unity docs provide some basic situation for layout rebuild:

1.In setters for properties that can change the layout.    In LayoutElement.cs, notice setdirty() in setter.


public virtual float minWidth { get { return m_MinWidth; } set { if (SetPropertyUtility.SetStruct(ref m_MinWidth, value)) SetDirty(); } }
 public virtual float minHeight { get { return m_MinHeight; } set { if (SetPropertyUtility.SetStruct(ref m_MinHeight, value)) SetDirty(); } }
 public virtual float preferredWidth { get { return m_PreferredWidth; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredWidth, value)) SetDirty(); } }
 public virtual float preferredHeight { get { return m_PreferredHeight; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredHeight, value)) SetDirty(); } }
 public virtual float flexibleWidth { get { return m_FlexibleWidth; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleWidth, value)) SetDirty(); } }
 public virtual float flexibleHeight { get { return m_FlexibleHeight; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleHeight, value)) SetDirty(); } }

2. OnEnable

So the normal way to hide UI which activates the gameobject and deactivate still causes the rebuild, optimal solutions will be discussed later.

3. OnDisable

4. OnRectTransformDimensionsChange

5.OnDidApplyAnimationProperties

6.OnTransformParentChanged

Graphics rebuild:

  1. SetAllDirty(): Will set all dirty: SetLayoutDirty();
    SetVerticesDirty();
    SetMaterialDirty();

    1. OnTransformParentChanged()
    2. OnDidApplyAnimationProperties()
    3. When you change sprite element of Image component.
    4. SetNativeSize() in Image.cs
    5. change font in Text.cs
  2. SetVerticesDirty()
    1. When you change color field. The correct way to change color without rebuilding will be discussed later.
    2. Change image type, preserveAspect, fillCenter, fillMethod, fillAmount, fillClockwise, fillOrigin
    3. Change text string in Text.cs
    4. Set supportRichText, resizeTextForBestFit, resizeTextMinSize, resizeTextMaxSize, alignment, fontSize, horizontalOverflow, verticalOverflow, lineSpacing, fontStyle in Text.cs
  3. SetMaterialDirty()
    1. change material component
    2. Related to mask cull, I haven’t be clear about this part yet.

Conclusion:

To optimize UI, we have to reduce rebuild as much as possible. How to reduce will be introduced next.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s