Блог розробників (FFF#281) – Трохи більше кадрів

Spread the love

опублікував posila

Трохи більше кадрів

Раніше в FFF (#264): “Не дивно, що сцени, в яких багато диму або дерев, можуть красти FPS, особливо в 4K. Можливо, ми повинні щось із цим зробити.”

Іноді це просто помилка

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

Наприклад, я зробив візуалізацію тільки для скриншота для FFF-264, і поки писав FFF, я не аналізував їх. Відразу після того, як повідомлення в блозі було випущено, я подивився на нього і задумався – чому дим створює такий величезний кульку перемальовування, коли він ледь помітний в грі? Що ж, виявилося, що це багато в чому пов’язано з помилкою. Деякий час тому ми оптимізували частки диму, щоб вони оновлювалися тільки один раз кожні 120 тиків (2 секунди), а їх анімація (рух, масштаб і непрозорість) інтерпольованого під час малювання.

Справа в тому, що частинки знищуються тільки під час їх поновлення, якщо час життя частинки закінчиться десь в середині вікна 120 тиків, частка буде як і раніше намальована до руйнування. Оскільки дим зникає і наростає протягом свого життя, він буде повністю прозорим на великій площі. Якщо частка дим не малювалася після свого терміну служби, то кількість частинок, намальованих на 15%, зменшується і ще більше зменшується число растрованих пікселів. Крім того, частинки з непрозорістю нижче 2% насправді нічого не додають до остаточного зображення, тому ми можемо сміливо не малювати їх, щоб отримати невелике додаткове покращення.

Оптимізація візуалізації діапазону стрільби турелі

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

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

У нас було дві ідеї, як це оптимізувати. Спочатку включити перевірку трафарету, щоб пікселі могли бути записані тільки один раз. По-друге, передати список турелей в графічний процесор і перевірити, чи знаходиться кожен піксель в діапазоні будь турелі, використовуючи піксельний шейдер. Ідея тестування трафарету дала приблизно 3-кратне прискорення в наших екстремальних тестових випадках (20×20 турелей), що було недостатньо добре – якби у вашого GPU була проблема з сіткою 3×3, у неї знову були б проблеми з сіткою 9×3, що не так уже й безглузда схема, щоб мати. Ідея пиксельного шейдера виявилася змішаною – якби весь екран знаходився в діапазоні, шейдер був би блискавичним, але як тільки пікселі виходили за межі діапазону, це означало, що шейдеру довелося б перебирати весь список турелей до малюнка через те, що це не покриває , продуктивність почала швидко падати. У гіршому випадку було б гірше, ніж без оптимізації.

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

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

Продуктивність GPU

Як завжди, корінь всього зниження продуктивності – доступ до пам’яті. Оскільки ми в основному робимо просте малювання спрайтів, графічний процесор повинен зчитувати пікселі з текстури і змішувати її до кадрового буфер. Технічно це означає читання пікселів з іншої текстури, виконання простих математичних операцій з двома значеннями і запис результату назад. Графічні процесори призначені для цього в широкому паралельному масштабі і оптимізовані для високої пропускної здатності пам’яті, при цьому жертвуючи затримкою пам’яті. Передбачається, що ви захочете зробити хоча б трохи математики для квітів, які ви вибираєте з текстур, щоб надати виходить зображення деякі динамічні деталі. У цьому випадку кожне ядро ​​графічного процесора може мати багато запланованих завдань і перемикатися між ними, оскільки поточна задача починає чекати завершення деякої операції з пам’яттю.

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

Очевидний спосіб поліпшити це – малювати менше пікселів. Це те, що я згадував раніше в FFF-227, розділяючи тіні дерев від стовбурів дерев, щоб видалити великі області повністю прозорих пікселів. Це може бути покращено шляхом малювання спрайтів не у вигляді прямокутників, а у вигляді загальних полігонів, які обгортають спрайт так, що велика частина повністю прозорих областей не буде Растеризувати. Це те, що ми, ймовірно, зробимо для найпроблемніших спрайтів (дерев і декоративних елементів).

Ще один спосіб зменшити кількість затінених пікселів – просто рендерить в більш низькій роздільній здатності. Насправді ми вже давно це робимо для джерел світла, але це може бути використано для інших ефектів, які не мають важливих високочастотних деталей, наприклад, для диму. В кінцевому рахунку, деякі графічні процесори не призначені для рендеринга гри в дозволі FullHD, незважаючи ні на що (наприклад, на думку спадають Intel HD Graphics 2500 або мультимедійні карти, такі як GeForce GT 710 і Radeon HD 6450), тому їм буде корисний варіант рендеринга. гри в більш низькій роздільній здатності з накладенням графічного інтерфейсу в своєму дозволі.

У FFF-227 я також згадав mipmaps – зменшені копії текстур, які використовуються при зменшенні текстури. Це допомагає краще використовувати кеші на графічному процесорі і, отже, зменшує необхідність доступу до основної пам’яті. Ми вже використовували mipmaps для дерев і декоративних елементів в 0.16, але, як це не парадоксально, у деяких людей були проблеми з продуктивністю, коли у них були включені mipmaps. Проблема в тому, що в 0.16 ми завжди використовуємо трилинейную фільтрацію для текстур з mipmapped. Це означає, що якщо ви хочете малювати у вигляді спрайту в масштабі 75%, графічний процесор буде отримувати піксель з mipmap зі 100% -ним масштабом і mipmap зі шкалою 50% і усереднювати їх, щоб отримати піксель для версії з масштабом 75%. Доступ до двох різних рівнів mipmap сповільнить роботу. У новому коді рендеринга ми можемо контролювати це, тому для спрайтів, які можуть викликати проблеми з продуктивністю (наприклад, дим), ми можемо просто витягти пікселі з найближчого дрібнішого докладного mipmap.

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

Графічні процесори спочатку підтримують блокові стислі формати – текстура ділиться на 4×4 пікселя (або, можливо, різних розмірів в форматах, які зазвичай не підтримуються GPU ПК), і кожен блок стискається в фіксовану кількість байтів. Зазвичай підтримувані формати на настільних GPU – BC1-7. Найбільш цікавими для нас є:

  • BC1 (раніше  DXT1) – призначений для RGB без альфи (або 1-бітного альфа) з використанням 4 біт на піксель (на відміну від 32-бітного необробленого RGBA).
  • BC3 (раніше  DXT5) – використовує 8 біт на піксель – той же формат для кольору, що і BC1, але використовує 4 додаткових біти для альфа-каналу.
  • BC4 – зберігає тільки один кольоровий канал в тому ж форматі, що і альфа-канал в BC3. Таким чином, зберігається 4 біта на піксель.

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

Незжата і стисла ВС3

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

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

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

Однією з ідей є стиснення YCoCg-DXT, яке використовує BC3 в якості базового формату і зберігає яскравість в альфа-каналі для більш високої якості стиснення і кольоровості в каналах RGB. Піксельні шейдери, що використовують ці текстури, повинні трохи з математики перетворити кольору з колірного простору YCoCg в RGB. Ми спробували інтегрувати це (використовуючи окрему стислу текстуру BC4 для альфа-каналу) і були приємно здивовані результатом. Ви, ймовірно, не помітите артефакти, якщо не будете збільшувати масштаб і шукати їх. Фактично, два тижні тому я включив стиснення текстур за замовчуванням (і нікому не сказав), і всякий раз, коли я питаю кого-то в команді, відключили вони його, вони кажуть, що не знали, що вона була придбана. Так що я дуже радий цьому. Невеликим недоліком є ​​необхідність використання двох текстур (BC3 + BC4), що дає 12 біт на піксель, але найкраще, незважаючи на те, що піксельному шейдеру доводиться вибирати з 2 текстур замість однієї, графічний процесор здатний рендерить в два рази швидше через того, що кеші можуть вміщати більше пікселів в стислих форматах, ніж у необроблених форматах.

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

В 0.17 настройка графіки стиснення текстур змінюється на список, що випадає, що містить «Ні», «Висока якість» і «Низька якість»:

Висока якість буде використовувати для користувача стиснення YCoCg-DXT і буде використовуватися за замовчуванням на більшості комп’ютерів.
Низька якість буде використовувати BC3 і призначене для використання тільки на дійсно слабких графічних процесорах.
У вас не повинно бути ніяких причин відключати стиснення, можливість зробити це в основному на випадок, якщо виникнуть якісь технічні проблеми. Стиснення застосовується до всіх спрайт, крім GUI, який повинен бути максимально чітким. Крім того, незалежно від параметра стиснення текстури, тіньові спрайт завжди будуть стискуватися в форматі BC4.

Нестиснене і стиснення високої якості і стиснення низької якості

Після того, як ми додали черв’яків з високою роздільною здатністю, кусак і Плевако, використання VRAM зросла до 3,5 ГБ (з включеним високою роздільною здатністю, очевидно), коли не було застосовано стиснення (навіть тіньовий). Стиснення тільки тіней зменшило використання VRAM до рівня 0,16 ~ 2,5 ГБ. При включеному стисненні високої якості використання спрайтів в VRAM в даний час складає ~ 1 ГБ (без mipmap). Це означає, що ваніль повинна відмінно відтворюватися в графічних процесорах з високою роздільною здатністю з 2 ГБ відеопам’яті, а в поєднанні з потокової передачею текстур ці графічні процесори повинні підтримувати високий дозвіл навіть в тих випадках, коли моди додають багато нових спрайтів. Спрайт з високою роздільною здатністю спочатку призначалися для гравців з найпотужнішими комп’ютерами, але в 0.17 вони по суті стануть новим стандартом. Мета полягає в тому, щоб в кінцевому підсумку видалити параметри «низького» і «дуже низького» дозволу спрайтів, оскільки стиснення текстур «низької якості» на звичайних спрайт + потокова передача текстур повинні бути в змозі працювати навіть на графічних процесорах з дуже маленькими розмірами VRAM .

Примітка: я згадував, що існує 7 форматів BC. BC7 призначений для текстур RGB або RGBA і потенційно має набагато кращі якості, а, ніж BC3, з тим же ступенем стиснення. Проблема в тому, що формат був представлений в DirectX 11, тому він не підтримується обладнанням класу DirectX 10 і недоступний в OpenGL на macOS. Друга проблема полягає в тому, що стиснення чогось в цей формат займає дуже багато часу, оскільки компресор повинен випробувати велику кількість конфігурацій для кожного блоку, щоб знайти той, який дає найкращу якість. Так як Factorio відрізняється від інших тим, що його мистецтво поширюється у вигляді безлічі файлів PNG замість того, щоб все акуратно упаковувати, щоб графічні ресурси можна було завантажувати безпосередньо в графічний процесор без будь-якого перекодування в інший формат або динамічного створення атласів спрайтів, нам потрібно рішення для стиснення в реальному часі. Ми не прагнемо змінити те, як гра розподіляється дуже сильно, оскільки наявність всього в окремих файлах робить поновлення досить невеликими, коли ми змінюємо деякі з них, а наявність повністю відкритих ванільних даних полегшує людям модифікацію гри.

Тепер подивимося на ці графіки

Ми взяли кілька еталонів екстремальних сцен. Перший з них – від проекту Clusterio в минулому році. Твінс надіслав мені збереження, тому що він отримував зниження частоті кадрів в середині великого масиву парових турбін. Другий – з повідомлення про помилку. Я вибрав збереження, тому що у нього є рейки, що йдуть через ліс, і тому що я міг дійсно отримати низький FPS навіть на 0,16, я використовував редактор карт, щоб покрити землю декоративними травами.

Ми протестували на графічних процесорах з 2 ГБ або 1 ГБ відеопам’яті, які є в офісі. Тести для настільних графічних процесорів виконувалися на одному ПК (ми тільки що поміняли місцями графічні процесори) – Intel Core i7-4790K, 16 ГБ ОЗУ, Windows 10. Ми виміряли час обробки кадру на графічному процесорі на 1000 кадрів і усереднили результати, Ми порівняли його зі складанням 0,17 до того, як були реалізовані оптимізації на стороні GPU, на жаль, у нас немає способу зафіксувати час GPU в 0,16. Тести проводилися в дозволі FullHD (1920×1080) з включеними спрайтами високого дозволу, з графічної конфігурацією, яка, на мій погляд, є найкращою для продуктивності рендеринга. Для старої збірки: Mipmaps, спеціалізація Atlas, окремий атлас нижніх об’єктів опції стиснення текстур включені, використання відеопам’яті встановлено на All. І еквівалентна конфігурація для нової збірки, за винятком використання нової опції стиснення високої якості.

Ми також включили результат від одного високопродуктивного графічного процесора (GeForce GTX 980 4 ГБ), але він працював в дозволі 4K (3840×2160) на відміну від FullHD. Тест Intel HD Graphics 5500 виконувався на ноутбуці з процесором Intel Core i7-5600U і 16 ГБ оперативної пам’яті, а замість цього використовувалося стиснення текстур низької якості. Ми також включили результати з включеною 16-бітної глибиною кольору.

Час вимірювався в мілісекундах. Чим менше час, тим краще, і для 60 кадрів в секунду кадр повинен займати 16,66 мс.

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

Як завжди, дайте нам знати, що ви думаєте на нашому форумі.


Comments: