Оптимізація Графічного Процесора

Вступ

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

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

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

Виклики малювання, зміни стану та API

Примітка

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

Godot надсилає інструкції графічному процесору через графічний API (OpenGL, OpenGL ES або Vulkan). Комунікація та діяльність драйвера можуть бути досить дорогими, особливо у випадку OpenGL та OpenGL ES. Якщо ми зможемо надати ці інструкції у спосіб, якому надають перевагу драйвер і графічний процесор, ми зможемо значно підвищити продуктивність.

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

2D пакетування

У 2D, вартість обробки кожного елемента окремо, може бути непомірно високою - на екрані їх можуть бути тисячі. Ось чому в 2D використовується пакетування. Кілька схожих об'єктів групуються разом і вимальовуються в пакеті, за допомогою одного виклику малювання, замість того, щоб робити окремий виклик малювання для кожного об'єкта. Крім того, це означає, що зміни стану, матеріалу і текстури можуть бути зведені до мінімуму.

Для отримання додаткової інформації про 2D пакетування дивіться Оптимізація за допомогою пакетування.

3D пакетування

У 3D ми все ще прагнемо мінімізувати виклики малювання та зміни станів. Однак, об'єднати декілька об'єктів в один виклик малювання може бути складніше. Меші 3D, як правило, складається з сотень, або тисяч, трикутників, а об'єднання великих мешів у реальному часі є надзвичайно дорогим. Витрати на їх об'єднання швидко перевищують будь-які вигоди, оскільки кількість трикутників в одному меші різко зростає. Набагато кращою альтернативою є завчасне поєднання мешів (статичні меші відносно один одного). Це може бути зроблено або художниками, або програмно в межах Godot.

Пакетне об'єднання об'єктів у 3D також має певні витрати. Кілька об'єктів, зображених як один, не можуть бути вибраковані окремо. Ціле місто, яке знаходиться за межами екрану, все одно буде зображене, якщо його приєднати до однієї травинки, яка знаходиться на екрані. Таким чином, при спробі об'єднати 3D-об'єкти слід завжди враховувати розташування об'єктів та можливість їхнього відсіювання. Незважаючи на це, переваги об'єднання статичних об'єктів часто переважають інші міркування, особливо для великої кількості віддалених або низькополігональних об'єктів.

Для отримання додаткової інформації про специфічні 3D-оптимізації див. Optimizing 3D performance.

Повторне використання шейдерів та матеріалів

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

  • Повторне використання матеріалів: Чим менше різних матеріалів у сцені, тим швидшим буде рендеринг. Якщо у сцені велика кількість об'єктів (сотні або тисячі), спробуйте використовувати матеріали повторно. У гіршому випадку, використовуйте атласи, щоб зменшити кількість текстурних змін.

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

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

Вартість пікселя в порівнянні з вартістю вершини

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

На сучасному ПК та консолі вартість вершини є низькою. Спочатку графічні процесори рендерили лише трикутники. Це означало, що кожен кадр:

  1. Всі вершини повинні були бути перетворені процесором (включаючи відсікання).

  2. Всі вершини потрібно було відправити в пам'ять графічного процесора з основної оперативної пам'яті.

Сьогодні все це обробляється всередині графічного процесора, що значно підвищує продуктивність. 3D-художники зазвичай мають хибне уявлення про продуктивність багатокутників, оскільки 3D DCC (такі як Blender, Max тощо) повинні зберігати геометрію в пам'яті процесора для її редагування, що знижує реальну продуктивність. Ігрові рушії більше покладаються на графічний процесор, тому вони можуть рендерити багато трикутників набагато ефективніше.

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

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

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

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

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

Зверніть увагу на додаткову обробку вершин, необхідну при використанні:

  • Скінінг (скелетної анімації)

  • Морфінг (ключі форми)

  • Об'єкти з підсвічуванням вершини (поширені на мобільних)

Піксельні/фрагментні шейдери та швидкість заповнення

На відміну від обробки вершин, витрати на фрагментарне (на піксель) затінення значно зросли з роками. Роздільна здатність екранів зросла (площа 4K-екрану становить 8 294 400 пікселів проти 307 200 для старого 640×480 VGA-екрану, тобто в 27 разів більше), але також зросла і складність фрагментних шейдерів. Фізичний рендеринг вимагає складних обчислень для кожного фрагмента.

Перевірити, чи не обмежена частота заповнення проекту, дуже просто. Вимкніть V-Sync, щоб запобігти обмеженню кількості кадрів на секунду, а потім порівняйте кількість кадрів на секунду при роботі з великим вікном і при роботі з дуже маленьким вікном. Якщо ви використовуєте тіні, вам також може бути корисно зменшити розмір карти тіней. Зазвичай, ви побачите, що FPS значно зростає при використанні маленького вікна, що вказує на те, що у вас певною мірою обмежена швидкість заповнення. З іншого боку, якщо збільшення FPS незначне або взагалі відсутнє, це означає, що вузьке місце лежить деінде.

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

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

Читання текстур

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

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

Стиснення текстури

За замовчуванням Godot стискає текстури 3D-моделей при імпорті, використовуючи стиснення відеопам'яті (VRAM). Стиснення відеопам'яті не таке ефективне при зберіганні, як PNG або JPG, але значно підвищує продуктивність під час малювання достатньо великих текстур.

Це пов'язано з тим, що основною метою стиснення текстур є зменшення пропускної здатності між пам'яттю та графічним процесором.

У 3D, форма об'єктів більше залежить від геометрії, ніж від текстури, тому стиснення, як правило, непомітне. У 2D стиснення більше залежить від форм всередині текстур, тому артефакти, що виникають в результаті 2D стиснення, більш помітні.

Попередження: більшість пристроїв Android не підтримують стиснення текстур з прозорістю (тільки непрозорі), тому майте це на увазі.

Примітка

Навіть у 3D для "піксельних" текстур слід вимкнути стиснення VRAM, оскільки воно негативно вплине на їхній зовнішній вигляд, не покращуючи при цьому продуктивність через низьку роздільну здатність.

Постобробка та тіні

Ефекти постобробки та тіні також можуть бути дорогими з точки зору активності затінення фрагментів. Завжди перевіряйте їхній вплив на різному обладнанні.

Зменшення розміру карти тіней може підвищити продуктивність, як з точки зору запису, так і з точки зору читання карти тіней. Крім того, найкращий спосіб покращити продуктивність тіней - це вимкнути тіні для якомога більшої кількості джерел світла та об'єктів. Для менших або віддалених OmniLights/SpotLights часто можна вимкнути тіні, що матиме лише невеликий візуальний вплив.

Прозорість і змішування

Прозорі об'єкти створюють особливі проблеми для ефективності рендерингу. Непрозорі об'єкти (особливо у 3D) можна візуалізувати у будь-якому порядку, а Z-буфер гарантує, що затінюватимуться лише передні частини об'єктів. З прозорими або змішаними об'єктами все інакше. У більшості випадків вони не можуть покладатися на Z-буфер і мають бути відрендереними у "порядку художника" (тобто ззаду наперед), щоб виглядати коректно.

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

Непрозорі об'єкти можуть цього не робити. Зазвичай вони можуть скористатися перевагами Z-буфера, записуючи спочатку в Z-буфер, а потім застосовуючи фрагментний шейдер лише до "переможного" фрагмента, тобто об'єкта, який знаходиться спереду на певному пікселі.

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

Мультиплатформні поради

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

Загалом, вам слід розробляти гру до найменшого спільного знаменника, а потім додавати опціональні покращення для більш потужних платформ. Наприклад, ви можете використовувати бекенд GLES2 як для десктопних, так і для мобільних платформ, якщо ви орієнтуєтесь на обидві платформи.

Мобільні/плиткові рендерери

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

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