Найкращі методи оптимізації білдів для консолей. Досвід роботи на Unity, UE та нюанси рендеру

Вітаю, мене звати Надія Гавриленко, я обіймаю посаду Unity Developer у Pingle Studio. В компанії ми часто займаємось оптимізацією ігор, тож у цій статті ми з моїми колегами обговоримо найкращі методи оптимізації білдів для консолей, які також можуть допомогти покращити продуктивність гри на ПК. Я і Олександр Макеєв (Unity Developer) розповімо про оптимізацію на Unity, Костянтин Олещенко (Project Lead of Unreal Depatrment) — про виклики на рушії Unreal, а Render Developer Євген Карпенко окремо розкаже про нюанси зі сторони рендерингу.

Оптимізація ігор на ПК і на консолі відрізняється. Добре оптимізована гра для ПК покращує досвід гравця, проте не є критичною потребою. Натомість консольна гра має відповідати багатьом критеріям, аби взагалі бути опублікованою. Сувора та стабільна частота кадрів, ніяких довгих статичних екранів, обмеження щодо використання всієї оперативної пам’яті тощо — усі ці фактори важливі для доступу на консольні ринки.

Ігри на консолях повинні мати стабільний fps, але цифра може відрізнятися залежно від складності гри та підтримуваної роздільної здатності екрана (4K, FullHD або нижче).

Найпоширенішими параметрами є:

  • 30 кадрів на секунду для консолей Gen-8 і Nintendo Switch;
  • 60 кадрів на секунду для більшості ігор на PS5 і Xbox Series;
  • Іноді до 120 кадрів на секунду для ігор на консолях PS5 і Xbox Series.

Оскільки залізо завжди визначено заздалегідь, розробник сам має вибрати відповідну роздільну здатність і fps, щоб умістити якомога більше якісної картинки.

Оптимізація на Unity

Надія Гавриленко, Unity Developer — про оптимізацію ігор на Unity

Існують різні способи оптимізації ігор. Для ПК і консолей процес оптимізації схожий і складається з двох етапів: виявлення проблеми та її усунення.

Unity надає різні інструменти для виявлення проблеми, такі як Profiler, Memory Profiler і Frame Debugger. У випадку портування, цільова платформа може мати інструменти профайлінгу, розроблені спеціально під неї. Правильне використання функціональних можливостей devkit забезпечує глибше вивчення проблеми, що є надзвичайно корисним у випадках, коли універсальні інструменти ПК не працюють через відмінності платформ.

Для усунення проблем потрібно використовувати творчий підхід. Щойно проблема визначена, потрібно докласти зусиль для того, щоб вирішити її цілком — або знайти задовільний компроміс.

Напрямки оптимізації можна розділити на три компоненти: центральний процесор (CPU), графічний процесор (GPU) та пам’ять.

Найпоширеніші практики включають наступне:

Модифікація повільно працюючих фрагментів коду — усі розуміють важливість якості коду, але не всі приділяють цьому належну увагу. Переписування «повільних» фрагментів коду, переміщення їх до асинхронного потоку для роботи у фоновому режимі або кешування даних для повторного використання пізніше, швидше за все, одразу покращить fps на білді. Але треба зауважити, що для виконання цих дій може знадобитися додаткова пам‘ять.

Правильне використання пам‘яті. Для покращення продуктивності гри треба слідкувати за використанням пам‘яті. Виявлення та належне виправлення подібних проблем в Unity вимагає глибокого розуміння управління пам‘яттю та знання структури кожного конкретного проєкту. Unity Garbage Collector виконує за нас роботу очищення сміття, але може перевантажувати процесор і пам‘ять, тож треба про це пам‘ятати, навіть якщо він працює у фоновому режимі. Якщо один кадр створює багато байтів сміття, інший витратить багато мілісекунд, щоб очистити його.

З точки зору оптимізації графічного процесора, як тільки буде виявлено кадр/сцену, візуалізація якої потребує багато ресурсів, можна зробити наступне:

  • покращити batching;
  • оптимізувати задіяні шейдери;
  • «запікати» (baking) більше даних, щоб уникнути їх обчислення під час виконання (що вимагатиме більше пам‘яті);
  • передати деякі обчислення на центральний процесор (що займе більше часу на ньому, але розвантажить графічний процесор).

Важливо додатково приділяти увагу постобробці, оскільки більшість пакеджів оптимізовано для ПК, а не для консолей. Оптимізація GPU часто має свої тонкощі залежно від платформи.

Оптимізація для консолей вимагає кропіткої роботи, і зазвичай ми витрачаємо на це багато часу. В кінці процесу може виявитись, що деякі оптимізації не потребують компромісів із якістю та можуть принести користь будь-якій платформі: це можуть бути оптимізовані алгоритми, які все ще дають той самий результат, або використання підходів до асинхронного чи паралельного виконання, або прості недогляди в налаштуваннях чи ієрархії, які були знайдені та виправлені тощо.

Інші оптимізації, як-от зниження якості текстури, спрощення пост-обробки та додавання обмежень на кількість створюваних об‘єктів, дещо змінять якість зображення, обсяг використовуваної пам’яті та навантаження на процесор, а в рідкісних випадках обмежать ігровий процес. У цьому випадку доцільно використовувати їх лише для платформ, яким це справді потрібно.

Олександр Макеєв, Unity Developer — про найбільш поширені проблеми під час оптимізації та способи їх вирішення

Різні оптимізації можуть допомогти грі працювати стабільніше, з меншою кількістю збоїв і вищим fps.

Кожен тип проблеми вимагає різних дій, але підхід до вирішення їх усіх в Unity практично однаковий. Найкращим другом у цьому випадку є Unity Profiler.

Цей інструмент допомагає діагностувати першопричину проблем із продуктивністю гри. Використання профайлера разом із Deep Profiling дає змогу бачити виділення пам‘яті, витрачений процесорний час, і все це з прив’язкою до конкретних функцій в коді гри. Профайлер в Unity складається з багатьох різних інструментів, але найважливішими для оптимізації є вікно Profiler та Memory Profiler.

Якщо говорити про конкретні випадки, то ось найпоширеніші проблеми та способи їх вирішення:

Тривалі просадки та зависання

Їх можна виявити по пікам на графіку у вікні Profiler. Однією з найпоширеніших речей, які спричиняють зависання та просадки, є робота Garbage Collector, створення великої кількості об’єктів в одному кадрі та синхронна обробка важких алгоритмів. Щоб зменшити час роботи GC та те, як часто він відпрацьовує, необхідно знайти у вікні Profiler функції, які генерують багато сміття, оптимізувати їх, щоб не залишати об‘єктів, які вже не будуть використовуватися згодом.

Іноді деякі вимогливі функції займають велику частину кадру і від них не можна просто позбутися. Можна подумати над тим, щоб перевести їх на асинхронне виконання або принаймні розподілити навантаження на кілька кадрів за допомогою Coroutines.

Нестача пам‘яті

Це поширена проблема для консольних ігор, оскільки доступна пам‘ять набагато менша, ніж на ПК. Ця проблема є основною причиною критичних збоїв та вильотів. Використання Memory Profiler, дозволяє побачити, які об‘єкти займають велику частину пам’яті, а які залишаються в пам’яті без подальшого використання.

Дуже часто залишаються текстури, оскільки вони мають native та managed частини, котрі мають посилання одна на одну. Це не дозволяє GC виявити їх для звільнення пам’яті. Підказка: завжди давайте назву текстурам, які ви створюєте!

Крім того, залишення повних масивів/списків об‘єктів, які не використовуватимуться пізніше, або створення нових масивів/списків замість повторного використання старих призводить до витоку пам‘яті (наприклад, списки векторів, які були необхідні при генерації рівня, проте не потрібні в подальшому геймплеї).

Довгі екрани завантаження

Часто спричинені процесами, які не обов‘язково мають виконуватися прямо зараз. Іноді можна відкласти завантаження деяких речей, якщо вони знаходяться поза кадром або навіть не поряд з гравцем. Такі об’єкти можна завантажити пізніше вже під час геймплею. Всі згадані вище проблеми також можуть бути причиною повільного завантаження гри. Об‘ємне утворення сміття, коли GC змушений відпрацьовувати кілька разів за кадр, уповільнює процес завантаження. Використання вікна Profiler для скорочення часу виконання ваших функцій генерації/завантаження має допомогти.

Низька продуктивність гри загалом

Важливо розуміти, де знаходиться bottleneck гри. Залежно від того, чи це CPU чи GPU, слід розглянути різні дії.

Для оптимізації CPU варто спробувати зменшити постійне навантаження в методах Update() чи будь яких інших функціях, що часто викликаються. Навіть мінімальне зменшення часу обробки може призвести до значних покращень, якщо функція викликається тисячі разів на кадр. Все описане в попередніх пунктах також допоможе покращити загальну продуктивність.

Для оптимізації GPU потрібно розглянути можливість використання Levels Of Detail — системи, яка показує менш детальну версію моделі залежно від області, яку ця модель займає на екрані. Крім того, значний приріст може бути забезпечений застосуванням Dynamic Resolution, що дозволить згладити моменти великого навантаження в грі, коли на екрані багато об’єктів і ефектів. Зниження роздільної здатності в цей момент зазвичай не таке помітне, особливо в поєднанні з будь-яким методом швидкого масштабування, але допомагає підтримувати стабільну кількість кадрів на секунду.

Якщо виконуються важкі фізичні обчислення, слід подумати про оптимізацію — наприклад, використання Layers для фізичних взаємодій, піти на компроміс за допомогою менш коштовних фізичних методів, менш частого їх використання чи зменшення точності фізичних підрахунків.

І найважливіше — не намагайтесь оптимізувати те, що і так достатньо добре працює. Вирішуйте проблеми по мірі того, як вони з’являються.

Оптимізація на Unreal Engine

Костянтин Олещенко, Project Lead of Unreal Department — про оптимізацію ігор на UE

Незалежно від платформи, загальні завдання оптимізації залишаються незмінними — виявлення проблем і їх усунення. Але тепер розглянемо це з урахуванням особливостей Unreal Engine.

Однією з перших речей, на яку рекомендую звернути увагу, є тип рендеру — Forward (більш притаманний мобільним платформам) або Deferred (тип за замовчуванням, підходить для більшості платформ). Вибір найбільш підходящого типу рендеру саме для вашої гри є першим кроком для оптимальної продуктивності будь-якого проєкту на Unreal Engine.

Наступними рекомендую розглянути обмеження платформи. Це типово для консолей та мобільних платформ. Для ПК варто звернути увагу на конфігураційні пресети, чим менші мінімальні системні вимоги — тим ширше охоплення.

Найпоширеніші обмеження консольних та мобільних платформ:

Postprocess — на Forward рендері вони у більшості кейсів працювати не будуть, тож потрібна їх переробка. Як приклад, можна замінити постпроцеси такими речами, як Tonemapper та налаштувати його для досягнення найкращої, наскільки це можливо, ​​якості зображення.

Decals — на Forward рендері не працюють. Достатньо легко переробити на Plane + Material, візуально буде майже так само зі значно меншим оверхедом по навантаженню.

Джерела світла та інші обмеження, пов‘язані зі світлом — на мобільних платформах є суттєве обмеження щодо кількості Movable джерел світла. Зазвичай потрібна суттєва переробка (заміна Movable на статичне світло, перезапікання лайтмап тощо. Movable використовується лише у місцях, де без них не обійтись).

Те саме стосується ресурсів: текстур, бінарних файлів скелетної анімації та відображення шейдерів, які також можуть вимагати перекомпіляції у внутрішні формати, що використовуються платформою для досягнення кращої продуктивності. З цим пов’язано багато обмежень (як приклад, PS5 звуковий кодек підтримує лише певний діапазон бітрейту, що потребує ресемплинга особливо детальних звуків).

Звісно ж, проводиться оптимізація мешів (додаються більш спрощенні LODи, використовуються багато алгоритмованих кодеків для перестискання текстур, спрощуються матеріали без шкоди візуалу та ін.)

Наступне, що варто обговорити, — це перфоманс спайки (різкі просадки fps на короткий проміжок часу), які трапляються в будь-якому проєкті портування.

Ми розподіляємо їх на три загальні категорії.

Візуальні ефекти VFX

Багато ігор на Unreal Engine мають важкі візуальні ефекти, які можуть добре працювати на комп‘ютерах з потужними графічними процесорами. Тим не менш, з ними є проблеми навіть на консолях останнього покоління.

Щоб зменшити навантаження, викликане VFX, ми працюємо за двома основними напрямами:

  • Тісно співпрацюємо з командами VFX і Tech Art і оптимізуємо кожен можливий спецефект, щоб знайти баланс між картинкою та перфомансом.
  • Якщо необхідно, то тонко налаштовуємо MemoryManagement логіку для найоптимальнішого використання наявного бюджету пам’яті (особливо актуально для платформ Switch, Oculus та PICO).

Звукові ефекти

Якщо на проєкті є Wvise — то це дуже сильно полегшує роботу зі звуком. В інших випадках ми проводимо аналіз використання та завантаження звуків та ефектів, проводимо переналаштування їх за категоріями (бо з коробки рушій саме за категорією визначає, чи потрібно тримати якийсь звук у кеші постійно, чи можна його вже вивантажувати). Тонке налаштування саунд-кешу забирає багато часу, але винагороджується майже ідеальним балансом перформанс-память.

Як приклад, Android-платформа доволі погано оброблює звуки класу SoundCue (різниця між таким самим SoundWave приблизно у 3-4 рази за навантаженням на CPU). Тож у випадку з Android робиться необхідна переробка на більш перформанс орієнтований флоу.

PSO спайки

Ці просадки виникають, коли ігрове світло раптово змінюється через певні ігрові сценарії. Здебільшого їх можна виправити, ретельно налаштувавши кеш PSO. Ця операція часто займає багато часу: збір даних, компіляція зібраних даних у спеціальний файл, що потім має використовуватись при збірці наступних білдів, потрібно налаштовувати логіку збору кешу під час запуску аплікації тощо. Але результат вартує витрачених зусиль.

Оптимізація рендера

Євген Карпенко, один із ключових рендер інженерів Pingle — про оптимізацію рендера

При портуванні гри на консолі з боку рендерингу розробники можуть зіткнутися з такими проблемами:

  • Консолі різних поколінь та виробників мають різну архітектуру, тож вимагають оптимізацію та реалізацію коду рендерингу під кожну платформу.
  • Консолі зазвичай мають менш потужне залізо, ніж сучасні ПК, що може призвести до необхідності зниження якості графіки.
  • Консолі одного покоління можуть мати різні графічні процесори та архітектуру пам’яті, що може призвести до необхідності оптимізації рендерингу для кожної платформи.

Навіть у рамках одного покоління та одного виробника консолі можуть мати різні моделі з різним набором характеристик. Наприклад, PS4, PS4 Pro та PS4 Slim, а також Xbox One, Xbox One S та Xbox One X. Це також може призвести до необхідності навіть у рамках однієї консолі реалізовувати різні за функціоналом рендери.


Ще однією дуже важливою проблемою є сумісність графічних API консолей з графічним API оригінальної гри. Найкращим випадком є ​​портування DirectX гри на консолі Xbox, оскільки графічне API Xbox майже повністю, крім деяких моментів, сумісне з DirectX.

Якщо оригінальна гра була реалізована на іншому API, то можуть виникнути моменти несумісності та відмінності між API. До таких відмінностей можна віднести декларативну модель формування команд відмальовки, різницю між системами координат, концепцію «станів» рендерингу, шейдерну мову та шейдерну модель.

OpenGL, DirectX та Metal — це три популярні графічні API, що використовуються для розробки ігор і 3D-додатків, і при портуванні на конкретні ігрові консолі, розробники можуть зіткнутися з описаними вище труднощами.

Всі три графічні API використовують свою систему координат для визначення положення та орієнтації об’єктів у 3D-просторі. OpenGL та Vulkan мають вісь Y, спрямовану вгору. DirectX, Metal та консолі мають вісь Y, спрямовану вниз у Clip Space та координатах текстури.

OpenGL у clip space використовує діапазон глибини [-1, 1], де ближня площина — (-1), а дальня площина — (1). DirectX і Metal у clip space використовують діапазон глибини [0, 1], де ближня площина (0), а дальня площина (1).

Ці відмінності можуть викликати проблеми при перенесенні коду між API, а загальне рішення включає переворот осі Y в шейдерах при портуванні з OpenGL/Vulkan і налаштування обчислень буферу глибини на основі API.


Також є ключові відмінності у формуванні команд рендерингу між Metal, OpenGL та DirectX. Vulkan, DirectX 12 та Metal фокусується на низькорівневому підході, орієнтованому на буфер команд. Розробники явно записують команди рендерингу у чергу, яку графічний процесор ефективно виконує.

OpenGL та DirectX 11 пропонують підхід, що більш орієнтований на стан. Розробники встановлюють різні стани (наприклад, текстури, шейдери), що впливають на роботу наступних викликів рендерингу. У той же час DirectX 11, подібно до OpenGL, використовує підхід на основі станів, але з більш об’єктно-орієнтованою структурою.

Vulkan, DirectX 12 та Metal використовують попередній запис команд рендерингу у буфер команд. Це дозволяє графічному процесору оптимізувати виконання та знижує навантаження на драйвери. OpenGL та DirectX 11 залежать від змін стану, що може призвести до надмірних викликів і менш ефективного виконання складних сцен із частими змінами стану.


До складнощів портування рендерингу на консолі також можна віднести різницю між шейдерними моделями. Моделі шейдерів — це специфікації, які визначають можливості та функції, доступні у шейдерах графічного процесора (GPU). Різні версії пропонують різні рівні складності та функціональності, впливаючи на візуальні ефекти, що досяжні в іграх та застосунках.

Моделі шейдерів визначають набір інструкцій, типи даних та функції, що підтримуються графічним процесором для цих програм шейдерів.

У моделях шейдерів можуть бути представлені наступні функції:

  • складніші моделі освітлення (наприклад, фізичний рендеринг);
  • розширені методи фільтрації текстур (наприклад, анізотропна фільтрація);
  • тесселяційні шейдери для детальної геометрії;
  • обчислювальні шейдери для обчислень загального призначення та асинхронних обчислень на графіцІ;
  • більш точні числа з плаваючою комою для обчислень, що призводить до більш точних результатів та зменшення візуальних артефактів.

Різні консолі, у різних випадках, які описані на самому початку, можуть по-різному реалізовувати набір функцій у шейдерній моделі. Це вимагає від розробників більш детальної роботи з портування рендерингу. Сюди може входити як повна або часткова зворотна сумісність між реалізацією шейдерних моделей, що використовуються в портованій грі, так і реалізація відсутнього функціоналу в шейдерній моделі або його альтернатива.

Крім того, всі графічні API мають свою мову програмування шейдерів та системи шейдерів, які також можуть бути несумісні між графічними API. GLSL, HLSL та Metal Shading Language (MSL) — це мови, що використовуються для написання шейдерів, але вони обслуговують різні графічні API та мають деякі ключові відмінності.

Всі три можуть створювати вершинні, піксельні та геометричні шейдери, які керують тим, як об’єкти відображуються на екрані. Але всі вони мають різний синтаксис, різний процес компіляції шейдерів, різний процес завантаження і рефлексії, різний процес біндингу ресурсів і даних для шейдерів.

При портуванні гри на консолі, з погляду рендерингу, саме код шейдерів і система компіляції може бути великою проблемою, тому що різні графічні API, особливо консольні, можуть на дуже різних рівнях абстракції реалізовувати біндинг ресурсів для шейдерів, компіляцію і можливості шейдерів, для досягнення вищої продуктивності та використання апаратних засобів.

Те саме стосується ресурсів: текстури, бінарні файли скелетної анімації, рефлексія шейдерів, все це може також вимагати перекомпіляцію цих ресурсів у внутрішні формати, які використовуються консоллю для досягнення кращої продуктивності.


Крім усього цього, через те, що консолі мають мають менш потужне залізо, ніж сучасні ПК, під час реалізації рендерингу можуть бути використані різні техніки.

Однією з таких технік є upscaling. У відеоіграх масштабування відноситься до методів, які збільшують роздільну здатність візуалізованого зображення. Це стає критично важливим при перенесенні гри з ПК (де роздільна здатність може сильно відрізнятися) на консолі (з фіксованою роздільною здатністю).

Під час апскейлінгу гра рендериться з оптимальною для консолі роздільною здатністю, щоб гарантувати 60 fps, ця роздільна здатність може бути набагато меншою за прийняті стандарти, наприклад в 720p, а потім, програмними або апаратними засобами консолі роздільна здатність збільшується до більш прийнятного формату.

Щодо ресурсів, то в більшості випадків йде оптимізація текстур. Сюди можна віднести зміну форматів, перегін текстури у внутрішні формати консолі, перегін текстур в упаковані формати, які можуть гарантувати швидше завантаження текстур. Також банальна зміна розміру текстур, особливо у випадку використання upscale, коли оригінальний розмір текстур не потрібен, якщо гра рендериться у зниженій роздільній здатності.

Для шейдерів прикладами оптимізації може бути переписування непродуктивних ділянок коду, вибору більш відповідних типів даних, вибір менш складних та оптимальних алгоритмів шейдингу. Одним із реальних прикладів оптимізації на проєкті в Pingle є оптимізація формату вертексів геометрії, за допомогою якої можна прискорити завантаження геометрії на відеокарту та її відтворення.

Також прикладом оптимізації може бути реалізація простіших і менш вимогливих до продуктивності алгоритмів шейдингу. Наприклад, грубіші алгоритми затінення, Screen Space Ambient Occlusion. Перенесення real-time calculation алгоритмів і проходів рендеригу ​​в зазделегідь розраховані, наприклад — запікання карт тіней, запікання карт оточення та карт відображення. Змінюючи проходи відмальовки, що вимагають від консолі великих обчислень, на попередньо розраховані.

Оновлнення білду ПК за допомогою консольної оптимізації

Іноді ми бачимо, що недостатньо оптимізовані комп’ютерні ігри, після виходу патчів, починають працювати краще. Проте це далеко не ідеальний підхід, адже ексклюзивних ігор для ПК не так вже й багато.

Якщо ви плануєте випустити гру одразу на кілька платформ, вкрай важливо звернути увагу на оптимізацію версій для консолей. Оптимізація, яка необхідна для консольних версій, може також покращити продуктивність гри на ПК.

Застосувавши методи оптимізації консолі до ПК-версій, розробники забезпечують високу продуктивність своїх ігор. Це критично важливо на ринку, де стабільність і плавність гри значно впливають на задоволеність гравців.

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному2
LinkedIn


Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікаво щодо Steam Deck. Українською поки не бачив матеріалів щодо цієї консолі

було би дуже цікаво подивитися на приклади того про що розповідає рендерінг інженер, може у нього є блог?

У пана Євгена був дуууже крутий блог ще десь в 2012ому у вк! І мені це допомогло вчитися, інших джерел тоді майже не було якісних)

Час від часу пан Євген пише цікаві речі у себе в твіттері x.com/CaptainGPU

Підписатись на коментарі