How to Fix UGUI DrawCall Spikes from Canvas Rotation and Z-Offsets
You've packed your atlases, unified your Z values, and got DrawCall down to under 20. But as soon as you rotate the UI panel—or add a floating border effect with Z offsets—DrawCall jumps right back up. This is a common problem in UGUI: rotation and Z-value offsets break batching. In this section, you'll learn why this happens and how to fix it using World Space Canvas mode and custom shader vertex offsets, so you can keep both your visual design and your performance.
The Problem: Rotation Breaks Batching After optimizing with atlas packing and Z value unification, the DrawCall count dropped from 86 to under 20. But there was a catch: this approach sacrificed the original art design. The border no longer floated above the background, so the panel lost some of its three dimensional interactive feel. The performance target was met, but the art requirement wasn't fully satisfied. Worse yet, this panel supports rotation. When the panel rotates—even by a tiny amount—the DrawCall count immediately returns to more than 60. Why Rotation Breaks Batching To understand why rotation causes this problem, let's look at how the Canvas is set up: The panel is placed as a child Canvas under the root Canvas The root Canvas uses Screen Space Overlay render mode, which cannot be rotated The actual rotation is applied to the child Canvas Here's the issue: Z values are relative to the root node. When the child Canvas rotates, even though the elements' local Z values are 0, their World Position Z values are no longer 0 after rotation. Different world Z values mean different depths in the rendering queue, which breaks batching. Solution 1: Use World Space Canvas Mode Instead of rotating a child Canvas under an Overlay root, change the root Canvas itself to World Space mode. How World Space Helps In World Space mode: The Canvas exists as a 3D object in the scene It can be moved, scaled, and rotated freely You need to manually align it with the main camera there's no automatic binding When you rotate the root Canvas instead of a child Canvas, the elements' local Z values remain at 0 relative to the Canvas, so batching is preserved After switching the root Canvas to World Space and rotating it instead of the child, the DrawCall count no longer changes during rotation, and elements keep their Z value at 0. Solution 2: Replace Z Value Offsets with a Shader Now we have one remaining issue: the original art design was changed during optimization. The border in each icon no longer floats above the background—instead, it shares the same Z value. We unified all Z values to 0 because Unity can only batch elements that are adjacent in the queue and use exactly the same material and atlas. When the border and background use different Z values, batching cannot happen. We need another way to implement the UI element offset without relying on the Z value. A custom shader can do this by modifying vertex positions in the vertex shader during rendering. How the Shader Approach Works Many projects that pursue extreme UI optimization use shaders to replace this kind of offset effect. The idea is simple: Keep all elements at Z = 0 in the Canvas preserving batching Use a vertex shader to offset vertices outward in clip space, creating the floating effect visually Since batching is determined by material and atlas not by shader vertex modifications , the elements still batch together Implementing the Shader Create a custom UI shader e.g., . In the function: 1. Pass the incoming vertex position to the output 2. Add an offset value to the vertex position 3. Define as a material property so it can be controlled per material Setting Up Materials Create two materials using the custom shader: UI ADD — for elements that don't need an offset Z = 0 UI ADD Z15 — for border elements that should float above Z = 0.15 or similar Assign the appropriate material to each UI element: Background elements → UI ADD material Border/frame elements → UI ADD Z15 material The result: the border visually floats above the background, creating the same 3D effect as Z value offsets—but since both elements are still at Z = 0 in the Canvas, batching is preserved. Trade Offs This approach adds a small amount of per vertex GPU work, but it's negligible compared to the cost of extra DrawCalls. The main trade off is the added complexity of managing custom shaders and materials instead of simply adjusting Z values in the Inspector. However, for projects that need both visual quality and performance, this is a well established technique that delivers both.