How Static/Dynamic UI Separation Reduces UGUI Canvas BuildBatch Cost
You've optimized DrawCall and Overdraw, but your UI still causes CPU spikes. The problem might be on the logic side—specifically in Canvas.BuildBatch. When dynamic elements share a Canvas with static ones, every animation triggers a full rebuild of everything, even unchanged elements. This section explains how to diagnose BuildBatch cost and how static/dynamic UI separation dramatically reduces logic cost.
The Logic Side of UI Performance Most UGUI optimization guides focus on rendering DrawCall and Overdraw , but logic cost can be just as much of a bottleneck—especially on the CPU side. The main contributors are: — recalculates vertex data — merges meshes for batching This section focuses on cost and how to optimize it. Diagnosing BuildBatch Cost Unity Editor's built in Profiler has some additional overhead of its own and may affect performance judgment. For more accurate measurement, we used GameOptim's performance optimization tool GOT Online and optimized based on the data in its test report. What to Look For After running a 10 minute test with GOT Online, navigate to Engine → UI in the report. Look for the function . In our example project, the CPU cost of this function was 0.43 ms , which is relatively high. Why This Function Matters is a low level UGUI rendering function. It waits for to finish running on the worker thread before executing. If this function shows high CPU cost on the main thread, it means: is taking a long time on the worker thread The main thread is waiting for it to finish a stall The batching calculation itself is the bottleneck So even though BuildBatch runs on a worker thread, if it's slow enough, the main thread still has to wait for it, and you see the cost show up as time. The Root Cause: Mixed Static and Dynamic Elements Based on the current layout of the UI elements, we can apply the optimization principle of static/dynamic UI separation . Why Mixing Static and Dynamic Elements Is Expensive In our example, both dynamic UI elements rotating circles and static UI elements background, frames, icons were under the same child Canvas. Here's the problem: When the dynamic circles rotate, their transforms change Transform changes mark those elements as dirty must rebuild the batch for the entire Canvas All the static elements—background, frames, icons—get re batched every single frame, even though they haven't changed at all This causes unnecessary performance cost The more elements on the same Canvas, the more expensive each rebuild becomes. The Fix: Separate Canvases for Static and Dynamic Elements The solution is straightforward: separate dynamically changing elements and place them on their own Canvas . How to Implement It 1. Identify which UI elements change every frame animations, rotations, moving elements 2. Move those elements to a separate Canvas 3. Keep static elements backgrounds, frames, non animated content on the original Canvas In our example, we separated the dynamically rotating circles onto their own Canvas, leaving the static weapon icons, backgrounds, and frames on the original Canvas. Why This Works With separate Canvases: The dynamic Canvas rebuilds only its own smaller set of elements each frame The static Canvas rarely needs to rebuild only when its content actually changes has far less work to do overall The main thread spends less time waiting for the worker thread Results After implementing static/dynamic separation, we ran the test again with GOT Online. The CPU cost of dropped from 0.43 ms to 0.09 ms —a clear reduction that indicates the main thread is essentially no longer waiting on the worker thread. That's roughly a 79% reduction in BuildBatch related CPU cost, just from splitting elements into two Canvases. When to Apply This Optimization Static/dynamic separation is especially beneficial when: You have animated or continuously moving UI elements Those elements are on the same Canvas as many static elements You see high cost in your profiler The Canvas has a large number of elements Trade Offs Splitting Canvases isn't free. Each additional Canvas adds some overhead: More Canvas components to manage Potential DrawCall increases if elements that could previously batch now span different Canvases Slightly higher memory usage However, in most cases where BuildBatch is a bottleneck, the performance gain from reduced rebuild cost far outweighs these trade offs. As with any optimization, profile before and after to confirm the benefit. Series Recommendations What Are the Main Performance Bottlenecks in Unity UGUI? https://www.gameoptim.com/blog/post/ugui performance bottlenecks rendering logic How to Reduce UGUI DrawCall with Atlas Packing and Batching https://www.gameoptim.com/blog/post/reduce ugui drawcall atlas packing batching How to Fix UGUI DrawCall Spikes from Canvas Rotation and Z Offsets https://www.gameoptim.com/blog/post/ugui drawcall canvas rotation shader offset How to Reduce UGUI Overdraw: 3 Practical Techniques That Work https://www.gameoptim.com/blog/post/reduce ugui overdraw tight mesh 9 slice