Оптимизация Draw Calls мобильной игры
Draw Call — это команда CPU к GPU «нарисуй вот этот меш с этим материалом». На мобильных GPU с тайловой архитектурой (Mali, Adreno, Apple GPU) лишние Draw Calls дороже, чем на десктопе. На Adreno 618 переход от 300 к 150 Draw Calls в кадре может дать прирост с 45 до 60 FPS без единого изменения в шейдерах.
Откуда берутся лишние вызовы
Три главных источника.
Разные материалы на похожих объектах. Каждый уникальный материал = минимум один отдельный Draw Call. Часто вижу проекты, где у монет, врагов и бонусов — разные текстуры (PNG разных размеров), упакованные в разные атласы, с разными материалами. Batch для них невозможен.
Dynamic batching ломается незаметно. Unity батчит меши только если они меньше 900 вершин, используют один материал и не имеют статичного флага. Добавление тени (Shadow Casting = On) на динамический объект автоматически выбивает его из батча — это не очевидно из документации.
Skinned mesh без GPU instancing. Анимированные персонажи с SkinnedMeshRenderer не участвуют ни в static batching, ни в dynamic batching. Если на экране 20 врагов с одинаковым мешем, но без GPU Instancing — 20 отдельных Draw Calls.
Инструменты диагностики
Unity Frame Debugger (Window → Analysis → Frame Debugger) — первый шаг. Показывает каждый Draw Call в кадре с объяснением, почему батчинг не произошёл (Why This Draw Call Can't Be Batched).
Snapdragon Profiler (для Android на Adreno) и Xcode GPU Frame Capture (iOS) дают более детальную картину на реальном железе, включая time per draw call.
RenderDoc — для глубокого анализа, если Frame Debugger не даёт ответа.
Что делаем
Sprite Atlas для 2D
Для 2D-игр — SpriteAtlas (не устаревший Sprite Packer). Все спрайты одного игрового слоя — в один атлас, один материал, один Draw Call для всего слоя. Важно: атлас должен быть Include in Build, иначе в рантайме будут подгружаться отдельные текстуры.
Максимальный размер атласа на мобильных — 2048×2048 для большинства устройств, 4096×4096 допускается для Android API 26+ и iOS 12+. Использовать ASTC 6×6 для обоих: хорошая компрессия без заметных артефактов на игровых спрайтах.
GPU Instancing для 3D
Для повторяющихся объектов (враги, деревья, пули) — Enable Instancing на материале + убедиться, что шейдер поддерживает #pragma multi_compile_instancing. С Unity 2022+ можно использовать BatchRendererGroup для полного контроля.
GPU Instancing не работает с анимацией на CPU. Для анимированных врагов — либо GPU skinning через AnimationInstancing (asset), либо вертексные шейдеры с запечёнными анимациями в текстуре (Texture-based animation).
Static Batching
Статичные объекты (платформы, стены, декорации) — помечаем как Static, включаем Static Batching в Player Settings. Unity объединит меши в build time. Оверхед: увеличение размера памяти (меши дублируются), поэтому не помечаем статикой всё подряд.
Убираем лишние Shadow Casters
Тени — дорого. На мобильных часто отключают real-time тени полностью и заменяют blob shadow (простой dark circle спрайт под объектом). Если тени нужны — ограничиваем дистанцию и переходим на одну каскадную карту теней вместо четырёх.
Целевые метрики
| Устройство | Рекомендуемый максимум 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 |
Эти цифры — для 60 FPS. Если таргет 30 FPS, бюджет вдвое мягче.
Процесс работы
Аудит текущего Frame Debugger → категоризация Draw Calls по причинам → приоритизация по вкладу в GPU time → реализация атласов/instancing/batching → валидация на Low-end устройстве.
Сроки зависят от масштаба игры: простая 2D-аркада — два-три дня, 3D с анимированными персонажами — неделя и больше.







