Инструменты для профайлинга в Unreal Engine 4

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привет! Меня зовут Юрий Денисюк, я Production Lead в компании Pingle Game Studio. Мы уже 14 лет разрабатываем игры для международных лидеров индустрии — Embracer Group, Square Enix, Qualcomm, tinyBuild, Revolution Software, Egosoft и других. За это время накопили много опыта: как создавать игры с нуля, портировать их на разные платформы и оптимизировать для широкого круга устройств.

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

Важным аспектом оптимизации является, собственно, поиск проблемных мест. У разных платформ эти места могут отличаться. Для их поиска используются профайлеры. Почти у каждой платформы есть свой набор инструментов для этого, но кроме этого Unreal Engine 4 обладает собственным инструментарием для профайлинга — о нем мы сегодня и поговорим.

Ищем bottle-neck

Перво-наперво нужно понять где же находится наше бутылочное горлышко: в потоке Game (игровая логика), рендер (Draw) потоке или GPU. Чтобы определить это, запустите игру в не шиппинг конфигурации (в ней отсутствует консоль и возможность вывода дебаг информации) и введите консольную команду «stat unit», чтобы увидеть сколько времени тратится на выполнение каждого потока. Хорошим вариантом является Test конфигурация, которая максимально близка к шиппинг, но при этом поддерживает некоторые консольные команды, сбор статистики и профайлинга. Если же у вас проблемы со сборкой тест конфигурации, можете использовать Development, но с учетом того, что в нем есть много отладочных функций, которых не будет в релизной версии.

Время в строке Frame отображает общее время, затраченное на вывод одного кадра вашей игры. Так как оба потока — Game и Draw — выполняются до завершения вывода кадра, то время Frame часто близко к времени одного из этих потоков. Время GPU отображает время, которое видеокарта затрачивает на рендер сцены. Так как время GPU синхронизировано со временем отрисовки кадра (Frame), то скорее всего их значения будут примерно равны. Если время потока Game близко к времени Frame, то бутылочное горлышко в потоке Game. Если время Draw близко к времени Frame, то бутылочное горлышко в потоке рендера. Если ни одно из этих значений не близко к значению Frame, а GPU да, то проблема в видеокарте. Также возможен вариант, когда ни один из потоков не близок к Frame — это значит, что проблема в асинхронной загрузке данных.

Проблемы в Game

Давайте рассмотрим вариант, в котором проблема в потоке Game. Лучшим решением для анализа производительности потока Game будет снять файл статистики. Вы также можете подключится непосредственно к процессу игры, но иногда это неудобно или невозможно (консоли и мобильные платформы), а также в снятие и дальнейший анализ файла статистики можно разделить на нескольких людей. Для начала сбора профайла вам нужно вызвать консоль (на ПК клавиша тильда ~) и ввести консольную команду «stat startfile». Для вызова консоли на мобильных устройствах прикоснитесь к экрану четырьмя пальцами.

Оставьте выполняться минимум на 10 секунд, чтобы получить хорошее среднее значение из большого количества циклов вывода кадров. Файлы статистики большей длительности тоже хороши, так как позволяют отловить периодические проблемы, но файлы статистики длительностью более 30 минут не поддерживаются, так как их размер будет слишком большим.

Как только у вас есть файл статистики за достаточный промежуток времени, вы можете остановить сбор командой «stat stopfile». Файл ue4stats будет сохранен в папке вашего проекта по пути Saved/Profiling/UnrealStats.

Чтобы открыть файл статистики вам нужно запустить UnrealFrontend, ярлык которого можно найти в той же папке, где и UE4Editor. Или вы можете открыть вкладку Session Frontend в редакторе, которая находится в меню Window. Как только вы открыли вкладку Session Frontend вам нужно переключиться на вкладку Profiler внутри нее. Теперь вы можете загрузить свой файл статистики ue4stats.

Наиболее важная информация находится в дереве функций (function tree), которое находится внизу. Разверните GameThread и прокрутите вниз, пока не найдете значение, у которого «Inc Time» (Inclusive Time, время выполнения) больше, чем пара миллисекунд и которое не содержит много чилдов (вложенных функций) или не имеет их вовсе. Также обратите внимание на столбец «Calls» (вызовы), который показывает среднее количество вызовов функции каждый фрейм. Не обращайте внимание на «CPU Stall» — это значение всего лишь показывает время, которое потрачено на ожидание. Так что оно не учитывается и может только сказать нам, что фреймрейт ограничен или что бутылочное горлышко не в потоке game. В примере профиля ниже подозрительным выглядит время, которое тратится на кеширование шрифтов.

Это была реальная проблема, которую обнаружили в Fortnite. В данном случае показывалось много текста, который менял свой размер в зависимости от расстояния между камерой и важным объектом. Так как размер текста менялся в каждом кадре, то кэш шрифтов в Slate, UI системе Unreal Engine, был переполнен тысячами одинаковых строк разного размера. Решением стало отключение изменения размера от дистанции, заменив несколькими дискретными значениями для нескольких расстояний.

Есть несколько стандартных подозреваемых, к которым стоит присмотреться.

FTickFunctionTask очень важная вещь. Под ним находится каждый актер и компонент, который тикает. Уменьшение количества актеров и компонентов, которые постоянно тикают — один из замечательных способов повысить производительность.

Если в вашей игре есть актеры, которые не должны никогда тикать и вы используете C++ код, то вы можете добавить в конструктор актера следующий код, чтобы полностью отключить для него тик:

PrimaryActorTick.bCanEverTick = false;

Если же он иногда может тикать, то вы можете использовать следующий код:

PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false;

После этого вы сможете включать тик, используя функцию SetActorTickEnabled. Кроме того, вы можете снизить частоту тика для менее важных акторов или при некоторых условиях — например когда актор находится далеко от игрока или вне поля зрения. В движке уже есть significant manager, который может автоматически управлять этим, но вы можете также реализовать собственный механизм для более точного управления.

Другая вещь, на которую следует обратить внимание — BlueprintTime. Проще всего найти его в списке, переключившись в вид inclusive (coalesced). Это объединит все значения BlueprintTime в одну строку. Если вы выберете BlueprintTime и после этого переключитесь назад в вид Hierarchical, у вас выберутся все места, где исполняется код Блупринтов. Так вы сможете понять на что тратится время и в каких Блупринтах.

Другая частая причина потери производительности — TickWidgets. Если его время велико, то это значит, что у вас одновременно отображается слишком много виджетов, или у них слишком сложная иерархия, или делегаты их параметров слишком сложны. Например параметр visibility может вызываться несколько раз за фрейм. Так что важно чтобы они оставались небольшими и передавались своевременно.

Много Skeletal Meshes в игре? Время SkinnedMeshComp Tick иногда может стать слишком большим. Попытайтесь уменьшить количество костей в скелетах или сделайте их Anim Blueprints менее сложными. Если вам не нужно обновлять анимацию, когда вы не видите Skeletal Mesh, то вы можете установить параметр MeshComponentUpdateFlag в OnlyTickPoseWhenRendered. Будьте осторожны с этим параметром, так как AnimNotifies не будут отрабатывать для мешей, которых не видно.

Если вы ищете то, что вызывает падения производительности время от времени, то лучшим способом будет найти пик на таймлайне, выбрать кадры вокруг него и переключить вид из «Average» в «Maximum». Это переключит значения всех значений со средних на максимальные в данном промежутке.

Вместо выводов

Unreal Profiler является мощным и достаточно универсальным инструментом. Но его возможности не безграничны: на некоторых платформах часть информации будет недоступна (например время вызовой СДК самой платформы), может быть не точная синхронизация данных по времени, сам профайлер может достаточно сильно нагружать игру (вплоть до крашей) и т.д. Тут вам уже помогут специфические платформенные профайлеры. Так же недавно эпики добавили новый профайлер Unreal Insights, который меньше нагружает игру, дает больше данных и точнее отслеживает время. Но это уже тема для отдельной статьи.

Статья-источник: www.unrealengine.com/...​me-thread-cpu-performance
Документация по Unreal Profiler: docs.unrealengine.com/...​nceAndProfiling/Profiler
Unreal Insgihts: docs.unrealengine.com/...​Profiling/UnrealInsights

👍НравитсяПонравилось1
В избранноеВ избранном1
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

Этой статье много лет. Укажите пожалуйста что это перевод, это не является личной статьей. Я сколько анриалом пользуюсь(а я им пользуюсь с первого дня) помню эту статью. Вот реф:
www.unrealengine.com/...​me-thread-cpu-performance

Unreal Profiler ни разу не пользовался, меня вполне устраивает Unreal Insights

Unreal Insight, как я и писал, хорош — но он только недавно покинул бету (и то не до конца). Кроме того, если у вас проект до 25й версии, то Insight скорее всего вообще не будет работать.
Это напоминает переход с Matinee на Sequencer

Ничего не знал про разработку игр до этого момента. Было прикольно подсмотреть, спасибо.

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