Хабрахабр

Как ECS, C# Job System и SRP меняют подход к архитектуре

Мы в компании давно работаем с Unity и не могли не пригласить их ребят на Pixonic DevGAMM Talks, который был в сентябре. Field Engineer Валентин Симонов рассказал, как планировать архитектуру игр с учетом преимуществ новых технологий. Unity работает над ними уже несколько лет, чтобы добиться недостижимого ранее уровня производительности. Послушать выступление можно на YouTube, а почитать расшифровку со слайдами — сразу под катом.

Что если я скажу, что можно увеличить производительность вашей игры в 10 раз? На самом деле, это не совсем так, но в каждой шутке есть доля правды. Хочу рассказать о том, над чем мы сейчас работаем, что будет будущим Unity и что вы можете использовать уже сейчас.

Вот примеры, в которые я играю сам. На Unity делаются совершенно разные игры. Они используют разные фичи и для них нужна разная производительность, разный подход к разработке.

Это несколько особых фичей, которые при должном использовании позволят достигнуть существенного прироста производительности. И мы работаем над проектом, который называем Performance by Default. Особенно в задачах симуляции большого количества объектов, которые взаимодействуют друг с другом. В некоторых задачах мы измеряли х10 и даже х11.

И, на самом деле, не всем это нужно. Но когда мы говорим про Perfomance by Default, мы имеем в виду, что вам придется менять подход к разработке, сильно поменять подход к архитектуре игр.

Вы уберете все GameObject, уберете все Transform, иерархию и компоненты?». Популярный вопрос: «А что вы делаете в вашей ECS? Вы сможете работать с Unity ровно так же, как и сейчас, но если вы захотите большей производительности, то вам нужно знать о технологиях, о которых вкратце хочу рассказать. Нет, мы все это оставим.

Наверное, вы видели демку, которую мы показывали на одном из Unite. И хочу упомянуть еще одну технологию, которая называется Scriptable Render Pipelines (SRP) — она позволяет более эффективно писать рендер-пайплайн под вашу игру. Здесь на PC в real time симулируется гигантское количество юнитов, что-то около 60 тысяч (доходит до 100 тысяч и начинает чуть-чуть притормаживать):

А новые фичи, про которые я хочу рассказать, это: Entity Component System (ECS), C# Job System, наш новый суперкомпилятор Burst и Scriptable Render Pipelines (SRP).

Повторюсь: вам выбирать, хотите вы идти с нами вперед, изучать новые технологии или вам ОК разрабатывать игры, которые и так хорошо зарабатывают и просто делаются.

Чтобы понимать, что мы пытаемся решить, важно понимать, в каком состоянии находится железо в 2018 году.

С какого-то момента Single-thread Performance даже пошел вниз. Обратите внимание, как растет производительность и количество ядер CPU. Поэтому нам бы хотелось использовать мощь всех ядер. То есть сейчас у нас много ядер, но их производительность растет не так быстро.

И современный телефон может работать так же быстро, как и современный компьютер (но не очень долго из-за перегрева). В моем телефоне 8 ядер: 4 сильных и 4 слабых. Также нужно понимать, что увеличение производительности, это не только использование всех ядер, но и оптимизация одноядерной производительности.

И последняя картинка, которую мы всегда приводим в пример того, как производительность процессов идет вверх, а скорость доступа к памяти увеличивается не так сильно:

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

Вероятно многие из вас часами смотрят на подобную картинку в Unity:

Что-то делается, но хотелось бы занять их полностью. Здесь видно, что есть многопоточность, но остальные ядра и потоки в основном не заняты.

У вас есть выбор: Forward или Deferred, плюс разные настройки материалов, шейдеров, Command Buffer’ов и так далее. Сейчас рендеринг у нас, это такой black box. Можно сделать красивую картинку, но многие алгоритмы реализовать чрезвычайно сложно.

И все мы знаем об архитектуре в Unity: компоненты, GameObject’ы, иерархия Transform’ов, весь код, все данные в MonoBehaviour и каждый компонент обрабатывает свои данные.

Рано или поздно вы сталкиваетесь с этим и понимаете, как нужно и не нужно делать. Но с текущим положением вещей есть проблемы. А если у вас большое количество компонентов и апдейтов у них, то все становится сильно медленнее. Иерархия объектов сама по себе имеет некий оверхед, а какие-то сущности вовсе не обязательно должны быть GameObject’ами. Когда-то я написал эту статью, которая до сих пор релевантна, если вы хотите узнать, как делать не надо.

И самое важное в контексте процессоров это то, что все компоненты, все данные разбросаны в памяти, что ломает использование кэша процессора.

Теперь хочу быстренько пройтись по новым фичам.

Смысл в том, что у нас есть Entity, которые всего лишь ID неких сущностей в игре — на них хранятся данные в виде компонентов, т.е. Не буду сильно заостряться на том, что такое ECS и как это работает. И системы обрабатывают Entity с определенными компонентами и как-то меняют эти данные. только данные, без кода.

Есть несколько моментов. Зачем мы делаем свою ECS и чем она будет лучше конкурентов? Понятно, что мы не хотим избавляться от GameObject’ов, текущих компонентов Unity, полностью все выкидывать и ставить ECS. Первое, не совсем официально, но мы думаем, что так мы бы делали движок сейчас. Но мы хотим двигаться в сторону лучшего движка.

Не так давно к нам присоединился Майк Актон (если вы в С++ разработке, то знаете, что он один из евангелистов Data-Oriented Programming). Мы делаем расчет на высокую производительность. И мы хотим сделать, чтобы вся система работала максимально быстро — быстрее, чем С++.

Какое-то время назад мы объявили, что делаем новый networking и он тоже базируется на ECS — можно будет делать multiplayer-игру на ECS и шерить код между клиентом и сервером. Также думаем о том, как нативно интегрировать разные вещи в ECS.

Т.е. Работаем над инструментами отладки в Unity. Мы хотим всё упростить. пока что ECS существует как бы отдельно от GameObject’ов и компонентов и это очень неудобно.

Сейчас есть DebugView, который выглядит примерно так:

Здесь вы можете смотреть, какие у вас есть Entity, какие системы сколько времени занимают на обработку, какие системы работают с какими компонентами и у каждого компонента можно посмотреть в инспекторе, какие данные у каждого Entity в компонентах (отмечу, что API часто меняется и многие туториалы могли уже устарели).

Также, если вы слышали о нашей новой разработке Unity for Small Things (это очень маленький runtime, который позволяет делать игры для мессенджеров) — там тоже все строится на ECS.

Последнее время бум разработки и перехода на ECS — это очень популярная технология и всем нужно ее знать.

Кода там чрезвычайно много, поэтому сложно вытащить какой-то вразумительный кусочек, чтобы было что-то понятно. У нас конференция для программистов, поэтому сложно обойтись без слайда с кодом.

По сути, я взял одну систему из примера, которая работает с C# Job System (о чем позже), и мы делаем очень много, чтобы уменьшить количество кода, добавить синтакс-шугар.

И каждый апдейт системы мы создаем Job, запускаем этот Job, он где-то апдейтится, может быть разделяется на несколько групп и выполняется на разных потоках. Здесь есть система, которая работает с компонентами RotationData и ей тоже нужны трансформы GameObject’ов, которые представлены специальной штукой TransformAccessArray.

Так же как и в других реализациях ECS нужно понимать, что думать придется совершенно по-другому (в отличие от GameObject’ов и Transform’ов). Как использовать в проекте? Понятно, что нужно начинать с самого старта проекта, потому что я очень часто получаю вопросы, вроде «мы сделали игру и хотим перейти на ECS — как?». И свыкнутся с этой идеей. В готовой игре это сделать очень сложно.

Мы даем некоторые возможности взаимодействия с GameObject’ами и Transform’ами, но физика, рендеринг и т.д., тут становится все сложнее. Нужно продумывать взаимодействие с Unity, поскольку ECS живет отдельно, в своем маленьком мире. И пока нужно смирится с тем, что многое из привычного интерфейса будет недоступно, но над этим мы тоже работаем.

И сразу же нужно думать о том, что вы будете писать системы в Job System, что гораздо эффективнее.

Мы хотим сделать очень простой способ писать многопоточный код. Пару слов про Job System. Мы ограничиваем возможности языка, которые вы можете использовать в Job'ах, и называем этот subset С# High Performance C#. При этом писать на C#, проверять все за вас, не давать возможность сделать ошибки или показать почему, где и как вы их совершили. У вас в коде Job’ов нет референсов, нет строк, все данные нужно копировать — вы не можете использовать большое количество фич языка, тем самым, становится гораздо сложнее выстрелить себе в ногу многопоточностью.

Такая структура ECS и Job System позволяет добиться очень быстрого выполнения кода. Также мы представляем очень быстрые коллекции и интеграцию с ECS.

При этом мы не только даем вам возможность использовать эти технологии — мы сами работаем с этими системами и делаем новые API для того, чтобы их можно было использовать в Jobs.

Мы работаем над тем, чтобы, используя эти технологии, можно было расширять текущие системы, например, анимацию через Playbles API. Мы сделали Async Raycasts для физики, с помощью которых можно говорить «я хочу 600 рэйкастов, сделай мне когда-нибудь, пожалуйста». И думаем над тем, чтобы сделать новые системы в Unity, которые не будут закрыты в C++, а код которых будет в С# и доступен вам.

Job — это структура, в которой есть метод Execute, где мы выполняем какую-то работу, запустив этот Job. Если брать код Job’а, то он довольно простой. Здесь мы получаем JobHandle, который можем использовать, как зависимость для каких-то других Job’ов. Соответственно, наш внутренний Scheduler когда-нибудь поймет, где ее лучше запустить, разрулит все зависимости.

Хорошо, если вы будете использовать Job’ы с самого начала, но здесь это необязательно. Как использовать в проекте? Если у вас есть какая-то Performance сritical система, допустим, симуляции, pathfinding, networking или что-то еще — вы можете придумать, как ее оптимизировать с помощью этого инструмента.

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

И у вас (наверное) все работает. Дальше вы разбиваете эту работу на параллельные задачи, пишите Job-код и запускаете. Но использование Job System и ECS тоже, как я уже говорил, сильно влияет на то, как вы планируете архитектуру игры. Конечно, нужно протестировать и, главное, протестировать на целевой платформе, в зависимости от количества ядер и т.д.

Burst Compiler — это наша уникальная технология, специальный компилятор этого сабсета C# (High Performance C#) в машинный код текущей платформы, на которую вы паблишите в ваш проект. Дальше все гораздо проще.

И самое крутое, что он не требует никаких действий от вас — если у вас есть Job-код, вы просто добавляете атрибут [BurstCompile], Burst компилирует ваш код, и вы получаете «бесплатную» производительность. Ребята сделали какую-то магию, которую, наверное, никто кроме них не понимает, но эта штука ускоряет код Job’ов раз в 10 раз, что супер-круто. Это наша новая технология и вы можете попробовать ее уже сейчас.

И последнее, о чем я хочу вкратце упомянуть, это Scriptable Render Pipeline (SRP), над котором мы уже очень давно работаем и который призван дать вам возможность писать очень кастомизируемый рендеринг для вашей конкретной игры.

Сейчас у нас есть блэкбокс, который Forward или Deferred — они и так хорошие, мы получаем очень крутую графику на мобилках, на PC, на консолях. Render Pipeline — это некоторый алгоритм, который делает Culling (какие объекты будут рисоваться), Rendering и Post-processing. Используя эту новую фичу, SRP, вы можете написать свой Pipeline, вы можете что-то убрать оттуда, добавить, сделать все, что угодно. Но у них куча ограничений, потому что их нельзя расширять.

Один LWRP, который мы таргетируем на мобилки и слабые девайсы, и HDRP, который мы таргетируем на РС, консоли и над которым работают очень известные люди в индустрии. Сейчас мы работаем над двумя примерами Pipeline’ов. Наверняка, вы видели нашу демку Book of the Dead. До этого они делали AAA-игры.

Здесь мы использовали HDRP, чтобы показать всю мощь этой технологии.

Т.е. Чтобы это использовать, вам тоже нужно будет совершить довольно большое количество героических шагов, потому что с новым Pipeline’ом практически ничего не совместимо из того, что у нас есть сейчас. текстуры скорее всего будут выглядеть иначе. если вы апгрейдитесь с Legacy, то мы даем утилиту, которая апгрейдит большую часть материалов за вас, но вам нужно будет переписать свои шейдеры, т.е.

Если вы хотите что-то сделать на своем Pipeline, пожалуйста, свяжитесь с нами. Очень круто, если вы можете начать с нуля и экспериментировать со своим Pipeline’ом.

Опять же, поймите, что вам нужно, потому что теперь у вас есть больше возможностей что-то сделать, но нужны будут люди, которые смогут это сделать или вы должны будете научиться, как это делать.

На этом все, надеюсь, кто-то пойдет и посмотрит на эти технологии, сделает красивые, крутые игры. По-моему, это круто, потому что те, кто пойдут вперед с нами с этими новыми технологиями, будут более востребованы на рынке.

Вопросы из зала

— Когда можно будет взять себе ECS и разрабатывать?

какой-то AAA-проект. — Вы можете использовать ECS, проблема в том, что в текущем виде она больше таргетирована на людей, которые ориентированы на перфоманс, т.е. Поэтому нужна некая система, надстройка на ECS, которая позволила бы с той же легкостью, как мы сейчас используем MonoBehaviour, использовать и ECS. А задача Unity сделать так, чтобы Performance by Default был доступен всем. А то получится, что мы сделали фичу, которую будет использовать 1% наших пользователей. Пока нет такой надстройки, не думаю, что ECS выйдет в полноценный релиз. Я знаю людей, которые уже используют ECS в продакшене, просто имейте в виду, что эта фича еще глубоко в разработке и сейчас мы решаем вопрос, как сделать максимально удобный интерфейс. А это не задача Unity. Т.е. А следующая задача (не менее сложная) — как сделать какой-то API, который живет поверх этой ECS и позволит использовать его с той же легкостью, как MonoBehaviour. ответа на вопрос «когда именно» пока нет.

А что делать, если у меня мало объектов, но у них разные сущности? — ECS и остальные пункты ориентированы на то, чтобы взять какой-то базовый один GameObject и сделать 150 тысяч его клонов и управлять ими.

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

NET, пробовали сервер на Unity, ничего не выходит. — У нас клиент на Unity, сервер на . Мы купили компанию Multiplay какое-то время назад для того, чтобы сделать качественный хостинг для Unity-игр. Но в тоже время хочется технологии, которые есть в Unity, использовать и на сервере тоже.

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

Еще доклады с Pixonic DevGAMM Talks

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть