DEV Community

Cover image for A Breakdown of UGUI Performance Bottlenecks: Rendering vs. Logic
GameOptim
GameOptim

Posted on

A Breakdown of UGUI Performance Bottlenecks: Rendering vs. Logic

When optimizing UGUI, the first question to answer is: where is the cost actually coming from? Without this understanding, optimization efforts are likely to be scattered and ineffective.

Problem / Scenario

Mobile game teams often encounter frame-rate drops and device heating as UI complexity grows. The UI system seems simple on the surface—just images and text—but the underlying cost structure is more nuanced than many developers realize.

The Two Dimensions of UI Performance

UGUI performance cost breaks down into two categories: rendering and logic.

Rendering

DrawCall

DrawCall is primarily a CPU cost. Each draw command issued to the GPU requires state setup and parameter passing. High DrawCall counts directly increase CPU usage.

Benchmark targets from real projects:

  • Complex MMO-style HUD: under 30 Draw Calls
  • Combat UI: single-digit Draw Calls

These targets apply when the UI is composited on top of a 3D scene. Full-screen UIs without scene rendering can have a slightly relaxed budget.

Overdraw

Overdraw measures how many times screen pixels are redrawn by UI elements. Every image or button placed on screen contributes. Translucent UIs (common in high-tech visual styles) are especially prone to Overdraw waste since the 3D scene behind must remain visible.

Logic

Three functions account for nearly all UI logic cost:

1. EventSystem.Update

Two components:

  • Event detection: Every frame, Unity traverses all interactive UI elements to check for intersection with touch points. Complexity scales with the number of interactive elements.
  • Event callback logic: Code inside onClick and similar callbacks runs within this function (though this is application logic, not UI system overhead).

2. Canvas.SendWillRenderCanvases

This function recalculates vertex data for dirty UI elements. Vertex attributes (positions, UVs, etc.) are stored in C# arrays. When text content changes—or any property affecting vertex positions changes—the arrays must be rebuilt. The data is then transferred to Mesh objects for rendering.

Key insight: This is pure main-thread C# work. The more elements change per frame, the higher the cost.

3. Canvas.BuildBatch / BatchJob

After vertex data is ready, each UI element still has its own mesh. Drawing them one by one would result in an unacceptably high DrawCall count. BuildBatch merges compatible elements into larger meshes.

Batching considers:

  • Same atlas (texture)
  • Same material
  • Depth order in the VisibleList (sorted by Depth → Material → Atlas → Hierarchy priority)
  • Whether elements overlap
  • Rendering order adjustments for non-overlapping elements

The batching calculation itself has non-trivial CPU cost. However, a significant portion of BatchJobs can run on worker threads, which helps on multi-core devices.

Key Takeaway

In practice, EventSystem.Update is rarely the primary bottleneck. The usual suspects are:

  • DrawCall and Overdraw on the rendering side
  • SendWillRenderCanvases and BuildBatch on the logic side

Knowing which one to target is half the battle. The remaining sections in this series dive into optimization techniques for each area, using a real MMO weapon UI panel as a case study.

Top comments (0)