Уменьшение задержек при компиляции шейдеров (конвейеров)
Конвейерная компиляция, также известная как компиляция шейдеров, — это ресурсоемкая операция, необходимая движку для отрисовки любого контента с помощью графического процессора.
Шейдеры и материалы в Godot проходят несколько этапов, прежде чем их сможет запустить графический процессор.
Точнее говоря, компиляция шейдеров подразумевает преобразование GLSL-кода, сгенерированного Godot, в промежуточный формат, который можно использовать на разных системах (например, SPIR-V при использовании Vulkan). Однако этот формат не может использоваться графическим процессором напрямую.
Конвейерная компиляция — это этап, на котором драйвер графического процессора преобразует промежуточный формат шейдера (результат компиляции шейдера) в формат, который графический процессор может использовать для рендеринга. Драйверы обычно хранят кэш конвейеров где-то в системе, чтобы избежать повторения процесса при каждом запуске игры. Этот кэш обычно удаляется при обновлении драйвера.
Конвейеры содержат больше информации, чем просто код шейдера, а это значит, что для каждого шейдера могут быть десятки и более конвейеров! Это затрудняет предварительную компиляцию движком, поскольку это было бы очень медленно и потребовало бы много памяти. Кроме того, этот шаг можно выполнить только на системе пользователя, и очень сложно поделиться результатом между пользователями, если у них нет одинакового оборудования и версии драйвера.
До Godot 4.4 не существовало другого решения проблемы конвейерной компиляции, кроме как её генерации при появлении объекта в поле зрения камеры, что приводило к печально известному заиканию шейдера или задержкам, возникающим только при первом прохождении. В Godot 4.4 были введены новые механизмы для устранения заиканий при конвейерной компиляции.
Убершейдеры: Godot использует константы специализации — функцию, позволяющую драйверу оптимизировать код конвейера с учётом набора параметров, таких как освещение, качество теней и т. д. Константы специализации используются для оптимизации шейдера путём ограничения ненужных функций. Изменение константы специализации требует перекомпиляции конвейера. Убершейдеры — это особая версия шейдера, способная изменять эти константы во время рендеринга. Это означает, что Godot может предварительно скомпилировать только один конвейер и компилировать более оптимизированные версии в фоновом режиме во время игры. Это значительно сокращает количество создаваемых конвейеров.
Предварительная компиляция конвейера: Используя убершейдеры, движок может предварительно компилировать конвейеры в нескольких местах, например, при загрузке сеток или добавлении узлов в сцену. Будучи частью процесса загрузки ресурсов, конвейеры могут даже предварительно компилироваться в нескольких фоновых потоках, если это возможно, во время экранов загрузки или даже игрового процесса.
Начиная с Godot 4.4, Godot определяет необходимые конвейеры и выполняет их предварительную компиляцию во время загрузки. Эта система определения в основном работает автоматически, но полагается на то, что RenderingServer видит все шейдеры, сетки и функции рендеринга во время загрузки. Например, если вы загружаете сетку и шейдер во время игры, конвейер для этой комбинации сетки/шейдера не будет скомпилирован, пока не будут загружены сетка/шейдер. Аналогично, такие действия, как включение MSAA или создание экземпляра узла VoxelGI во время игры, приведут к перекомпиляции конвейеров.
Мониторы предварительной компиляции конвейера
Предварительная компиляция конвейеров — основной механизм, используемый Godot для предотвращения задержек шейдеров, но это не идеальное решение. Знание о ситуациях, которые могут привести к задержкам конвейеров, может быть очень полезным, а обходные пути довольно просты по сравнению с предыдущими версиями. Эти обходные пути могут стать менее необходимыми в будущих версиях Godot по мере реализации большего количества методов обнаружения.
Отладчик Godot предлагает мониторы для отслеживания количества конвейеров, создаваемых игрой, и этапа, запустившего их компиляцию. Вы можете следить за этими мониторами во время игры, чтобы выявить потенциальные источники подтормаживаний шейдеров, не очищая кэш драйвера каждый раз перед тестированием. Резкое увеличение этих значений за пределами загрузочных экранов может проявляться в виде подтормаживаний во время игры при первом запуске игры на своей системе. Рекомендуется ознакомиться с этими мониторами, чтобы определить возможные источники подтормаживаний у ваших игроков, поскольку вы можете не заметить их самостоятельно, не очистив кэш драйвера или не проведя тестирование на более слабой системе.
Конвейерные компиляции одного из демонстрационных проектов.
Примечание
Мы можем видеть конвейеры, скомпилированные во время игры, и определить, какие этапы могут вызывать подтормаживания. Обратите внимание, что эти значения будут только увеличиваться и никогда не уменьшаться, поскольку удалённые конвейеры не отслеживаются этими мониторами, и конвейеры могут быть удалены и созданы заново во время игры.
Canvas: Компилируется при отрисовке 2D-узла. В настоящее время движок не поддерживает предварительную компиляцию для 2D-элементов, поэтому при первой отрисовке 2D-узла будут наблюдаться подтормаживания.
Mesh: компилируется в процессе загрузки 3D-сетки и определения конвейеров, которые можно предварительно скомпилировать на основе её свойств. Это может привести к подтормаживанию, если сетка загружается во время игры, но его можно устранить, если сетка загружается с помощью фонового потока. Модификаторы, входящие в состав узлов, такие как переопределения материалов, не могут быть скомпилированы на этом этапе.
Surface: Компилируется перед отрисовкой кадра и первым появлением 3D-объектов в дереве сцены. Это также может включать компиляцию для узлов, которые даже не видны в дереве сцены. Задержка будет наблюдаться только в первом кадре, когда узел добавляется в сцену, что не приведёт к заметной задержке, если она происходит сразу после загрузочного экрана.
Отрисовка: Компиляция выполняется по запросу, когда требуется отрисовка 3D-объекта, а убершейдер не был предварительно скомпилирован. Движок не может выполнить предварительную компиляцию этого конвейера из-за возникновения ситуации, которая ещё не была рассмотрена, или из-за внесённого в код движка изменения. Это приводит к подтормаживаниям во время игры. Аналогично версиям Godot до 4.4. Если вы видите здесь компиляции, пожалуйста, сообщите разработчикам <https://github.com/godotengine/godot/issues>, так как в системе убершейдеров такого происходить не должно. При этом обязательно прикрепите минимальный проект для воспроизведения.
Специализация: Компилируется в фоновом режиме во время игры для оптимизации частоты кадров. Не вызывает подтормаживаний, но может привести к снижению частоты кадров, если в кадре происходит много событий.
Особенности предварительной компиляции конвейера
Godot предлагает множество функций рендеринга, которые не обязательно используются в каждой игре. К сожалению, конвейерная прекомпиляция не может заранее определить, используется ли та или иная функция в проекте. Некоторые из этих функций обнаруживаются только при добавлении пользователем узла в сцену или изменении определённого параметра в проекте или окружении. Система конвейерной прекомпиляции отслеживает эти функции при их первом появлении и позволяет выполнять их прекомпиляцию для любых создаваемых впоследствии сеток или поверхностей.
Если ваша игра использует эти функции, создайте сцену, которая использует их, как можно раньше, до загрузки большей части ресурсов. Такая сцена может быть очень простой и будет работать, если в ней используются функции, запланированные в игре. При необходимости её можно даже отрисовать за пределами экрана хотя бы на один кадр, например, перекрыв её узлом ColorRect или используя SubViewport, расположенный за пределами окна.
Также следует помнить, что изменение любого из этих параметров во время игры приведёт к мгновенным подтормаживаниям. Изменяйте эти параметры только на экранах настроек при необходимости и добавляйте загрузочные экраны и сообщения после применения изменений.
Уровень MSAA: Включается при изменении уровня 3D MSAA в настройках проекта. К сожалению, использование разных уровней MSAA на разных вьюпортах приводит к подтормаживанию, поскольку движок отслеживает только один уровень за раз для выполнения прекомпиляции.
Reflection Probes: Активируется при размещении узла ReflectionProbe на сцене.
Отдельное отражение: Включается при использовании таких эффектов, как подповерхностное рассеивание или эффект компоновщика, который основан на выборке отражения непосредственно с экрана.
Векторы движения: активируется при использовании таких эффектов, как TAA, FSR2 или эффекта композитора, требующего векторов движения (например, размытия движения).
Нормальность и шероховатость: Включается при использовании SDFGI, VoxelGI, отражений в экранном пространстве, SSAO, SSIL или использовании
normal_roughness_bufferв пользовательском шейдере или CompositorEffect.Карты освещения: Включается, когда на сцене размещен узел LightmapGI, использующий запеченную карту освещения.
VoxelGI: включается, когда на сцене размещен узел VoxelGI.
SDFGI: Включается, когда WorldEnvironment включает SDFGI.
Multiview: Включено для проектов XR.
16/32-битные Тени: Включается при изменении конфигурации точности глубины карт теней в настройках проекта.
Всенаправленный двойной параболоид: активируется, когда всенаправленный источник света отбрасывает тени и использует режим двойного параболоида.
Omni Shadow Cubemap: Включается, когда источник света omni отбрасывает тени и использует режим кубической карты (который используется по умолчанию).
Если вы наблюдаете подтормаживания во время игры, а мониторы сообщают о внезапном увеличении количества компиляций на этапе Surface, скорее всего, какая-то функция не была включена заранее. Включение этого эффекта при загрузке игры, вероятно, поможет решить проблему.
Экземпляр предварительной компиляции конвейера
Одной из распространённых причин подтормаживаний в играх является то, что некоторые эффекты отображаются на сцене только из-за взаимодействий, происходящих только во время игрового процесса. Например, если эффект частиц добавляется на сцену только через скрипт, когда игрок выполняет какое-либо действие. Даже если сцена предварительно загружена, движок может не успеть прекомпилировать конвейеры, пока эффект не будет добавлен на сцену хотя бы один раз.
К счастью, Godot 4.4 и более поздние версии могут предварительно компилировать эти конвейеры, если сцена создается хотя бы один раз на сцене, даже если она полностью невидима или находится вне поля зрения камеры.
Скрытый узел пули, прикреплённый к проигрывателю в одном из демонстрационных проектов. Это помогает движку заранее скомпилировать конвейеры эффектов.
Если вам известны какие-либо эффекты, которые динамически добавляются к сцене во время игрового процесса, и вы видите внезапное увеличение на мониторе компиляции при появлении этих эффектов, обходным решением будет прикрепить скрытую версию эффекта куда-нибудь, где она гарантированно отобразится.
Например, если персонаж игрока способен вызвать какой-либо взрыв, вы можете прикрепить этот эффект к дочернему элементу игрока в виде невидимого узла. Обязательно отключите скрипт, прикреплённый к скрытому узлу, или скройте все остальные узлы, которые могут вызывать проблемы. Для этого включите Редактируемые дочерние элементы для нужного узла.
Shader baker (Шейдер-запекатель)
Начиная с Godot 4.5, можно выбрать запекание шейдеров при экспорте для ускорения первоначального запуска. Как правило, это не решает существующие проблемы, но сокращает время первой загрузки игры. Это особенно актуально при использовании Direct3D 12 или Metal, которые значительно медленнее Vulkan из-за необходимости конвертации. Собственные шейдеры Godot используют GLSL и SPIR-V, но Direct3D 12 и Metal используют другие форматы.
Примечание
Shader baker может запекать исходный код только в промежуточном формате (SPIR-V для Vulkan, DXIL для Direct3D 12, MIL для Metal). Он не может запекать промежуточный формат в конечном pipeline, поскольку это зависит от драйвера видеокарты и аппаратного обеспечения.
Shader Baker не заменяет pipeline предварительную компиляцию, но призван ее дополнять.
При включении shader baker скомпилированный код шейдера будет упакован в PCK, что полностью пропустит этап компиляции шейдера. Недостаток заключается в том, что экспорт займёт немного больше времени. Файл PCK будет больше на несколько мегабайт.
По умолчанию shader baker отключен, но его можно включить в каждом пресете экспорта в диалоговом окне Export, установив флажок рядом с опцией экспорта Shader Baker > Enabled.
Обратите внимание, что shader baking позволит экспортировать шейдеры только для драйверов, поддерживаемых платформой, на которой в данный момент работает редактор:
Редактор, работающий в Windows, может экспортировать шейдеры для Vulkan и Direct3D 12.
Редактор, работающий на macOS, может экспортировать шейдеры для Vulkan и Metal.
Редактор, работающий в Linux, может экспортировать шейдеры только для Vulkan.
Редактор, работающий на Android, может экспортировать шейдеры только для Vulkan.
Shader baker будет экспортировать только те шейдеры, которые соответствуют настройкам проекта rendering/rendering_device/driver для целевой платформы.
Примечание
The shader baker поддерживается только для рендеров Forward+ и Mobile. Он не будет работать, если в проекте используется рендер с поддержкой Compatibility или если пользователи используют резервную версию совместимости, поскольку их оборудование не поддерживает рендер Forward+ или Mobile.
Это также означает, что shader baker не поддерживается на веб-платформе, поскольку веб-платформа поддерживает только Compatibility renderer.