Оптимізація пайплайну Lumen+Nanite+VSM для реалістичного проєкту в Unreal Engine 5
Привіт! Мене звати Сергій, я Head Dev команди Spacedev Games. У цій невеликій статті я поділюся досвідом оптимізації графічної складової комплексного проєкту в стилі «реалізм» на Unreal Engine 5 з використанням пайплайну Lumen/Nanite/Virtual Shadow Maps і способами, як досягти результату у більше ніж 100 FPS на
Пайплайн Lumen + Nanite + VSM
З офіційним релізом UE5 і його подальшими оновленнями Epic Games пропагують використовувати Lumen GI для програмного трасування променів, технологію Nanite замість Level of Details, а також Virtual Shadow Maps (VSM) для рендерингу тіней. Кожен новостворений проєкт на UE5 за замовченням конфігурується під цей пайплайн, що може викликати проблеми із продуктивністю при високому рівні деталізації вашої сцени, використанні ассетів від Quixel Megascans, великої кількості Foliage на сцені тощо.
За загальноприйнятим стандартом, для досягнення мінімум 60 кадрів на секунду, на рендер кожного кадру вашої сцени має виділятися до 16.6 мілісекунд. В рамках даного фрейм-бюджету ви маєте вмістити і задачі для GPU в Render Thread, і обчислення CPU в Game Thread. Консольна команда, що допоможе моніторити дані показники в реальному часі:
stat unit
І також кількість кадрів на секунду:
stat fps
Зазвичай проблеми із продуктивністю виникають або в Game Thread, що означає ботлнек на CPU, або в Render Thread, що свідчить про ботлнек на стороні відеоприскорювача. Але часто завантажений Game Thread може викликати додаткові обчислення Render Thread через так званий CPU stall (час очікування відеокартою завершення обчислення інструкцій CPU) і навпаки. Тож перш за все слід визначити, яка саме частина вашого проєкту є ботлнеком. В цьому можуть допомогти як вищезазначені команди, так і профайлери:
- команди
profilegpu
іstat gpu
підкажуть, на що саме витрачається ресурс відеокарти; stat streaming
допоможе дізнатися, чи не є проблемою текстури у вашій сцені.
Для профайлінгу Game Thread можна використовувати команду stat game
, яка покаже, як часто CPU навантажується тими чи іншими операціями — Blueprint Time, World Tick Time тощо. Для більш точного визначення проблемних аспектів на стороні CPU мені подобається використовувати профайлер із Session Frontend через його візуальну простоту і доступність, який був вирізаний із нових релізів Unreal Engine на користь нового профайлера Unreal Insights. Для Session Frontend можна записати стат-файл командами stat startfile
і stat stopfile
(у будь-якій актуальній версії рушія) і завантажити їх у профайлер Session Frontend, який доступний у всіх версіях рушія до 5.2 включно.
Тут я буду розбирати варіант саме ботлнека на стороні GPU, за умови що у нас немає проблем із Game Thread, а CPU не перенавантажує відеокарту надмірним потоком інструкцій.
Отже, перше, з чим ви зіткнетесь при спробі оптимізувати ваш Render Thread, буде факт того, що Lumen за замовченням, навіть у пустій сцені, використовує до 5 мілісекунд часу для реалізації обчислень. Маючи всього декілька Nanite мешів на сцені ви побачите ще один неприємний факт у вигляді відносно високого показника Nanite::VisBuffer
, що фіксується рушієм за замовченням навіть із мінімальним використанням Nanite. Це не є великою проблемою, якщо ви цілитесь виключно на гравців із high-end системами та прискорювачами рівня RTX 40XX. Проте якщо мета дозволити гравцям із більш доступними збірками насолодитися вашою грою у 60+ кадрів з рендером в нативній роздільній здатності — це може виявитись фактично недосяжним.
Пайплайн Lumen/Nanite/VSM з коробки не дозволяє використовувати ніякі інші підходи або комбінування із більш класичними методами (як от LOD, Masked Foliage, Cascaded Shadow Maps або використання Screen Space Global Illumination замість Lumen). Всі три технології були створені для роботи виключно між собою, а їх комбінування з альтернативними методами або вимкнення на користь legacy підходів може вирішити початкову проблему, але накладе x3 додаткові проблеми зверху.
Наведені нижче підходи з погляду Epic Games фактично є «костилями» і не рекомендовані до реалізації під Lumen/Nanite/VSM пайплайн. Проте допомогли розібратися з проблемами швидкодії на боці GPU у нашому проєкті (Glory to the Heroes). Він при цьому використовує Lumen, фотоскани, великі відкриті локації та зараз не виходить за рамки
Nanite
Nanite був створений для заміни LOD-ів та поліпшення візуальної якості віддалених об’єктів. Він чудово виконує свою функцію без значного імпакту на продуктивність при використанні з no-baked high-poly геометрією (як фотоскани). Однак якщо ваша сцена складається із мешів з різною геометрією і топологією (наприклад, високополігональних сканів великих каменюк або архітектури разом із no-geometry) ви отримаєте проблеми з швидкодією та overhead на Render Thread через Virtual Shadow Maps, що необхідний для коректного рендерингу нанітної геометрії, а саме їх вплив на Shadow Path. Застосування Nanite до masked ассетів або геометрії із якісною лоу-полі топологією може викликати циклопічного рівня навантаження на GPU через так званий Quad Overdraw.
«Тому якщо ваш проєкт включає як лоу-полі, так і хай-полі моделі — Nanite слід використовувати виключно там, де він необхідний і на тій геометрії, яка має важку топологію і високу щільність геометрії»
Рендерер не зможе обробити нанітні і не нанітні відбивачі (casters) тіней разом в одному графі, тому Shadow Path розділиться на два окремих — Nanite Rasterize Path і Non-Nanite Rasterize Path, що будуть оброблятися послідовно. Також не нанітні меші не будуть мати відповідної «Nanite Data Structure», що використовується рендерером для прискорення обчислень нанітної геометрії.
Простим рішенням, яке допомогло нам при створенні локації із густим лісом, що повністю складається із не-нанітних мешів дерев, трави, кущів тощо, стало агресивне використання якісних LOD-ів з 2Д «імпостерами» на останньому LOD-і, а також вимкнення тіней на всіх LOD-ах, крім першого і нульового. Це призвело до ледь помітних втрат візуальної якості через зменшення загальної деталізації тіней на сцені, проте мінімізувало час на обчислення Non-Nanite Rasterize path.
Lumen
Так само як і VSM, Lumen покладається на Nanite Data Structure, щоб побудувати граф сцени GI (Global Illumination). І так само як у випадку із нанітною і не-нанітною геометрією разом із VSM, Lumen вимагає додатковий окремий граф для включення нанітної і не-нанітної геометрії в GI.
Для зменшення імпакту через ці додаткові обчислення з мінімальними втратами якості і деталей відображень (Lumen Reflections) можна законфігурувати наступні налаштування Lumen (можна використовувати як консольні команди, так і глобально налаштувати проєкт), що мають найбільший вплив на продуктивність:
- Lumen.ScreenProbeGather.MaxRayInstensity — даний параметр відповідає за максимальну інтенсивність променів, що використовуються для збору інформації про освітлення в сцені (Screen Probe Gather) в рамках Lumen GI. Простіше кажучи, він контролює, наскільки яскравими можуть бути відбиття та непряме освітлення. Зменшення цього значення може допомогти оптимізувати продуктивність, але може призвести до менш точних або тьмяніших відбиттів.
- Lumen.ScreenProbeGather.ScreenTraces.HZBTraversal — відповідає за оптимізацію трасування променів, зокрема, з використанням ієрархічного буфера глибини (Hierarchical Z-Buffer, HZB) для пришвидшення цього процесу. Вимкнення цієї опції може покращити якість, але значно вплине на продуктивність.
- Shadow.Virtual.ResolutionLodBiasDirectional — контролює зміщення рівня деталізації (Level of Detail, LOD) для віртуальних тіней від спрямованих джерел світла (Directional Lights). Він впливає на роздільну здатність карт тіней, що використовуються для рендерингу. Збільшення значення цього параметра зменшить роздільну здатність тіней, що призведе до покращення продуктивності, але зробить тіні менш чіткими.
- Lumen.Reflections.DownsampleFactor — контролює роздільну здатність, в якій обчислюються Lumen Reflections. Простіше кажучи, він визначає, наскільки сильно зменшується роздільна здатність, перш ніж Lumen обробить відбиття. Збільшення цього значення призведе до менш детальних відбиттів, але значно покращить продуктивність.
Додатково можна поекспериментувати із наступними параметрами у PostProcessingVolume:
- Lumen Scene Lightning Update Speed
- Final Gather Lightning Update Speed
- Diffuse Color Boost
Оптимальні значення, звісно, залежать від конкретного проєкту та особливостей вашої сцени.
Застосування наведених методів підвищить шанси досягти прийнятної продуктивності навіть на
12 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів