
Управление использованием GPU для игр на ПК и консолях
Это третья статья в серии, которая раскрывает советы по оптимизации для ваших проектов Unity. Используйте их в качестве руководства для работы на более высоких частотах кадров с меньшими ресурсами. После того как вы попробуете эти лучшие практики, обязательно ознакомьтесь с другими страницами в серии:
- Настройка вашего проекта Unity для повышения производительности
- Оптимизация производительности для графики высокого класса
- Продвинутое программирование и архитектура кода
- Улучшенная физическая производительность для плавного игрового процесса
Поймите ограничения вашего целевого оборудования и как профилировать GPU для оптимизации рендеринга вашей графики. Попробуйте эти советы и лучшие практики, чтобы уменьшить нагрузку на GPU.
Найдите много других лучших практик в бесплатной электронной книге, Оптимизируйте производительность вашей игры для консоли и ПК.

Используйте пакетирование вызовов отрисовки
Чтобы нарисовать GameObject на экране, Unity отправляет вызов рисовки в графический API (например, OpenGL, Vulkan или Direct3D). Каждый вызов рисовки требует много ресурсов.
Изменения состояния между вызовами рисовки, такие как переключение материалов, могут вызвать накладные расходы на стороне ЦП. Аппаратное обеспечение ПК и консолей может обрабатывать много вызовов рисовки, но накладные расходы на каждый вызов все еще достаточно высоки, чтобы попытаться их уменьшить. На мобильных устройствах оптимизация вызовов рисовки имеет жизненно важное значение. Вы можете достичь этого с помощью группировки вызовов рисовки.
Группировка вызовов рисовки минимизирует эти изменения состояния и снижает затраты ЦП на рендеринг объектов. Unity может объединить несколько объектов в меньшее количество групп с использованием нескольких техник с High Definition Render Pipeline (HDRP) или Universal Render Pipeline (URP):
- Группировка SRP: Включите SRP Batcher в Pipeline Asset в разделе Advanced. При использовании совместимых шейдеров SRP Batcher уменьшает настройку GPU между вызовами рисовки и делает данные материалов постоянными в памяти GPU. Это также может значительно ускорить время рендеринга вашего ЦП. Используйте меньше вариантов шейдеров с минимальными ключевыми словами для улучшения группировки SRP. Обратитесь к документации SRP, чтобы узнать, как ваш проект может воспользоваться этим рабочим процессом рендеринга.
- GPU инстансирование: Если у вас есть большое количество идентичных объектов с одной и той же сеткой и материалом, используйте GPU инстансирование, чтобы сгруппировать их через графическое оборудование. Чтобы включить GPU инстансирование, выберите ваш материал в окне Проект в Инспекторе, затем отметьте Включить инстансирование.
- Статическая пакетировка: Для неподвижной геометрии Unity может уменьшить количество вызовов отрисовки для сеток, использующих один и тот же материал. Это более эффективно, чем динамическая пакетировка, но использует больше памяти. Отметьте все сетки, которые никогда не движутся, как Пакетировка Статическая в Инспекторе. Unity объединяет все статические сетки в одну большую сетку во время сборки. Класс StaticBatchingUtility также позволяет вам создавать эти статические пакеты во время выполнения (например, после генерации процедурного уровня неподвижных частей).
- Динамическая пакетировка: Для небольших сеток Unity может группировать и преобразовывать вершины на ЦП, а затем отрисовывать их все за один раз. Однако имейте в виду, что не следует использовать это, если у вас недостаточно низкополигональных сеток (не более 300 вершин каждая и 900 общих атрибутов вершин). В противном случае включение этого будет тратить время ЦП на поиск небольших сеток для пакетировки.
Вы можете максимизировать пакетировку несколькими простыми способами:
- Используйте как можно меньше текстур в сцене. Меньшее количество текстур требует меньше уникальных материалов, что упрощает их пакетировку. Кроме того, используйте Текстурные атласы где это возможно.
- Всегда запекайте световые карты в максимально возможном размере атласа. Меньшее количество световых карт требует меньше изменений состояния материалов, но следите за объемом памяти.
- Будьте осторожны, чтобы не инстансировать материалы непреднамеренно. Доступ к Renderer.material в скриптах дублирует материал и возвращает ссылку на новую копию. Это нарушает любую существующую партию, которая уже включает в себя материал. Если вы хотите получить доступ к материалу пакетированного объекта, используйте Renderer.sharedMaterial вместо этого.
- Следите за количеством статических и динамических партий по сравнению с общим количеством вызовов отрисовки, используя Profiler или статистику отрисовки во время оптимизации.
Смотрите документацию по Draw call batching для получения дополнительной информации.

Проверьте отладчик кадров
Используйте отладчик кадров, чтобы заморозить воспроизведение на одном кадре и пройти через процесс того, как Unity строит сцену. Таким образом, вы можете выявить возможности для оптимизации. Ищите объекты GameObjects, которые отрисовываются без необходимости, и отключайте их, чтобы уменьшить количество вызовов отрисовки на кадр.
Одно из основных преимуществ отладчика кадров заключается в том, что вы можете связать вызов отрисовки с конкретным объектом GameObject в сцене. Это упрощает исследование определенных проблем, которые могут быть невозможны в внешних отладчиках кадров.
Примечание: Отладчик кадров не показывает отдельные вызовы отрисовки или изменения состояния. Хотя только родные GPU Profilers могут предоставить вам подробную информацию о вызовах отрисовки и времени, отладчик кадров все равно может быть очень полезен для отладки проблем с конвейером или проблем с пакетированием.
Читать документацию отладчика кадров для получения дополнительных сведений.
Оптимизируйте коэффициент заполнения и уменьшите переотрисовку
Коэффициент заполнения относится к количеству пикселей, которые GPU может отрисовать на экране каждую секунду. Если ваша игра ограничена коэффициентом заполнения, это означает, что она пытается отрисовать больше пикселей на кадр, чем GPU может обработать.
Отрисовка на одном и том же пикселе несколько раз называется избыточной отрисовкой. Избыточная отрисовка снижает коэффициент заполнения и требует дополнительной пропускной способности памяти. Наиболее распространенные причины избыточной отрисовки:
- Перекрывающаяся непрозрачная или прозрачная геометрия
- Сложные шейдеры, часто с несколькими проходами рендеринга
- Неоптимизированные частицы
- Перекрывающиеся элементы пользовательского интерфейса
Хотя вы должны минимизировать его влияние, не существует универсального подхода к решению проблемы избыточного рендеринга. Экспериментируйте с следующими техниками, чтобы уменьшить его влияние.
Сократите количество пакетов
Как и на других платформах, оптимизация на консолях часто означает уменьшение количества пакетов вызовов отрисовки. Вот несколько техник, которые могут помочь:
- Используйте Окклюзия, чтобы удалить объекты, скрытые за объектами переднего плана, и уменьшить избыточный рендеринг. Имейте в виду, что это требует дополнительной обработки на ЦП, поэтому используйте Профайлер, чтобы убедиться, что перенос работы с ГП на ЦП действительно полезен.
- Инстансирование ГПУ также может уменьшить ваши пакеты, если у вас много объектов, которые используют одну и ту же сетку и материал. Ограничение количества моделей в вашей сцене может улучшить производительность. Если это сделано искусно, вы можете создать сложную сцену, не делая ее повторяющейся.
- SRP Batcher может уменьшить настройку ГПУ между вызовами отрисовки, объединяя Привязку и Отрисовку команд ГПУ. Чтобы воспользоваться этой пакетной обработкой SRP, используйте столько материалов, сколько необходимо, но ограничьте их небольшим количеством совместимых шейдеров (например, Lit и Unlit шейдеров в URP и HDRP).

Обратите внимание на отсечение
Окклюзия происходит для каждой камеры и может значительно повлиять на производительность, особенно когда несколько камер включены одновременно. Unity использует два типа окклюзии:
- Окклюзия фрустра выполняется автоматически для каждой камеры. Это гарантирует, что объекты игры вне Объема видимости не рендерятся для экономии производительности.
- Вы можете установить расстояния отсечения по слоям вручную через Camera.layerCullDistances. Это позволяет вам отсекать небольшие GameObjects на расстоянии, меньшем, чем значение по умолчанию farClipPlane. Для этого организуйте GameObjects в Слои. Используйте массив .layerCullDistances, чтобы назначить каждому из 32 слоев значение меньше farClipPlane (или используйте 0, чтобы по умолчанию использовать farClipPlane).
- Unity сначала отсеивает по слоям. Он сохраняет только GameObjects на слоях, которые использует Камера. После этого отсечение фрустрации удаляет любые GameObjects вне Фрустрации камеры.
- Отсечение фрустрации выполняется в виде серии задач, которые используют доступные рабочие потоки. Каждый тест отсечения слоя быстрый (по сути, это просто операция с битовой маской). Тем не менее, эта стоимость все равно может накапливаться при большом количестве GameObjects. Если это станет проблемой для вашего проекта, вам может понадобиться реализовать какую-то систему для деления вашего мира на "секторы" и отключать сектора, которые находятся вне фрустрации камеры, чтобы снизить нагрузку на систему отсечения слоев/фрустрации Unity.
- Отсечение окклюзии удаляет любые GameObjects из вида игры, если камера не может их видеть. Объекты, скрытые за другими объектами, все еще могут рендериться и потреблять ресурсы. Используйте Отсечение окклюзии, чтобы отбросить их.
- Например, рендеринг комнаты не нужен, если дверь закрыта и камера не может видеть внутрь комнаты. Если вы включите отсечение окклюзии, это может значительно увеличить производительность, но также использовать больше дискового пространства, времени ЦП и ОЗУ. Unity запекает данные окклюзии во время сборки, а затем нужно загрузить их с диска в ОЗУ во время загрузки сцены.
- Хотя отсечение фрустрации вне вида камеры является автоматическим, отсечение окклюзии - это запеченный процесс. Просто отметьте ваши объекты как Статические, Окклюдеры или Окклюдируемые, затем запеките через диалог Окно > Рендеринг > Отсечение окклюзии.
Смотрите Учебник по работе с отсечением окклюзии, чтобы узнать больше.
Используйте динамическое разрешение
Настройка камеры Разрешение динамического масштаба позволяет вам динамически изменять масштаб отдельных целевых рендеров, чтобы уменьшить нагрузку на GPU. В случаях, когда частота кадров приложения снижается, вы можете постепенно уменьшать разрешение, чтобы поддерживать постоянную частоту кадров.
Unity инициирует это масштабирование, если данные о производительности указывают на то, что частота кадров собирается снизиться из-за привязки к GPU. Вы также можете заранее инициировать это масштабирование вручную с помощью скрипта. Это полезно, если вы приближаетесь к ресурсоемкому участку приложения. Если масштабировать постепенно, динамическое разрешение может быть почти незаметным.
Смотрите страницу руководства по динамическому разрешению для списка поддерживаемых платформ.

Проверьте несколько видов камеры
Иногда вам нужно рендерить с более чем одной точки зрения во время вашей игры. Например, в шутерах от первого лица (FPS) обычно рисуют оружие игрока и окружение отдельно с разными углами обзора (FOV). Это предотвращает искажение объектов на переднем плане через широкий угол обзора фона.
Вы можете использовать Накладка камер в URP, чтобы рендерить более одного вида камеры. Тем не менее, для каждой камеры все еще выполняется значительное отсечение и рендеринг. Каждая камера несет некоторые накладные расходы, независимо от того, выполняет ли она значимую работу или нет.
Используйте только компоненты камеры, необходимые для рендеринга. На мобильных платформах каждая активная камера может использовать до 1 мс времени ЦП, даже когда ничего не рендерит.
Используйте уровень детализации
Когда объекты движутся на расстояние, Уровень детализации (LOD) может регулировать или переключать их на использование моделей с более низким разрешением с более простыми материалами и шейдерами. Это усиливает производительность GPU.
Смотрите курс по работе с LOD на Unity Learn для получения дополнительных сведений.

Профилируйте эффекты постобработки
Профилируйте ваши эффекты постобработки, чтобы увидеть их стоимость на GPU. Некоторые эффекты на весь экран, такие как Сияние и Глубина резкости, могут быть дорогими, поэтому стоит поэкспериментировать, пока вы не найдете желаемый баланс между визуальным качеством и производительностью.
Постобработка не колеблется сильно во время выполнения. Как только вы определили ваши Объемные переопределения, выделите эффектам постобработки статическую часть вашего общего бюджета кадров.
Избегайте шейдеров тесселяции
Тесселяция делит форму на меньшие версии самой себя, что может улучшить детали за счет увеличенной геометрии. Хотя есть примеры, когда тесселяция имеет наибольший смысл, такие как кора дерева в демонстрации Unity Книга мертвых, старайтесь избегать тесселяции на консолях, так как они дорогие для GPU.
Читать больше о демонстрации Книга мертвых здесь.
Замените геометрические шейдеры на вычислительные шейдеры
Как и шейдеры тесселяции, геометрические и вершинные шейдеры могут выполняться дважды за кадр на GPU – один раз во время предварительного прохода глубины и снова во время прохода теней.
Если вы хотите генерировать или изменять данные вершин на GPU, шейдер вычислений часто является лучшим выбором, особенно по сравнению с геометрическим шейдером. Выполнение работы в шейдере вычислений означает, что вершинный шейдер, который фактически рендерит геометрию, может работать гораздо быстрее.
Узнайте больше о основных концепциях шейдеров.

Стремитесь к хорошей заполняемости волновых фронтов
Когда вы отправляете вызов рисования на GPU, эта работа делится на множество волновых фронтов, которые Unity распределяет по доступным SIMD внутри GPU. Каждый SIMD имеет максимальное количество волновых фронтов, которые могут работать одновременно.
Занятость волнового фронта относится к тому, сколько волновых фронтов в настоящее время используется относительно максимума. Это измеряет, насколько хорошо вы используете потенциал GPU. Инструменты профилирования для разработки на консолях показывают занятость волнового фронта в больших деталях.
В приведенном выше примере из Книги мертвых Unity волновые фронты вершинного шейдера отображаются зеленым цветом, а волновые фронты пиксельного шейдера – синим. На нижнем графике много волновых фронтов вершинного шейдера появляются без значительной активности пиксельного шейдера. Это показывает недоиспользование GPU.
Если вы выполняете много работы с вершинными шейдерами, которая не приводит к пикселям, это может указывать на неэффективность. Хотя низкая заполняемость волнового фронта не обязательно плоха, это метрика, которую вы можете использовать для начала оптимизации ваших шейдеров и проверки других узких мест. Например, если у вас есть задержка из-за операций с памятью или вычислениями, увеличение заполняемости может улучшить производительность. С другой стороны, слишком много активных волновых фронтов может вызвать тряску кэша и снизить производительность.
Используйте асинхронные вычисления
Если у вас есть интервалы, когда вы недоиспользуете GPU, вы можете использовать асинхронные вычисления, чтобы переместить работу шейдера вычислений параллельно с вашей графической очередью. Например, во время генерации теневой карты GPU выполняет рендеринг только по глубине. В этот момент выполняется очень мало работы с пиксельными шейдерами, и многие волновые фронты остаются незанятыми.
Если вы можете синхронизировать некоторую работу шейдера вычислений с рендерингом только по глубине, это обеспечит лучшее общее использование GPU. Неиспользуемые волновые фронты могут помочь с Экранным пространственным амбиентным окклюзией (SSAO) или любой задачей, которая дополняет текущую работу.
Посмотрите эту сессию о Оптимизации производительности для высококлассных консолей от Unite.

Одна из наших самых полных руководств когда-либо собирает более 80 практических советов о том, как оптимизировать ваши игры для ПК и консолей. Созданные нашими экспертами в области успеха и ускоренных решений, эти подробные советы помогут вам максимально использовать Unity и повысить производительность вашей игры.