Search Unity

ОПТИМИЗАЦИЯ СЕРВЕРНОЙ ЧАСТИ ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА

, 7 сентября, 2015

В Unity 4.6 / 5.0 существует проблема: пакеты данных для рендеринга пользовательского интерфейса генерируются очень медленно. Этому есть несколько причин, но главным образом это произошло из-за нехватки времени на оптимизацию этой части интерфейса. Тогда мы были слишком заняты работой над программным интерфейсом и доступностью пользовательского интерфейса, а срок поджимал. Закончив работу над этими версиями, мы решили провести анализ, чтобы узнать, как лучше исправить ситуацию.

В итоге к версии 5.2 мы убрали обработку интерфейса (кроме планировки задач) из основного потока, а также внесли исправления в некоторые алгоритмы, используемые для сортировки пакетов.

Наша цель производительность

Мы разработали несколько тестовых сцен для испытания производительности интерфейса, чтобы получить исходную точку для работы над оптимизацией. Эти сцены подвергают интерфейс сильной нагрузке различными способами. К примеру, одна из них полностью заполняет экран кнопками, что хорошо подходит для проверки оптимизации сортировки и генерации пакетов. Во время тестирования элементы интерфейса постоянно изменяются, и с каждым кадром производится перерасчет. Используется около 10 тыс. элементов интерфейса (в реальном интерфейсе на рабочем поле бывает около 300 элементов).

Тест может размещать элементы интерфейса упорядоченно или в случайном порядке, и во втором случае алгоритм сортировки использует больше вычислительных ресурсов. Необходимо, чтобы сортировка пакетов проходила быстро, но в версиях 4.6 и 5.0 она серьезно замедляется в обоих вариантах сцены.

Для тестов использовался MacBook Air 13, модель 2013 г.

Часть сцены:unnamed

Версии до 4.6

Получив отчеты по бета-тестированию 4.6, мы узнали о том, что сортировка пакетов сильно замедляется при наличии большого количества элементов на рабочем поле. Оказалось, что алгоритм сортировки недостаточно совершенен. После итерации проводилась проверка на столкновения и присвоение глубины по установленным правилам, и с добавлением большого количество элементов скорость алгоритма сильно снижалась (O(N^2)).

Версии 4.6 / 5.0

Мы разработали новый алгоритм. Основной принцип: упорядоченные элементы обычно отображаются в том же порядке. Производится построение прямоугольной границы для группы из N элементов, и новые элементы сначала проверяются на столкновение с этой группой, а потом — с отдельными элементами. При этом достигается достаточное быстродействие в тех случаях, когда элементы интерфейса находятся рядом. Однако в тех случаях, когда их расположение случайно или же они расположены далеко друг от друга, улучшение практически незаметно. В одном из примеров при добавлении случайных элементов скорость серьезно упала, и создание сцены заняло 100 мс, что никуда не годится.

unnamed (1)

Кроме того, из временного профиля на изображении становится ясно, что этот алгоритм не позволяет производить дополнительных действий. Генерация пакетов происходит прямо перед рендерингом интерфейса, часто после вызова LateUpdate и рендеринга камер сцены. Следовало бы перенести генерацию назад, чтобы она происходила сразу после LateUpdate, одновременно с рендерингом сцены.

unnamed (2)

Оптимизация сортировки (часть 1)

Первая попытка оптимизации алгоритма сортировки всё ещё основывалась на идее локальности элементов, но с некоторыми улучшениями. Был сделан акцент на оптимальность групп элементов с точки зрения процесса генерации пакетов. Было достигнуто некоторое ускорение, однако при обработке сцен с большими пространствами между элементами все равно происходило замедление. Кроме того, алгоритм плохо масштабировался на большое количество элементов.

Элементы не сгруппированы в пространствеunnamed (3)

Элементы сгруппированы в пространстве
unnamed (4)

После этого мы решили, что необходим новый подход.

Оптимизация сортировки (часть 2)

Нам требовался такой алгоритм сортировки, который не замедлял бы обработку больших сцен с разнесенными элементами. В конце концов мы добавили сетку рабочего поля. К каждой ячейке сетки привязана группа, к которой приписываются элементы, прикасающиеся к ячейке, поэтому при добавлении нового элемента нужно рассматривать только те группы, к ячейкам которых прикасается элемент, чтобы узнать, с какими другими элементами он может быть объединен в пакет. После этого скорость обработки сцен, особенно в случаях со случайными сценами, серьезно возросла.

Элементы не сгруппированы в пространстве unnamed (5)

Элементы сгруппированы в пространствеunnamed (6)

Geometry Job

Компонент GeometryJob — одно из нововведений, добавленных в Unity 5. Мы воспользовались им, чтобы отделить обработку интерфейса от основного потока. GeometryJob используется для заполнения вершинных (индексных) буферов. Его возможности позволили снять большое количество кода с основного потока. Задание и инструкции к нему занимают некоторые количество памяти, однако оно незначительно по сравнению с высвобожденными ресурсами.

unnamed (7)

Упрощение сортировки пакетов

Мы также выполнили серию оптимизаций меньшего масштаба. Наиболее значимой из них была векторизация проверок на пересечение прямоугольников во время сортировки. Ранее эти проверки занимали до 60% времени сортировки.

После векторизации проверок
unnamed (8)

Taking it all off (the main thread)

Следующий логический шаг — полностью убрать генерацию интерфейса из основного потока. Мы использовали внутреннюю систему для составления списка задач для параллельного и последовательного исполнения:

1) Разделение входящих инструкций к интерфейсу на множество инструкций, пригодных для рендеринга (в одной инструкции к интерфейсу может быть много запросов на отрисовку из-за использования различных материалов и подсеток). Память выделяется на максимально возможное количество полученных инструкций, которые обрабатываются параллельно и помещаются в выходной массив, который сжимается в единую секцию памяти, содержащую только допустимые инструкции.

2) Сортировка инструкций. Сравниваются глубины, перекрытия и т.д. В результате получается буфер команд, который будет требовать наименьшее возможное изменение состояния системы во время рендеринга.

3) Генерация пакетов

  1. Генерация буфера команд для рендеринга, создание вызовов к рисовке.
  2. Генерация инструкций на трансформацию, используемых Geometry Job.

Задания планируются после LateUpdate. Они выполняются во время рендеринга сцены и до появления интерфейса на экране. Когда эти задания планируются, на основном потоке ставится барьер: вызовы на рендеринг рабочего поля и GeometryJob ждут генерации нужных данных.

В примере видно, как задание GeometryJob останавливается и ждет генерации пакетов. В этих сценах элементы интерфейса — единственное, для чего нужен рендеринг; мы считаем, что проблема остановки менее серьезна в тех случаях, когда обрабатываются сложные сцены.

unnamed (9)

Выполнение на компьютере с большим количеством ядер, чем мой  MacBook Air
unnamed (10)
Итог: 0,4 мс для основного потока при использовании сложного интерфейса.

Различные улучшения производительности

  • Граница отсечения (оказалось, что в большинстве интерфейсов не нужен буфер шаблонов; сделана для уменьшения количества вызовов к рисовке и изменений состояния).
  • Граница отбрасывания (отбрасываются все элементы вне границы рендера).
  • Умный буфер команд рабочего поля.
    • Общие шейдеры и материалы для нормальных элементов и текстов.
    • Уменьшение количества вызовов Set Pass.
    • Большое количество специфических данных интерфейса убрано в блоки свойств материалов.
    • Сначала один вызов Set Pass, затем множество вызовов рисовки.
  • * Единый буфер индексов для интерфейса.
    • Для рендеринга используется DrawIndexRange.
    • Единый буфер VBO / индексов, масштабируется по ситуации.
    • Используется следующий вызов рисовки, когда индексов более 2^16.

Дальнейшие шаги

Скорость генерации и сортировки пакетов стала удовлетворительной. Конечно, их оптимизацию можно продолжать и далее, но больше всего времени сейчас занимает GeometryJob, изолированная от основного потока. Скорее всего, в других аспектах также есть неоптимальные решения; например, можно векторизовать медленные вычисления. Все реализованные улучшения уже включены в Unity 5.2.

Заключение

Благодаря нововведениям Unity 5.2 им мы смогли минимизировать нагрузки интерфейса на основной поток и оптимизировать сбор пакетов. Во процессе работы мы использовали Unity Profiler для поиска проблемных участков. Несколько раз нам приходилось начинать заново, так как предыдущее решение оказывалось неоптимальным. В этом заключается значительная часть работы над Unity: поиск и решение проблем, о которых нам сообщаете вы. Вместе с вами мы делаем Unity лучше.

Команда UI

42 replies on “ОПТИМИЗАЦИЯ СЕРВЕРНОЙ ЧАСТИ ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА”

The UI is still broken in the latest 5.2.0p1. Half of the gui is unclickable. raycasts go thru.
Maybe thats why the gui is faster, because IT IS NOT WORKING.

What I want to know is if these improvements can be applied to the Legacy GUI system.
The UGUI system is kinda unwieldly, and it’s difficult to make procedural layouts that use data, which is something the Legacy GUI is incredibly good at doing.

After a little bit of fiddling with both, I’d even say the Legacy GUI is easier to make look good at different resolutions than UGUI is, and it doesn’t clutter up the hierarchy with tons of objects.

If there is an eventsystem in the scene (and no graphic raycasters), then your UI will not be clickable.

For every canvas root node you should ensure you have a Graphic Raycaster, as a graphicRaycaster does not raycast nest UI elements on canvas root nodes not in the same root node as tha Raycaster itself.

You also need some kind of InputModule in the scene, either one of the default InputModules, or one created by yourself.

I have no problems making the UGUI system work for me, using my own InputManager/Inut module handlers.

I also upgraded to 5.2 and for my iOS game the CPU usage dropped from 45% to 20%. Great! But on the other hand the GPU usage increased, which now makes my game a lot slower. In the xcode performance profiler the renderer now runs constantly at 100%, before it only ran at 33%.

Very strange…:(

I upgraded to 5.2 yesterday and it’s killing me softly. So many weird issues now. My Android game is pretty much unplayable at this point. Reverting to 5.1.3.

I don’t see shadows or other mesh modifiers in Performance Project (looking in picture), they heavily influence on UI performance / memory allocations and are the bottleneck of whole system.

This rules! Thanks for going so in depth. We are updating our UI heavy project, XPETS, to 5.2 right now. Excited to see if we get some performance increases!

First tests shows a dramatic decrease in frame rate for our UI-based game.

For one Android phone the frame rate dropped to about half of Unity 5.1.2. Will analyse why this is happening in the coming week. We had performance problems before and they are far worse now…

အမ ရ ထ တ လ တ ဖတ ပ ည လ မ အ မ ပ ပသ ရတယ ..ဒ လ ပ ပ ..လ တစ ယ က မ တစ ခ ခ က တ ထ ခ ကတ ခ ည ပ ပ …အမမ လ ထ ခ တ attraction တရပ ရ နတယ ..အ ဒ က ဘ လ တ ပ တတ ဘ …အမစ က ဖတ ပ ရင သ တယ လ တ တ မ တယ ….အမ ပ ရ င ပ စ… န က ဆ ပ တ စ သ လ အ တ က က မ သဗ ..ဘ တ .. ရ င ရယ က ရ င ရယ ……… ခင တ …မ နတ

Great job, one more reason to complete my transition to the new UI.

Quick question: should we expect benefits from multithreading when making 3D UIs ? Are they also rendered at the end of the frame ? (I would assume they are not)
What about rendering the UI to a rendertexture? I suspect multithreading would not provide the same gain for similar reasons

Hi,

Great news, I just have one small question. What exactly do you mean with «vectorised a bunch of our rectangular overlap checks in the sorting» and «and it’s also using a bunch of slow maths that could handle being vectorised very nicely»? Could you give one small example?

Do you rewrite the whole UI System and Change the API of UI System? And Is it expensive to update existing project to unity 5.2? finally, Whether is the new UI System compatible with the old UI System’s API?

Since you mention command buffers, will we have the ability to use the UI system with the existing command buffer API? Specifically, I want to render canvas renderers to a render texture, but I think it only works with mesh renderers. Maybe also expose the UI rendering step in the CameraEvent. This would be quite useful for making special effects for the UI, especially for text.

That parallel UI/geometry rendering scheme is a thing of beauty! Great job guys!

We are currently in the process of updating ALL of KSP’s UI to use 4.6/5 canvas components (from the many redundant/conflicting UI solutions we had before)… That in itself is already a massive load off our frame times, so Unity 5.2 should make our UI overhaul that much more worth the effort!

Very great news indeed! Many thanks from the KSP team!

Cheers

*You* are awesome! And I might re-think cnninag at some point now that you’ve brought to mind the possibility of doing it in December rather than the summer! I loved cnninag days as a child, but it was alway so hot.

«We reached the first step on the path to pulling the UI off the main thread by using the new Geometry Job system which was introduced in Unity 5. This is an internal feature that can be used to populate a vertex / index buffers in a threaded way.»

In this sentence, the word «internal» really sucks :D.

Any idea when mere mortals.. I mean when «us users» can implement and shedule jobs like this? ;)

Now this is finally great news, especially for slow but heavily multicore oriented platforms like Android or iOS in the future (the ipad air2 sits on 3 cores) :D

Congratulations, looking forward to run further tests on 5.2 more sooner than later.

Great to see this sort of a hard focus on optimization, this actually might be the last kick I need to migrate over to the new UI & update to 5.2 once it’s out!

Comments are closed.