- PVSM.RU - https://www.pvsm.ru -
В этой статье разбираются вопросы оптимизации UI-элементов проектов, сделанных в Unity. На основании информации из официальной документации и личного опыта я постарался наглядно объяснить принципы работы UI-элементов. Также здесь вы найдёте практические советы, которые помогут улучшить производительность вашего проекта в том, что касается пользовательского интерфейса.
UI-элементы — все элементы в Unity, относящиеся к построению пользовательского интерфейса. Сюда относятся, например: кнопка, текст, картинка, выпадающее меню и др.
Холст (canvas) — базовый элемент UI, являющийся контейнером для остальных элементов.
Меш (mesh) — совокупность параметров описывающих 3D-модели.
Квад (quad) — это меш, который представляет из себя четырехугольник.
Батчинг (batching) — объединение мешей объектов в один большой меш для более быстрой отрисовки.
Вызов отрисовки (draw-call) — команда на отрисовку от движка к графическому API (например, OpenGL или Direct3D).
Transparent queue — очередь отрисовки прозрачных объектов.
Альфа-смешивание (Alpha blending) — алгоритм смешивания пикселей по альфа-каналу для получения изображения с прозрачностью.
Атлас (Atlas) — вид ресурсов, который объединяет несколько текстур в одну.
В оптимизация UI нет универсальных правил, работающих в любой ситуации. Всё сводится к нахождению баланса между стоимостью батчинга и количеством вызовов отрисовки. Можно выделить четыре основные проблемы:
Базовым элементом пользовательского интерфейса Unity является холст. Он отвечает за генерацию, сортировку и отрисовку мешей дочерних элементов интерфейса. Все элементы UI должны быть дочерними к какому либо холсту, в противном случае они не будут отображаться в игре.
Отрисовка происходит от самого дальнего к самому ближнему объекту от камеры (back-to-front) в Transparent queue с альфа-смешиванием.
Отдельно надо отметить, что прозрачность UI элементов НЕ влияет на производительность. Даже если элемент полностью состоит из “непрозрачных” пикселей, он все равно будет отрисован с использованием альфа-смешивания.
Также важно понимать, что при отрисовке обрабатываются все пиксели всех активных элементов. Это не зависит от того, видны они, перекрыты другими объектами или вообще полностью прозрачны.
Перестроение интерфейса пользователя — это многоэтапный процесс, включающий в себя построение мешей каждого элемента UI, и попытка батчинга этих мешей для того, чтобы минимизировать количество отрисовок (draw calls).
Перестроение происходит в четыре этапа:
Перестроенный холст кешируется и переиспользуется до тех пор, пока один из элементов в холсте не помечается как изменённый.
Изменёнными (dirty) помечаются объекты, которые были активированы или деактивированы; у которых изменился материал, позиция, масштаб, поворот; изменилось текстовое значение у текстового компонента; производилось переназначение родителя и т.д.
В этом случае заново происходит перестроение холста, содержащего хотя бы один изменённый элемент. Правда, это относится только к тому холсту, в котором находится элемент. То есть изменения элементов в дочерних холстах не влияют на родительские.
Чем больше элементов в холсте, тем больше будут издержки на анализ и сортировку объектов.
Объединение мешей, или батчинг, помогает снизить нагрузку на GPU, уменьшая количество вызовов отрисовки. В процессе батчинга меши сортируются по глубине и проверяются на перекрытие. При проходе от дальнего элемента к ближнему (или от верхнего элемента к нижнему в иерархии) в рамках одного холста объекты с одинаковыми материалами или текстурами объединяются в один меш. Для этого между ними не должно быть объектов с иными материалами. Также объекты с иными материалами не должны перекрывать запекаемые объекты своими габаритными контейнерами. Операция батчинга мультипоточная, её производительность значительно разнится в зависимости от количества ядер у процессора.
Текст может батчиться с другим текстом, если у них один и тот же шрифт. При этом неважно, выбраны одинаковые или разные настройки размера шрифта и стили. Если же шрифты разные, то текст не будет батчиться.
Также надо учитывать, что текст может перекрыть объект своим габаритным контейнером, и такое перекрытие можно легко пропустить.
Рассмотрим пример. Есть три объекта A, B и C, расположенные в иерархии таким образом:
На изображении слева объекты A и C будут объединены, т.к. имеют один и тот же материал и не пересекаются с объектом B. На изображении справа объекты A и C не будут объединены, т.к. имеется пересечение с объектом B.
Прежде чем начинать оптимизацию, настоятельно рекомендуется провести профайлинг UI. Это поможет определить узкие места, приводящие к потере производительности (если они есть). Для профайлинга существует множество инструментов, как встроенных в Unity (Unity Profiler), так и сторонних. Но вопросы профайлинга UI в этой статье мы разбирать не будем.
Вот что можно посоветовать для оптимизации UI в Unity:
Raycast Target у компонента изображения.
У TextMeshPro он спрятан во вкладке Extra Settings.
Для TextMeshPro можно задать установку Raycast Target по умолчанию в Player Settings -> TextMest Pro -> Settings -> Enable Raycast Target.
Элементы проходят проверку, когда:
Также имеет смысл убирать флаг Raycast Target с дочерних элементов, если корневой объект уже имеет его и полностью перекрывает своей геометрией дочерние элементы. Например, стандартная кнопка Unity UI.
Изображение своей формой полностью перекрывает текст, в этом случае можно снять Raycast Target с компонента текста.
Если все элементы холста не ждут событий ввода, то можно удалить компонент Graphic Raycaster с холста/вложенного холста.
Текст в интерфейсе Unity состоит из сеток, в которых на каждый символ создаётся свой квад (quad). Меш перестраивается каждый раз, когда изменяется значение текста. Также перестроение происходит, если был выключен и повторно включён текстовый компонент или его родитель.
По умолчанию шрифты в Unity добавляются как динамические. Для каждого динамического шрифта, используемого в текстовом компоненте на сцене, создаётся свой атлас. В этот атлас включаются только используемые символы. Например, если текстовое поле содержит текст “New Text”, то созданный для него атлас будет содержать символы “N”, ”e”, “w”, “T”, “x” и “t”. Для каждого символа, отличающегося по размеру или стилю, будет создано своё представление в атласе.
Текст на сцене
Атлас шрифта
Если в процессе выполнения программы содержимое текстового компонента изменится и там появятся символы, которые отсутствуют в атласе, будет вызвана перестройка всего атласа. В случае, если в текстуре атласа есть свободное место, то необходимые символы будут просто добавлены туда. При этом символы, которые в данный момент не используются, не удалятся. Если в атласе недостаточно места для новых символов, его размер будет удвоен и заполнен заново на основе используемых символов в активных текстовых компонентах.
Если для проекта задано строго определённое количество символов, например только латинский алфавит, то стоит использовать статичные шрифты, которые хранятся в памяти постоянно. Если в вашем проекте допускается большое количество символов, то лучше всё же остановиться на динамических шрифтах.
Можно также повысить производительность, если заменить текстовый компонент на спрайт. Например, появляющиеся в игре цифры (счёт) можно сделать, используя спрайты из одного атласа, содержащего набор только необходимых символов. В этом случае не будет издержек на перестроение холста и атласа шрифта.
Использование резервных шрифтов (Fallback fonts), перечисленных в поле Font Names в настройках шрифта, приводит к увеличению используемой памяти. Особенно это заметно на пиктографических шрифтах.
Не рекомендуется использовать Best Fit, т.к. эта опция приводит к быстрому переполнению атласа и вызывает его перестройку.
Best fit игнорирует настройки размера шрифта и пытается уместить текст в габаритный прямоугольник текстового компонента.
Теперь пара слов о TextMeshPro (TMP), популярной замене стандартному текстовому компоненту Unity. TextMeshPro также перестраивает свою сетку каждый раз, когда значение текста меняется. Однако TMP-текст не использует динамические шрифты. Для него заранее генерируются атласы шрифтов, в которые включаются все необходимые символы. Если для какого-то текста на сцене нет символа в назначенном на его компонент шрифте, то TMP начинает искать в резервных шрифтах. Если и там ничего не находится, то TMP будет пытаться найти этот символ во всех загруженных шрифтах.
Best Fit в TMP не создаёт проблем, как обычный текстовый компонент, так что его вполне можно использовать.
Большое количество шрифтов с разными локализациями или большие атласы шрифтов могут занимать много памяти. Поэтому лучше использовать предварительную загрузку только необходимых для конкретной локализации шрифтов.
В World Space рекомендуется использовать TextMeshPro вместо TextMeshProUGUI.
TextMeshProUGUI используется в холстах.
Спрайтовые атласы — это вид ресурсов, который объединяет несколько текстур в одну. Они позволяют снизить количество отрисовок (draw calls) и повысить производительность.
Создание атласа.
Создаём атлас: Asset > Create > Sprite Atlas.
Выделяем атлас и помещаем необходимые sprite в Objects for Packing.
Нажимаем Pack Preview для предпросмотра атласа.
Нужно учитывать, что даже если на сцене используется один или несколько спрайтов из атласа, то атлас всё равно будет загружен целиком. Поэтому нет смысла пытаться внедрить все изображения в один гигантский атлас, который на устройствах с небольшой оперативной памятью может занять её ощутимую часть. Лучше разбить на какие-то более мелкие атласы, например на атлас пользовательского интерфейса меню игры и атлас интерфейса игрового режима.
Избегайте больших пустых мест в атласе, чтобы не занимать лишнее место в памяти. Для этого можно изменить размер атласа или добавить дополнительные изображения, чтобы максимально заполнить пустое пространство.
Для изображений, которые не попали в атлас, необходимо выбрать правильные настройки.
К каждому формату сжатия имеются требования, при соблюдении которых оно будет эффективно работать. Самые частые требования к изображениям:
При выборе формата сжатия Unity подскажет, если какие-то требования для выбранного формата не будут выполнены.
Также не забывайте, что форматы отличаются от целевого устройства. Дополнительную информацию и рекомендации по форматам можно посмотреть в официальной документации [11]:
Исчерпывающая информация о оптимизации Unity UI [12]
Ещё один официальный источник по этой теме, содержит информацию в более сжатой форме [13]
Официальная документация по атласам [14]
Дополнительная информация по форматам сжатия текстур [11]
Автор: Дмитрий
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ui/332439
Ссылки в тексте:
[1] Терминология: #therms
[2] Вводная: #enter
[3] Отрисовка Unity UI: #draw
[4] Перестроение интерфейса пользователя: #rebuild
[5] Батчинг мешей: #batch
[6] Общие советы по оптимизации UI: #common
[7] Работа с текстом и его оптимизация: #text
[8] Атласы Спрайтов: #atlas
[9] Резюмируем способы оптимизации: #resume
[10] Источники: #links
[11] официальной документации: https://docs.unity3d.com/Manual/class-TextureImporterOverride.html
[12] Исчерпывающая информация о оптимизации Unity UI: https://learn.unity.com/tutorial/optimizing-unity-ui#5c7f8528edbc2a002053b5a0
[13] Ещё один официальный источник по этой теме, содержит информацию в более сжатой форме: https://unity3d.com/how-to/unity-ui-optimization-tips
[14] Официальная документация по атласам: https://docs.unity3d.com/Manual/class-SpriteAtlas.html
[15] Источник: https://habr.com/ru/post/470608/?utm_source=habrahabr&utm_medium=rss&utm_campaign=470608
Нажмите здесь для печати.