Draw Call Optimization in Mobile Games
A Draw Call is a CPU command to GPU: "render this mesh with this material". On mobile GPUs with tile architecture (Mali, Adreno, Apple), extra Draw Calls are more expensive than on desktop. On Adreno 618, moving from 300 to 150 Draw Calls per frame can boost 45 to 60 FPS without touching shaders.
Where Extra Calls Come From
Three main sources.
Different materials on similar objects. Each unique material = minimum one separate Draw Call. Often see projects where coins, enemies, and bonuses have different textures (PNGs different sizes), packed in different atlases, with different materials. Batching impossible for them.
Dynamic batching breaks silently. Unity batches meshes only if <900 vertices, use one material, and aren't marked static. Adding shadow (Shadow Casting = On) to dynamic object automatically removes it from batching—not obvious from docs.
Skinned mesh without GPU instancing. Animated characters with SkinnedMeshRenderer don't participate in static or dynamic batching. 20 enemies on screen with identical mesh but no GPU Instancing = 20 separate Draw Calls.
Diagnostic Tools
Unity Frame Debugger (Window → Analysis → Frame Debugger)—first step. Shows each Draw Call per frame with explanation why batching failed (Why This Draw Call Can't Be Batched).
Snapdragon Profiler (Android Adreno) and Xcode GPU Frame Capture (iOS) provide detailed real-device view, including time per draw call.
RenderDoc—for deep analysis when Frame Debugger doesn't answer.
What We Do
Sprite Atlas for 2D
For 2D games—SpriteAtlas (not deprecated Sprite Packer). All sprites of one game layer—in one atlas, one material, one Draw Call for entire layer. Important: atlas must be Include in Build, else individual textures load at runtime.
Max atlas size on mobile—2048×2048 for most, 4096×4096 allowed for Android API 26+ and iOS 12+. Use ASTC 6×6 for both: good compression, no visible artifacts on game sprites.
GPU Instancing for 3D
For repeated objects (enemies, trees, bullets)—Enable Instancing on material + verify shader supports #pragma multi_compile_instancing. With Unity 2022+ can use BatchRendererGroup for full control.
GPU Instancing doesn't work with CPU animation. For animated enemies—either GPU skinning via AnimationInstancing (asset), or vertex shaders with baked animation in texture (Texture-based animation).
Static Batching
Static objects (platforms, walls, decorations)—mark Static, enable Static Batching in Player Settings. Unity merges meshes at build time. Overhead: memory increase (meshes duplicated), so don't mark everything static.
Remove Unnecessary Shadow Casters
Shadows are expensive. On mobile, often disable real-time shadows entirely and replace with blob shadow (simple dark circle sprite under object). If shadows needed—limit distance and use one cascade map instead of four.
Target Metrics
| Device | Recommended Max Draw Calls |
|---|---|
| Low-end Android (Adreno 505) | 80–120 |
| Mid-range Android (Adreno 618) | 150–200 |
| iPhone 12 / A14 | 200–300 |
| iPhone 15 / A17 | 300–400 |
These figures for 60 FPS. If target is 30 FPS, budget is twice looser.
Process
Audit current Frame Debugger → categorize Draw Calls by reason → prioritize by GPU time contribution → implement atlases/instancing/batching → validate on low-end device.
Timeline depends on game scope: simple 2D arcade—two-three days, 3D with animated characters—week and more.







