Оптимизация графического процессора
Введение
Потребность в новых графических функциях и прогресс практически гарантированно приводят к возникновению узких мест в графике. Некоторые из них могут быть связаны с центральным процессором, например, с вычислениями в движке Godot для подготовки объектов к рендерингу. Узкие места могут также возникать в центральном процессоре, в графическом драйвере, который сортирует инструкции для передачи в графический процессор, и при передаче этих инструкций. И наконец, узкие места возникают и в самом графическом процессоре.
Узкие места при рендеринге сильно зависят от аппаратного обеспечения. Мобильные видеокарты, в частности, могут испытывать трудности со сценами, которые легко воспроизводятся на настольных компьютерах.
Понимание и исследование узких мест графического процессора несколько отличается от ситуации с центральным процессором. Это связано с тем, что зачастую производительность можно изменить только косвенно, изменяя инструкции, передаваемые графическому процессору. Кроме того, проводить измерения может быть сложнее. Во многих случаях единственный способ измерить производительность — это изучить изменения времени, затрачиваемого на рендеринг каждого кадра.
Вызовы отрисовки, изменения состояния и API-ы
Примечание
Следующий раздел не предназначен для конечных пользователей, но полезен для предоставления справочной информации, которая будет важна в последующих разделах.
Godot отправляет инструкции графическому процессору через графический API (Vulkan, OpenGL, OpenGL ES или WebGL). Соответствующие коммуникации и действия драйвера могут быть весьма затратными, особенно в OpenGL, OpenGL ES и WebGL. Если мы сможем предоставить эти инструкции в удобном для драйвера и графического процессора виде, мы сможем значительно повысить производительность.
Практически каждая команда API в OpenGL требует определённой проверки, чтобы убедиться, что графический процессор находится в правильном состоянии. Даже кажущиеся простыми команды могут привести к потоку скрытых действий. Поэтому цель состоит в том, чтобы свести эти инструкции к минимуму и максимально сгруппировать похожие объекты, чтобы их можно было отрисовывать вместе или с минимальным количеством этих дорогостоящих изменений состояния.
2D-партийная обработка
В 2D-графике затраты на обработку каждого объекта по отдельности могут быть чрезмерно высокими — на экране их могут легко быть тысячи. Именно поэтому используется 2D-batching (пакетирование). Несколько похожих объектов группируются и визуализируются в пакете одним вызовом отрисовки, а не отдельными вызовами отрисовки для каждого объекта. Кроме того, это позволяет свести к минимуму изменения состояний, материалов и текстур.
3D batching (пакетирование)
В 3D мы по-прежнему стремимся минимизировать количество вызовов отрисовки и изменений состояния. Однако объединить несколько объектов в один вызов отрисовки может быть сложнее. 3D-сетки, как правило, состоят из сотен или тысяч треугольников, а объединение больших сеток в реальном времени обходится непомерно дорого. Затраты на их объединение быстро перевешивают любые выгоды по мере роста числа треугольников в сетке. Гораздо лучшей альтернативой является предварительное объединение сеток (статичных сеток относительно друг друга). Это могут сделать художники или программно в Godot с помощью дополнения.
Объединение объектов в 3D также имеет свои издержки. Несколько объектов, отрисованных как один, нельзя отсечь по отдельности. Целый город, находящийся за пределами экрана, всё равно будет отрисован, если его присоединить к одной травинке на экране. Поэтому при объединении 3D-объектов всегда следует учитывать местоположение объектов и их отсечку. Несмотря на это, преимущества объединения статичных объектов часто перевешивают другие соображения, особенно для большого количества удалённых или низкополигональных объектов.
Для получения дополнительной информации о специальных 3D-оптимизациях см. Оптимизация 3D-производительности.
Повторное использование шейдеров и материалов
Рендерер Godot немного отличается от существующих. Он разработан, чтобы максимально минимизировать изменения состояния графического процессора. StandardMaterial3D хорошо справляется с повторным использованием материалов, требующих схожих шейдеров. Если используются пользовательские шейдеры, обязательно используйте их повторно как можно чаще. Приоритеты Godot:
Повторное использование материалов: Чем меньше различных материалов в сцене, тем быстрее будет рендеринг. Если в сцене огромное количество объектов (сотни или тысячи), попробуйте повторно использовать материалы. В худшем случае используйте атласы, чтобы уменьшить количество изменений текстур.
Повторное использование шейдеров: Если материалы невозможно использовать повторно, попробуйте хотя бы повторно использовать шейдеры. Примечание: шейдеры автоматически используются повторно между объектами StandardMaterial3D, имеющими одинаковую конфигурацию (функции, включаемые или отключаемые флажком), даже если у них разные параметры.
Например, если в сцене 20 000 объектов, каждый из которых использует 20 000 различных материалов, рендеринг будет медленным. Если же в той же сцене 20 000 объектов, но используется всего 100 материалов, рендеринг будет гораздо быстрее.
Стоимость пикселя против стоимости вершины
Возможно, вы слышали, что чем меньше полигонов в модели, тем быстрее она будет визуализироваться. Это действительно относительно и зависит от многих факторов.
На современных ПК и консолях стоимость вершин низкая. Изначально графические процессоры рендерили только треугольники. Это означало, что каждый кадр:
Все вершины должны были быть преобразованы центральным процессором (включая обрезку).
Все вершины необходимо было отправить в память графического процессора из основной оперативной памяти.
В настоящее время всё это обрабатывается графическим процессором, что значительно повышает производительность. 3D-художники часто ошибочно оценивают производительность, связанную с количеством полигонов, поскольку программы для 3D-моделирования (такие как Blender, 3ds Max и т. д.) вынуждены хранить геометрию в памяти процессора для её редактирования, что снижает производительность. Игровые движки больше полагаются на графический процессор, поэтому они могут гораздо эффективнее рендерить множество треугольников.
На мобильных устройствах ситуация иная. Видеокарты для ПК и консолей — это мощные монстры, способные потреблять из электросети столько электроэнергии, сколько им нужно. Мобильные видеокарты ограничены крошечным аккумулятором, поэтому им нужно быть гораздо более энергоэффективными.
Чтобы быть более эффективными, мобильные графические процессоры стараются избегать overdraw. Перерисовка происходит, когда один и тот же пиксель на экране отображается более одного раза. Представьте себе город с несколькими зданиями. Графические процессоры не знают, что является видимым, а что скрытым, пока не отрисуют его. Например, может быть нарисован дом, а затем ещё один дом перед ним (что означает, что рендеринг одного и того же пикселя происходит дважды). Графические процессоры ПК обычно не слишком заботятся об этом и просто добавляют больше пиксельных процессоров, чтобы увеличить производительность (что также увеличивает энергопотребление).
Использование большего количества ресурсов на мобильных устройствах невозможно, поэтому они используют технологию, называемую плиточным рендерингом, которая делит экран на сетку. Каждая ячейка хранит список отрисованных на ней треугольников и сортирует их по глубине для минимизации перерисовки. Эта технология повышает производительность и снижает энергопотребление, но снижает производительность обработки вершин. В результате для отрисовки обрабатывается меньше вершин и треугольников.
Кроме того, тайловый рендеринг испытывает трудности, когда на небольшом участке экрана находятся мелкие объекты с большим количеством геометрических элементов. Это вынуждает мобильные графические процессоры создавать большую нагрузку на один тайл экрана, что значительно снижает производительность, поскольку все остальные ячейки должны ждать его завершения, прежде чем отобразить кадр.
Подводя итог, не беспокойтесь о количестве вершин на мобильных устройствах, но избегайте концентрации вершин в небольших областях экрана. Если персонаж, NPC, транспортное средство и т. д. находится далеко (и выглядит крошечным), используйте модель с более низким уровнем детализации (LOD). Даже на настольных видеокартах желательно избегать треугольников размером меньше пикселя на экране.
Обратите внимание на дополнительную обработку вершин, необходимую при использовании:
Skinning (скелетная анимация)
Morphs (ключи формы)
Объекты с вершинным освещением (часто встречается на мобильных устройствах)
Пиксельные/фрагментные шейдеры и скорость заполнения
В отличие от обработки вершин, стоимость фрагментного (попиксельного) шейдинга за последние годы значительно выросла. Разрешение экранов увеличилось: площадь 4K-экрана составляет 8 294 400 пикселей против 307 200 пикселей у старого VGA-экрана с разрешением 640×480. Это в 27 раз больше! Кроме того, сложность фрагментных шейдеров резко возросла. Физически корректный рендеринг требует сложных вычислений для каждого фрагмента.
Проверить, ограничена ли скорость заполнения проекта, можно довольно просто. Отключите V-Sync, чтобы предотвратить ограничение кадров в секунду, затем сравните количество кадров в секунду при работе с большим окном и при работе с очень маленьким окном. Вы также можете получить пользу от аналогичного уменьшения размера карты теней, если используете тени. Обычно при использовании маленького окна FPS увеличивается довольно сильно, что указывает на то, что вы в какой-то степени ограничены в скорости заполнения. С другой стороны, если FPS практически не увеличивается, значит, узкое место находится в другом месте.
Производительность в проекте с ограниченной скоростью заполнения можно повысить, уменьшив объём работы, выполняемой графическим процессором. Это можно сделать, упростив шейдер (например, отключив ресурсоёмкие опции, если вы используете StandardMaterial3D) или уменьшив количество и размер используемых текстур. Кроме того, при использовании частиц без затенения рассмотрите возможность принудительного затенения вершин в их материале для снижения стоимости затенения.
См. также
На поддерживаемом оборудовании Затенение с переменной скоростью можно использовать для снижения затрат на обработку затенения, не влияя на резкость краев на конечном изображении.
При ориентации на мобильные устройства рассмотрите возможность использования максимально простых шейдеров, которые вы можете себе позволить.
Чтение текстур
Другим фактором, влияющим на фрагментные шейдеры, является стоимость чтения текстур. Чтение текстур — дорогостоящая операция, особенно при чтении нескольких текстур в одном фрагментном шейдере. Кроме того, следует учитывать, что фильтрация может ещё больше замедлить процесс (трилинейная фильтрация между MIP-текстурами и усреднение). Чтение текстур также потребляет много энергии, что является серьёзной проблемой на мобильных устройствах.
Если вы используете сторонние шейдеры или пишете собственные шейдеры, старайтесь использовать алгоритмы, требующие как можно меньшего количества чтений текстур.
Сжатие текстур
По умолчанию Godot сжимает текстуры 3D-моделей при импорте, используя сжатие видеопамяти (VRAM). Сжатие видеопамяти не так эффективно с точки зрения размера, как PNG или JPG при сохранении, но значительно повышает производительность при отрисовке достаточно больших текстур.
Это связано с тем, что основной целью сжатия текстур является сокращение пропускной способности между памятью и графическим процессором.
В 3D форма объектов больше зависит от геометрии, чем от текстуры, поэтому сжатие обычно незаметно. В 2D сжатие больше зависит от формы внутри текстур, поэтому артефакты, возникающие при 2D-сжатии, более заметны.
В качестве предупреждения следует отметить, что большинство устройств Android не поддерживают сжатие текстур с прозрачностью (только непрозрачные), поэтому имейте это в виду.
Примечание
Даже в 3D-текстурах "pixel art" следует отключать сжатие видеопамяти, поскольку это негативно скажется на их внешнем виде, не приводя к значительному повышению производительности из-за их низкого разрешения.
Постобработка и тени
Эффекты постобработки и тени также могут быть затратными с точки зрения фрагментного затенения. Всегда проверяйте их влияние на различное оборудование.
Уменьшение размера карт теней может повысить производительность, как при записи, так и при чтении. Кроме того, лучший способ улучшить производительность теней — отключить тени для как можно большего числа источников света и объектов. Тени небольших или удалённых источников OmniLight/SpotLight часто можно отключить, что лишь незначительно повлияет на визуальный эффект.
Прозрачность и смешивание
Прозрачные объекты представляют собой серьёзные проблемы с эффективностью рендеринга. Непрозрачные объекты (особенно в 3D) можно визуализировать практически в любом порядке, и Z-буфер обеспечит затенение только передних объектов. Прозрачные или смешанные объекты работают иначе. В большинстве случаев они не могут полагаться на Z-буфер и должны визуализироваться в "painter's order" (то есть от заднего плана к переднему), чтобы выглядеть корректно.
Прозрачные объекты также особенно плохо влияют на скорость заполнения, поскольку каждый элемент должен быть отрисован, даже если позже поверх него будут отрисованы другие прозрачные объекты.
Непрозрачным объектам это не нужно. Обычно они могут воспользоваться преимуществами Z-буфера, сначала записав данные только в Z-буфер, а затем выполнив фрагментный шейдер только для "победившего" фрагмента — объекта, находящегося впереди в определённом пикселе.
Прозрачность особенно затратна при перекрытии нескольких прозрачных объектов. Обычно лучше использовать как можно меньшие по размеру прозрачные области, чтобы минимизировать требования к скорости заполнения, особенно на мобильных устройствах, где скорость заполнения очень дорога. Действительно, во многих ситуациях рендеринг более сложной непрозрачной геометрии может оказаться быстрее, чем использование прозрачности для "обмана".
Советы по работе с несколькими платформами
Если вы планируете выпустить игру на нескольких платформах, тестируйте её как можно раньше и часто на всех платформах, особенно на мобильных. Разрабатывать игру для ПК, а в последний момент пытаться портировать её на мобильные устройства — верный путь к провалу.
В целом, следует разрабатывать игру, ориентируясь на наименьший общий знаменатель, а затем добавлять дополнительные улучшения для более мощных платформ. Например, можно использовать метод рендеринга совместимости как для настольных компьютеров, так и для мобильных платформ, если вы ориентируетесь на обе платформы.
Мобильные/плиточные рендереры
Как уже было сказано, графические процессоры мобильных устройств работают совершенно иначе, чем графические процессоры настольных компьютеров. Большинство мобильных устройств используют тайловые рендереры. Тайловые рендереры разбивают экран на плитки стандартного размера, помещающиеся в сверхбыструю кэш-память, что сокращает количество операций чтения/записи в основную память.
Однако есть и недостатки. Плиточный рендеринг может значительно усложнить и удорожить реализацию некоторых техник. Плиточные модели, использующие результаты рендеринга в разных тайлах или сохраняемые результаты предыдущих операций, могут работать очень медленно. Будьте особенно внимательны при тестировании производительности шейдеров, текстур области просмотра и постобработки.