Intro
A high Rendering.UpdateBatches cost in Unity’s UGUI often signals excessive SyncTransform activity. This article breaks down how TransformDirty propagation happens, why frequent SetActive is a common trigger, and how separating dynamic and static Canvas structures can significantly reduce UI overhead in production projects.
Q:
In the main scene report from GameOptim GOT Online, we noticed that UGUI consistently takes around 7ms, which is even higher than in the combat scene.
From the call stack, a large portion of the cost appears to come from Rendering.UpdateBatches itself.
Could this be caused by calling SetActive too frequently in the project?
A:
Your direction is correct.
There is a high probability that frequent SetActive calls are triggering SyncTransform, which is a very typical optimization pitfall in UGUI.
However, while SetActive is one of the most common causes, it is not the only one. Operations such as RectTransform size changes, LayoutGroup rebuilds, ContentSizeFitter updates, and SetParent can also continuously trigger SyncTransform. Further confirmation still requires checking the call stack.
The profiling path here is relatively clear:
The main performance entry point of UGUI is Rendering.UpdateBatches, but the internal breakdown of this stage determines where the actual problem lies.
Under normal circumstances, most of the cost is concentrated in the SendWillRenderCanvases node.
But in this project report, the unusual part is that the self-time of UpdateBatches itself accounts for an abnormally large portion, which points to a specific internal function:
SyncTransform
Within a sampling window of 4000 frames, the number of SyncTransform calls remains around 300.
This call count is highly correlated with the self-time percentage of UpdateBatches:
the higher the call count, the higher the self-time, and the overall UGUI cost increases accordingly.
It is important to note that a high SyncTransform call count is not the root cause by itself.
It is the result of continuous TransformDirty generation.
When a UI node’s active state, hierarchy, position, or size changes frequently, Unity continuously synchronizes Transform data.
These changes are then propagated to the Canvas system, triggering batch rebuilds and Canvas updates.
Therefore, when investigating SyncTransform, the focus should not only be on the cost itself, but on identifying what is continuously generating TransformDirty.
Why does SetActive trigger SyncTransform?
When GameObject.SetActive(true) is called, the UI hierarchy containing that node triggers Transform synchronization and Canvas dirty flag updates.
In complex Canvas hierarchies, this impact can spread to a large number of related UI elements.
If certain UI elements are shown and hidden at high frequency—such as combat HUD floating damage text, status icons, or timers—every visibility change can cause the entire Canvas to resynchronize its Transforms.
This causes the number of SyncTransform calls to continuously increase.
A more subtle case occurs when frequently toggled UI elements share the same Canvas as static UI.
In this case, every SetActive operation can force the static portion of the Canvas to be recalculated as well, creating a large amount of unnecessary rebuild overhead.
Optimization Suggestions
Use localScale 0/1 instead of SetActive for high-frequency visibility toggles
For UI elements with high toggle frequency and short lifecycles (such as floating damage text, status icons, and timers), switching between localScale = 0 and localScale = 1 can avoid continuous SyncTransform triggering.
For medium-to-long lifecycle elements with high reuse frequency, object pooling should be prioritized.
For cases where position and state need to be preserved and only temporary hiding is required, using an Alpha fade approach may be more appropriate.
Separate dynamic and static UI
Move frequently toggled UI nodes out of the main Canvas and place them under a dedicated Canvas.
This ensures each visibility change only affects the smaller dynamic Canvas, while the static UI under the main Canvas remains untouched.
Inspect nested Canvas coverage
If a parent Canvas contains a large number of child elements, frequent updates in child nodes can still affect the parent.
By combining the SyncTransform call count curve with scene-level performance peaks, it is possible to trace back which Canvas is being repeatedly invalidated.
Top comments (0)