All tools
PerformanceBatchingCanvas

UI Perf Budget

Estimate draw calls, batches and rebuild cost before you build the screen.

Screen composition

Many similar cells plus a scroll rect — where batching usually falls apart.

Structure
2
1
180
120
Materials
4
2
0.70
2
Texture memory

The usual mobile UI default — good balance.

Per-frame churn
12
60
60
1

Estimate — Mid mobile (60 fps)

Batches
38
budget 60
Rebuild cost
1.92 ms
budget 1.5 ms
Texture memory
9.1 MB
budget 48 MB
Overdraw
2×
budget 3×
Frame budget used
11.5%
16.7 ms frame
Elements / canvas
90
Re-batched together
Raycast / pointer
0.30 ms
Per touch, not per frame
Font atlases
2.0 MB
1024² per font asset
If you apply every recommendation
Batches3822
Rebuild1.92 ms1.16 ms
Textures9.1 MB7.3 MB

Dynamic content on its own canvas, layout groups mostly gone, atlases down to three, RectMask2D, raycast targets trimmed to what is clickable, ASTC 6×6. Toggle the button above to inspect the fixed screen in full.

Batch composition38 total
material groups 12interleaving 26mask stencil 0
Rebuild cost breakdown
Canvas re-batch
0.54 ms
Layout rebuild
0.72 ms
Graphic mesh
0.24 ms
Text mesh
0.42 ms

These are estimates from the shape of UGUI's batching, rebuild and fill-rate behaviour, not measurements. They are good for ranking two layouts and catching structural mistakes; for a real number use the Frame Debugger and the Canvas.BuildBatch profiler marker.

Findings (5)

  1. high

    Split the dynamic content onto its own canvas

    A single dirty element re-batches its whole canvas. With ~90 elements per canvas, every one of the 12 per-frame changes pays for all of them. Move the changing elements to a small canvas of their own — usually the single biggest win available.

  2. high

    Replace layout groups with anchored positions

    60 elements sit under a layout group. Layout rebuilds walk the whole subtree and run before the graphic rebuild, so they multiply the cost of every change. For grids and lists whose cell size is known, anchor the cells directly and drop the group.

  3. medium

    120 of 180 elements are raycast targets

    Image and Text default to Raycast Target = on, and almost none of them need it. The raycaster walks every one on each pointer event, and every parent up the tree. Turn it off on labels, icons and backgrounds — keep it only where a click is actually handled.

  4. medium

    Reorder the hierarchy so same-atlas elements are adjacent

    Batching follows hierarchy order. Alternating between atlases — icon, label, icon, label — starts a new batch each time. Grouping all icons then all labels within a row costs nothing and can halve the batch count.

  5. medium

    60 text elements is a lot of mesh

    Every TMP label is a mesh whose vertex count scales with character count. For numbers that change often, prefer a fixed-width format so the mesh size stays constant, and never call SetText every frame with an unchanged value — guard it.

UI performance estimate — Mid mobile (60 fps)
============================================================

Screen
  Canvases                2 (1 dynamic)
  UI elements             180  (~90 per canvas)
  Sprite atlases          4 @ 2048² ASTC 6x6
  Font assets             2
  Masks                   2 (RectMask2D)
  Raycast targets         120
  Full-screen layers      1
  Elements dirty / frame  12
  Under layout groups     60
  Text elements           60

Estimated batches         38   (budget 60)
  material groups         12
  interleaving penalty    26
  mask stencil            0

Estimated rebuild cost    1.92 ms   (budget 1.5 ms of a 16.7 ms frame)
  canvas re-batch         0.54 ms
  layout rebuild          0.72 ms
  graphic mesh rebuild    0.24 ms
  text mesh rebuild       0.42 ms
  raycast per pointer     0.30 ms

Texture memory            9.1 MB  (budget 48 MB)
  sprite atlases          7.1 MB
  font atlases            2.0 MB
Full-screen overdraw      2x  (budget 3x)

With every recommendation applied
  batches                 38 -> 22   (42% lower)
  rebuild cost            1.92 -> 1.16 ms   (39% lower)
  texture memory          9.1 -> 7.3 MB   (20% lower)

Findings
1. [HIGH] Split the dynamic content onto its own canvas
   A single dirty element re-batches its whole canvas. With ~90 elements per canvas, every one of the 12 per-frame changes pays for all of them. Move the changing elements to a small canvas of their own — usually the single biggest win available.

2. [HIGH] Replace layout groups with anchored positions
   60 elements sit under a layout group. Layout rebuilds walk the whole subtree and run before the graphic rebuild, so they multiply the cost of every change. For grids and lists whose cell size is known, anchor the cells directly and drop the group.

3. [MEDIUM] 120 of 180 elements are raycast targets
   Image and Text default to Raycast Target = on, and almost none of them need it. The raycaster walks every one on each pointer event, and every parent up the tree. Turn it off on labels, icons and backgrounds — keep it only where a click is actually handled.

4. [MEDIUM] Reorder the hierarchy so same-atlas elements are adjacent
   Batching follows hierarchy order. Alternating between atlases — icon, label, icon, label — starts a new batch each time. Grouping all icons then all labels within a row costs nothing and can halve the batch count.

5. [MEDIUM] 60 text elements is a lot of mesh
   Every TMP label is a mesh whose vertex count scales with character count. For numbers that change often, prefer a fixed-width format so the mesh size stays constant, and never call SetText every frame with an unchanged value — guard it.

These are estimates derived from how UGUI batches and rebuilds, not measurements.
Use them to rank options and catch structural mistakes; confirm with the Frame
Debugger and the Profiler before acting on any specific number.

Paste into a ticket when arguing for a UI refactor — the findings are the argument.