Последовательность выполнения
Эта статья посвящена детальному рассмотрению последовательности выполнения UNIGINE. Здесь вы узнаете, что происходит под капотом UNIGINE Engine. Для общего обзора рабочего процесса UNIGINE см. статью Архитектура двигателя.
Внутренний код движка UNIGINE и логика вашего приложения, которые могут быть расширены с помощью плагинов, написанных в C++ или C#, выполняются в заранее определенном порядке:
- Инициализация . На этом этапе подготавливаются и инициализируются необходимые ресурсы. Как только эти ресурсы готовы к использованию, Engine переходит в основной цикл.
Основной цикл . Когда UNIGINE входит в основной цикл, все его действия можно разделить на три этапа, которые выполняются один за другим в цикле:
- Update этап, содержащий всю логику вашего приложения, которая выполняется каждый кадр
- Rendering этап, содержащий все операции, связанные с рендерингом, вычисления моделирования физики и поиск пути
- Swap этап, содержащий все операции синхронизации, выполняемые для переключения между буферами
Этот цикл повторяется каждый кадр во время работы приложения.
- Shutdown. Когда UNIGINE останавливает выполнение приложения, он выполняет операции, связанные с завершением работы приложения и очисткой ресурсов.
Следующая диаграмма (кликните, чтобы увеличить) представляет основные этапы последовательности выполнения UNIGINE в режиме физики Async Rendering, используемом по умолчанию.
Ожидание GPU#
В большинстве случаев UNIGINE завершает все этапы основного цикла быстрее, чем графический процессор может фактически отрендерить кадр. Поэтому используется двойная буферизация : она позволяет быстрее рендерить кадры, меняя местами буферы графического процессора (задний и передний), в которые выполняется рендеринг.
Когда все сценарии обновлены и все вычисления на ЦП завершены, графический процессор все еще отображает кадр, рассчитанный на ЦП. Таким образом, ЦП должен ждать, пока графический процессор не закончит рендеринг кадра и буферы рендеринга не поменяются местами. Период такого времени ожидания представлен счетчиком Waiting GPU в Профилировщике производительности .
Если время Waiting GPU слишком велико, это может сигнализировать о существовании узкого места графического процессора и необходимости оптимизации художественного контента. Но если при этом частота кадров остается стабильно высокой, это означает, что у вас все еще есть ресурсы процессора, доступные для обработки большего количества данных.
Сколько времени прошло с момента, когда все скрипты были обновлены и все вычисления на CPU были завершены, до момента, когда GPU завершил рендеринг кадра, также зависит от того, включена ли вертикальная синхронизация (VSync) (может быть сделано через Системное меню ). Если VSync включен , ЦП ожидает, пока графический процессор не закончит рендеринг, и будет выполнена вертикальная синхронизация. В этом случае значение счетчика Waiting GPU будет больше.
На четырех схемах ниже показаны различные сценарии производительности ЦП и ГП с отключенным и включенным VSync.
Первые две схемы показывают расчет и рендеринг кадра при отключенном VSync (в обоих случаях вертикальный обратный ход луча монитора игнорируется):
- Схема 1. ЦП вычисляет кадр быстрее, чем графический процессор. Итак, CPU ожидает GPU (время Waiting GPU высокое).
- Схема 2. Вычисления CPU выполняются медленнее, чем GPU отрисовывает кадр. Таким образом, графическому процессору приходится ждать, пока процессор завершит свои вычисления. В этом случае время Waiting GPU мало.
Схемы 3 и 4 показывают расчеты и рендеринга кадра , когда VSync включен (вертикальный обратный ход луча монитора учитывается):
- Схема 3. ЦП вычисляет кадр быстрее, чем может его обработать графический процессор, и процессор ожидает появления графического процессора. Однако в этом случае и CPU, и GPU также ждут VSync.
- Схема 4. ЦП вычисляет кадр медленнее, чем его отображает графический процессор. В этом случае GPU ожидает не только завершения вычислений CPU, но и VSync.
Потоки физики и поиска пути#
Движок использует все доступные потоки для одновременного обновления видимых нод и многопоточной физики (в асинхронном режиме обновления физики) и поиск пути, который обновляется параллельно с этапом рендеринга.
На следующей схеме показаны потоки в процессе расчета и рендеринга кадра с включенным режимом физики Async Rendering:
Первый физический фрейм вычисляется параллельно с рендерингом в основном потоке. Затем все остальные фреймы физики обновляются в основном потоке. Весь клиентский код, например updatePhysics(), также вызывается в основном потоке.
Последние две схемы иллюстрируют, как обновляется физика в режиме Before Rendering:
В обоих случаях CPU или GPU должны ждать друг друга, чтобы завершить рендеринг и поменяться местами для синхронизации.
Корреляция между частотой кадров рендеринга и физикой#
Частота кадров рендеринга обычно варьируется, в то время как, как мы уже упоминали ранее, частота кадров моделирования физики остается фиксированной . Это означает, что ваши функции update() и updatePhysics() из мировой логики вызываются с разной частотой.
На рисунке выше показано, что происходит, когда частота кадров физики фиксируется на уровне 60 кадров в секунду, а частота кадров рендеринга меняется. В общем, есть три возможных случая:
- Частота кадров рендеринга намного выше . В этом случае физические расчеты выполняются один раз для двух или более кадров. Это не вызывает никаких проблем, поскольку положения движущихся объектов интерполируются между расчетами. Режим Stable FPS включен по умолчанию, чтобы избежать скачков и падений частоты кадров (см. подробное объяснение в следующем разделе).
- Частота кадров при рендеринге такая же или почти такая же. Это тоже нормально, расчеты производятся один раз за кадр; физика идет в ногу с графикой и наоборот.
Частота кадров рендеринга намного ниже. Здесь начинаются проблемы. Во-первых, как видно из рисунка, физика должна вычисляться дважды или даже больше за кадр, и это не ускоряет общий процесс рендеринга. Во-вторых, вы не можете установить слишком низкую частоту кадров физики, так как в этом случае потеря точности расчетов будет слишком велика.
Нет смысла устанавливать слишком большое количество итераций. UNIGINE проверяет, можно ли выполнить следующую итерацию в рамках бюджета физики, а если нет, он задерживается и переходит к следующему кадру рендеринга.
Стабильный FPS#
Когда частота кадров рендеринга приложения выше, чем у обновлений физики, для расчета некоторых кадров с обновлениями физики потребуется больше времени. Это приводит к скачкам и падению частоты кадров приложения, что может стать причиной нестабильной симуляции (при наличии интерактивности и обработки пользовательского ввода).
Чтобы сделать частоту кадров более стабильной, по умолчанию включена функция Stable FPS. Она добавляет состояние Idle, если текущий кадр занял меньше времени, чем предыдущий (обычно из-за отсутствия физических расчетов). Это гарантирует, что каждый кадр занимает одинаковое количество времени, эффективно устраняя пульсацию частоты кадров.
Вы можете отключить эту функцию, если ваше приложение используется для машинного обучения или для захвата последовательностей кадров (Video Grabber), когда плавность не важна.
Синхронизация частоты кадров движка с обновлением физики#
Как уже упоминалось, в случае, если частота кадров движка выше, чем физическая, результаты физических расчетов интерполируются между кадрами. Но вы можете включить вычисление физики для каждого визуализированного кадра, синхронизируя частоту кадров движка с физической (установив флаг SyncEngineUpdateWithPhysics из кода). В этом режиме нет подергивания физических объектов, если они имеют нелинейные скорости. Этот флаг не действует, если Engine FPS ниже физического.