На Android mid-range пристрої GPU Profiler показує 18 мс на кадр при цільових 16.6 мс — гра не тримає 60 FPS. Комбінація з 340 draw calls, overdraw-важких ефектів та текстур 2048×2048, які на екрані займають 64×64 пікселі — кожен фактор окремо терпимий, разом — катастрофа.
Чому оптимізація графіки для мобільних платформ потребує комплексного підходу?
Мобільні GPU — tile-based: вони розбивають кадр на тайли та рендерять послідовно. Це робить їх чутливими до overdraw та fill rate. На відміну від десктопу, де immediate mode GPU прощає більше, на мобільному пристрої кожен зайвий draw call або піксельний шейдер б'є по продуктивності. Ми об'єднуємо профілювання, стиснення текстур, налаштування батчингу та шейдерів в єдиний процес — інакше результату не буде.
Як виміряти та скоротити draw calls?
Draw Call — це команда CPU відмалювати групу трикутників з певними налаштуваннями. Кожен draw call потребує синхронізації CPU-GPU та передачі даних. На мобільних пристроях з tile-based GPU (PowerVR, Mali, Adreno) це дорожче, ніж на десктопних immediate mode GPU.
Інструменти: Unity Profiler (GPU Usage), Frame Debugger, а для детального аналізу — RenderDoc на Android або Xcode Instruments на iOS. GPU Profiler прямо в редакторі показує кількість batch'ів та SetPass calls. Хороша ціль для мобільного проекту — до 100 draw calls на кадр, реалістична для mid-core — 150–200.
Основні джерела зайвих draw calls:
-
Static Batching об'єднує статичні об'єкти з однаковим матеріалом в один mesh при старті сцени. Умова: об'єкти мають бути помічені Static в Inspector та використовувати однаковий Material — один асет матеріалу, не однакові налаштування. Якщо у двох об'єктів Material з ідентичними параметрами, але це різні Material Instance — батчинг не працює.
- GPU Instancing масштабується краще при великій кількості копій одного об'єкта (дерева, каміння, вороги одного типу). Dynamic Batching для дрібних об'єктів (до 900 вершин) працює автоматично, але на практиці його вимикають заради Instancing.
- Canvas Overlay режим в uGUI: Canvas в Screen Space - Overlay рендериться поверх всього, і кожна зміна в будь-якому UI-елементі помічає весь Canvas Dirty, перераховує mesh та робить окремий draw call. Для UI з анімованими елементами обов'язково розносити статичні та динамічні елементи по різних Canvas'ам.
Як текстури впливають на продуктивність мобільних ігор?
Мобільні пристрої використовують Unified Memory Architecture: GPU та CPU ділять одну пам'ять. 512 MB RAM — не рідкість для бюджетних Android-пристроїв. Некомпресована RGBA32 текстура 2048×2048 = 16 MB. 10 таких текстур на рівні = 160 MB лише на текстурах.
Формати стиснення з апаратною підтримкою — оптимізація графіки
| Формат |
Біт на піксель |
Підтримка |
| ETC2 без альфи |
4 bpp |
Android, iOS (через ASTC?) |
| ETC2 з альфою |
8 bpp |
Android |
| ASTC 4×4 |
8 bpp |
iOS, Android (Adreno 400+, Mali G7x+) |
| ASTC 6×6 |
3.5 bpp |
iOS, Android (нові) |
| ASTC 8×8 |
2 bpp |
iOS, Android (високе стиснення) |
| DXT5 (BC3) |
8 bpp |
PC |
ASTC — найкращий варіант для iOS та сучасних Android (Adreno 400+ серії, Mali G7x та вище). На старих Android пристроях з OpenGL ES 2.0 ASTC не підтримується — потрібен ETC2 fallback. Unity дозволяє задати різні формати для різних платформ через Texture Importer.
Міпмапи — обов'язкові для 3D-об'єктів, не потрібні для UI. Міпмап додає 33% до розміру текстури в пам'яті, але для UI-елементів, які завжди рендеряться в нативному роздільній здатності, це марна витрата. Перевіряємо через Texture Importer → Generate Mipmaps → вимкнути для всіх UI-спрайтів.
В одному з проектів — мобільна 3D стратегія — при запуску кампанії гра падала з OOM на пристроях з 512 MB RAM. Memory Profiler показав 380 MB лише на текстурах. Аудит виявив: 60% текстурного бюджету займали текстури terrain та environment у форматі RGBA32 без стиснення (розробник вимкнув стиснення на етапі прототипування і забув повернути), ще 15% — UI-текстури з увімкненими міпмапами. Рішення: переведення всіх terrain/environment текстур в ASTC 6×6, UI-текстури в ASTC 8×8 без міпмапів, для ефектів з альфою — ASTC 4×4. Підсумок: 142 MB. OOM-кріші припинилися, звільнилося 240 MB для ігрової логіки та звуку.
Чому overdraw особливо критичний на мобільних GPU?
Overdraw — це рендеринг одного пікселя кілька разів. Напівпрозорі партикули, складні постпроцесингові ефекти, перекривні UI-елементи — все це overdraw. На tile-based мобільних GPU overdraw особливо дорогий: кожен раз, коли тайл пам'яті читається та записується повторно, це додаткова робота.
Візуалізувати overdraw в Unity: Scene View → Render Mode → Overdraw. Білі плями — проблема. Особливо стежимо за particle systems — партикли часто рендерять десятки напівпрозорих квадів поверх один одного в одній точці.
Для партикульних систем: обмежувати Max Particles, використовувати непрозорі або cutout шейдери де це візуально допустимо (cutout дорожчий за прозорий по fill rate, але дешевший по overdraw у глибину), сортувати партикли по Sorting Layer щоб мінімізувати перетин з геометрією.
Для шейдерів: прості Unlit шейдери в 3–5 разів дешевші за Lit на мобільних пристроях. Для декорацій дальнього плану, тіней на землі, billboard-об'єктів Unlit достатній. Lit шейдер з per-pixel lighting лише для ближнього плану та ключових об'єктів.
Які інструменти використовувати для профілювання?
Починаємо з Unity Profiler підключеним до пристрою через USB (Build → Development Build + Autoconnect Profiler). GPU Usage Profiler показує час рендера за категоріями. Frame Debugger — детальний розбір draw calls. Memory Profiler — знімок пам'яті з розбивкою за категоріями.
Для Android додатково: Android GPU Inspector (AGI) для пристроїв з Adreno, Mali Performance Counters для Mali GPU. Вони показують fill rate utilization, texture bandwidth та cache hit rate — метрики, недоступні в Unity Profiler. Перевірити підтримку ASTC на пристрої можна через SystemInfo.SupportsTextureFormat(TextureFormat.ASTC_6x6).
| Задача |
Терміни |
| Аудит продуктивності + звіт з рекомендаціями |
2–5 днів |
| Оптимізація текстур та матеріалів (одна сцена) |
3–7 днів |
| Комплексна оптимізація графіки (весь проект) |
2–6 тижнів |
| Оптимізація під конкретний мінімальний пристрій |
1–3 тижні |
Що входить у нашу роботу з оптимізації?
- Детальний аудит продуктивності з повним звітом та рекомендаціями
- Пережатитя текстур з вибором оптимальних форматів для кожної платформи
- Налаштування батчингу, інстансингу та шейдерів
- Профілювання та усунення вузьких місць (draw calls, overdraw, memory)
- Консультація та навчання команди замовника
- Гарантія результату: FPS та стабільність на цільових пристроях
Ми більше 5 років займаємося оптимізацією мобільних ігор, реалізували 20+ проектів на Unity та Unreal Engine. Зв'яжіться з нами для аудиту вашого проекту. Замовте оптимізацію та отримайте стабільний FPS на цільових пристроях.
Типові проблеми продуктивності ігор
Проект запускається на топових девайсах розробника без питань. На mid-range Android п'ятирічної давності — 20 fps і перегрів через 5 хвилин. На iPhone 11 — стабільні 60, але на iPhone XR — просідання у важких сценах. Ми стикаємося з цим кожен день. Оптимізація — не «потім», а архітектурне рішення з першого коміту. За 8 років роботи ми провели оптимізацію більш ніж для 50 ігор — від гіперказуалки до AAA на консолях. Гарантуємо: після нашого аудиту ви отримаєте не просто список проблем, а конкретний план з вимірними цілями та термінами. Замовте аудит продуктивності — оцінимо ваш проект за 3 дні і покажемо, як знизити draw calls на 40% без втрати якості.
Як профілювати продуктивність ігор?
Перш ніж оптимізувати — виміряти. Оптимізація без профілювання — вгадування.
| Інструмент |
Призначення |
| Unity Profiler |
CPU/GPU час по системах, GC allocations, audio |
| Frame Debugger |
Інспекція кожного draw call у кадрі |
| Memory Profiler |
Знімок пам'яті, граф залежностей асетів |
| RenderDoc |
Глибокий аналіз GPU-стану, актуальний для PC/Console |
| Android GPU Inspector |
Профілювання GPU на реальному Android-пристрої |
| Xcode Instruments |
GPU + memory на iOS (Metal Performance HUD) |
| Snapdragon Profiler |
Qualcomm GPU — детальна статистика шейдерів |
Профілюйте на цільовому залізі, а не в редакторі. Editor додає оверхед — цифри з Play Mode не репрезентативні. Розділяйте GPU та CPU bottleneck: на CPU багато draw calls або важка логіка, на GPU — складні шейдери або overdraw. Документація Unity: профілювання на пристрої — обов’язковий крок для мобільних ігор. Типовий час профілювання одного сценарію — 5–8 годин, включаючи збір метрик на 3–5 пристроях різних поколінь. Аналізуємо 10–15 ключових ігрових сценаріїв за 3 робочих дні.
Як зменшити кількість draw calls?
Draw call — команда CPU до GPU «намалюй це». Кожен виклик має overhead незалежно від складності геометрії. На мобільних 200–300 draw calls за кадр — межа. Мета: мінімізувати їх кількість, об’єднуючи геометрію з однаковим матеріалом.
Static Batching об’єднує нерухомі меші при збірці. Вимога: прапорець Static на об’єкті та однаковий матеріал. Ефективний для статичного оточення, але збільшує споживання пам’яті — об’єднаний меш зберігається окремо. Static Batching може знизити draw calls в 3 рази, але збільшує пам’ять на 20% порівняно з іншими методами. У сценах з тисячами статичних об’єктів слідкуйте за пам’яттю через Memory Profiler.
Dynamic Batching об’єднує меші в рантаймі з жорсткими обмеженнями: менше 900 вертексних атрибутів на меш, однаковий матеріал та масштаб. На практиці ефективний лише для дрібних об’єктів (партикли, UI). В URP за замовчуванням вимкнений — його витіснив SRP Batcher.
SRP Batcher — не класичний батчінг, а оптимізація CPU-overhead при підготовці draw calls. Замість того щоб кожен кадр заново завантажувати uniform-дані шейдера, SRP Batcher кешує їх у GPU-пам’яті та оновлює лише ті, що змінилися. Draw calls залишаються колишніми за кількістю, але кожен займає менше часу CPU — SRP Batcher дає приріст в 2–3 рази по CPU-часу рендеру. Вимога: шейдер має бути сумісний з SRP Batcher (декларувати per-object властивості в UnityPerDraw CBUFFER). Стандартні URP Lit/Unlit шейдери сумісні. Кастомні — перевіряємо в Inspector матеріалу: SRP Batcher compatible: Yes/No. Для ввімкнення переконайтеся, що в URP Asset опція SRP Batcher активна, а для кастомних шейдерів використовуйте макрос UNITY_INSTANCING_BUFFER і декларуйте per-object властивості в блоці CBUFFER_START(UnityPerDraw). Після ввімкнення в Profiler має знизитися час RenderLoop.Draw на CPU.
GPU Instancing — для множини копій одного меша з одним матеріалом (дерева, трава, NPC). Відправляє один draw call з масивом per-instance даних. Вмикається на матеріалі: Enable GPU Instancing. Обмеження: всі інстанси в одному batch повинні мати однаковий матеріал і меш. Graphics.DrawMeshInstanced / Graphics.DrawMeshInstancedIndirect — для процедурного рендерингу без GameObject overhead.
Вибір методу залежить від сценарію: Static Batching підходить для статичного оточення, SRP Batcher виграє в проектах з множиною унікальних матеріалів, GPU Instancing незамінний для масових об’єктів (ліс, натовп), Dynamic Batching — лише для дрібних та рідкісних випадків. На практиці ми комбінуємо все, починаючи з профілювання.
| Метод |
Тип об'єктів |
Вплив на CPU |
Вплив на GPU |
Споживання RAM |
| Static Batching |
Статичні |
Помірне зниження |
Без змін |
Збільшується |
| Dynamic Batching |
Дрібні (≤900 вертексів) |
Зниження |
Без змін |
Без змін |
| SRP Batcher |
Будь-які (сумісні шейдери) |
Значне зниження |
Без змін |
Без змін |
| GPU Instancing |
Копії одного меша |
Мінімальне |
Значне зниження |
Незначно |
Детальніше про технологію — на Wikipedia.
Чому оптимізація пам'яті критична для мобільних ігор?
Мобільні платформи — жорсткі обмеження по RAM. iOS вбиває додаток при перевищенні пам’яті без попередження (memory pressure kill). Android — аналогічно, але з onLowMemory callback. Цільові бюджети: iOS — <1 GB для сучасних пристроїв, <512 MB для підтримки iPhone 8/X; Android — <800 MB для широкої сумісності (ОС займає 400–600 MB). Типова економія після нашої оптимізації — 30–50% оперативної пам’яті при збереженні якості. Середня економія бюджету на етапі оптимізації — від $3,000 до $8,000 залежно від складності проекту, що підтверджується нашими кейсами.
Addressables та Asset Bundles: як не перевищити бюджет
Завантажувати все одразу при старті — неприйнятно для великих проектів. Addressables (надбудова над Asset Bundles) — система адресованого асинхронного завантаження асетів. Явне вивантаження: Addressables.ReleaseInstance / Addressables.Release. Addressables не вивантажують асети автоматично при знищенні об’єкта. Типова помилка: Addressables.InstantiateAsync у циклі без Release — пам’ять росте до крашу. Reference counting: асет вивантажується тільки коли всі його handles звільнені. Архітектурний патерн: сервіс/менеджер тримає handle завантаженого асета, звільняє при переході між сценами.
Groups та Bundle Strategy: групуємо асети за логікою завантаження. Наприклад, всі асети одного рівня — в один bundle, шарені (UI, шрифти) — в окрему групу з Prevent Updates. Така стратегія економить до 30% пам’яті.
Texture Memory: звідки береться 70% займаного об'єму
Текстури — основний споживач пам’яті. Аналіз через Memory Profiler: вкладка All Of Memory → Texture2D — одразу видно список найважчих текстур. На практиці ми знаходимо текстури з завищеним Max Size (4096 для мобільної іконки — типова помилка). Заходи: Mipmap для 3D-текстур (ввімкнути), для UI (вимкнути); Streaming Mipmaps для open world — завантажує mip-рівні по мірі наближення камери. Красовська проблема: текстури, на які посилаються невикористовувані Materials, залишаються в пам’яті — Memory Profiler покаже reference chain. Видаляйте зайві матеріали. Після заміни всіх текстур формату RGBA32 на ASTC 6×6 на Android економія досягає 60% без втрати якості.
GC Allocations: як усунути фризи в Hot Path
C# garbage collector в Unity — stop-the-world. Якщо за кадр алоційовано багато heap-пам’яті, GC-пауза викликає видимий фриз. Ціль: нульові алокації в hot path (Update, FixedUpdate, рендер). Типові джерела: string конкатенація в Update (замінюємо на StringBuilder), LINQ в hot path (ручні цикли з предалоційованими списками), GetComponent<T>() кожен кадр (кешуємо в Awake/Start), boxing value types при передачі в object параметри. Після профілювання за допомогою Unity Profiler ми знижуємо алокації в hot path на 95% — фризи зникають.
Як відсікти зайві 40% draw calls? LOD та Culling
LOD Group — перемикання на спрощену геометрію при віддаленні об’єкта від камери. Стандарт для 3D оточення: LOD0 (100%), LOD1 (30–50% трикутників), LOD2 (10–15%), Culled. Для мобільних поріг Culled ставимо агресивніше — менше малюємо за кадр. LOD знижує draw calls на 40–60% залежно від кількості об’єктів.
Occlusion Culling — Unity не рендерить об’єкти за стінами. Вимагає запечені occlusion дані. Для indoor сцен знижує draw calls на 20–40%.
Frustum Culling працює автоматично — об’єкти поза FOV камери не рендеряться. Але draw call на перевірку все одно відбувається. Для сцен з тисячами об’єктів — кастомний spatial partitioning (Quadtree, Octree). В одному з проектів впровадження оклюзійного калінгу та LOD знизило загальну кількість draw calls з 2800 до 450 на Android.
Оптимізація VR: як утримати 72 FPS на Quest 3
VR — окремий клас задач. Фреймрейт 72/90 Hz не можна порушувати, інакше motion sickness. Додатково до стандартних методів: Single Pass Instanced Rendering — рендер для обох очей за один прохід (знижує draw calls в 2 рази); Fixed Foveated Rendering (Quest) — зниження роздільної здатності на периферії; Late Latching (Quest 3) — оновлення позиції контролера максимально пізно перед рендером; Dynamic Resolution в URP/HDRP — автоматичне зниження роздільної здатності рендера при просадці fps. Для Quest профілюємо через OVR Metrics Tool — показує CPU/GPU time прямо в гарнітурі. Після застосування цих методів частота кадрів на Quest 2 стабілізується на 72 FPS навіть у сценах з 1.5 млн полігонів.
Які етапи оптимізації та терміни?
Ми пропонуємо комплексну послугу «під ключ»:
-
Профілювання на цільових пристроях — збір метрик (FPS, draw calls, пам’ять, GC). Аналізуємо 10–15 ключових сценаріїв за 3–5 робочих днів.
-
Звіт з пріоритетами — які проблеми критичні, які можна відкласти. Пріоритети виставляємо на основі впливу на ігровий досвід.
-
Впровадження оптимізацій — батчінг, LOD, Addressables, шейдери, occlusion culling. Середній цикл впровадження — 2–4 тижні.
-
Повторне профілювання — замір покращень. Типовий приріст FPS — 30–60% на мобільних пристроях.
-
Документація — рекомендації по підтримці та подальшій розробці.
-
Навчання команди — як не допустити регресу. Проводимо воркшопи з профілювання та оптимізації.
Завдяки запобіганню переробок на пізніх етапах замовник отримує значну економію. Отримайте консультацію — ми безкоштовно оцінимо ваш проект і покажемо потенціал оптимізації. Звертайтеся, щоб дізнатися точні терміни та вартість для вашого стеку.