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


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

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



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

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

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

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


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



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

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



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



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

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



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

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



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

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



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



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



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

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



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

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



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

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

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

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



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

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

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

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



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

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

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



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

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



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

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

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



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



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

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



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

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

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



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



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



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



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


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

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



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

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



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

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


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

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

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

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

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

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

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


Комментарии (11)


  1. poxvuibr
    23.10.2018 14:07

    Пара слов о технологиях будущего. Если посмотреть выступление упомянутого тут Майка Актона за 2014 год, можно выяснить, что речь идёт о технологиях отностительно далёкого прошлого. То есть Майк так и говорит, что ничего нового тут нет, такие дела. Это сильно перекликается с дискуссией про Electron в соседнем топике )).


  1. KHH
    23.10.2018 16:01

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

    В своё время не нашёл как добавить компонент к сущности или сущность в рантайме. Добавить получилось, но вот фильтры не видели изменений(в системах достучаться не получилось). Так как всё кешировалось до запуска ECS. Эту проблему исправили? Или, же, я что то недопонял и не дочитал?

    Второй момент, из коробки есть рендер 3д объектов, но нет спрайт рендера. На форуме можно найти решение, но нужно порыться в инете.

    Третий момент, физика которую нужно реализовывать самостоятельно.

    Т.е. не хватает примеров использования ECS в открытом доступе.


  1. mynameco
    23.10.2018 16:58
    +1

    Непонятно почему анимичную модель они назвали компонетной системой.


    1. poxvuibr
      23.10.2018 18:34

      Потому что тут речь о том, как данные лягут в кеш, а не о том, как удобнее строить модель.


    1. xorx
      23.10.2018 18:36

      Потому что это разные вещи. Анимичная модель может включать ECS, но всякая анимичная модель — ECS


  1. Nagg
    23.10.2018 20:38

    Ребята сделали какую-то магию, которую, наверное, никто кроме них не понимает

    Огромный набор ограничений джобов (аля нет ГЦ) позволил генерить простой LLVM IR, который дружествен ко всем возможным оптимизациям ллвм-а ;-)


  1. anz
    23.10.2018 22:37

    DOD и Jobs и правда классные подходы. ECS тоже, если знать где применить. Но реализация Unity, честно говоря, смущает. Если присмотреться поближе, нельзя сказать что это все тот же C#, хотя в презентации говорится «but it's still C#». Все преимущество C# в удобстве и скорости написания кода, а это GC, linq, коллекции, безопасность и все такое. Именно это становится недоступно в угоду производительности. То есть вместо подходящего инструмента, С++, выдается урезанный по всем фронтам higt performance C#, без GC, коллекций и всего остального. Почему они говорят это будет просто? Почему просто не дать нативное API? И почему я должен поверить что Burst compiler «сделает магию»? Да еще и превзойдет С++? То есть дефолтный юнити-программист напишет код, который скомпилируется компилятором который вот-вот появился на свет (за исключением llvm, конечно), и это заработает эффективнее, чем то же самое сделает дефолтный С++ программист на компиляторе с десятками лет оптимизаций?


  1. metamorphling
    24.10.2018 08:41

    Работая с Unity и балуясь с Unreal возникает ощущение, что первые играют в догонялки со вторыми. Какое-то чудовищное количество решений переделываются на ходу. Shadergraph и SRP, ecs и кастомный # компилятор урезанный до низкого уровня(но всё ещё #, зачем-то), сливание ассет бандлов в сторону addressable assets. Я уже молчу про совсем трешовые эксперименты вроде ml-agents, он то в кому впадает, то снова обрастает коммитами. Вроде как на всё это переходить не вынуждают, но силы компании разбрасываются, где-то жертвы да и вылезут. От сомнительных старых решений к сомнительным новым, куда «мы думаем, что так мы бы делали движок сейчас» заведёт — не понятно. Чтобы не быть совсем голословным критиком, из видимых решений: сесть да сделать несколько игр самим, как ребята из Unreal. Тогда увидят чего не хватает в реальных боевых условиях, где и что можно улучшить. То что спустя столько лет развития движка приходится в команду докупать amplify shader, зато иметь burst compiler, мне кажется не очень здоровой ситуацией.


    1. Desu0x
      24.10.2018 11:09

      Поддерживаю. Прогресс есть, но очень медленный. Ребята вкладываются в нишевые решения. Это конечно хорошо, но как много разработчиков будет пользоваться ml-agents или JobSystem? Думаю что единицы. Для таких решений и нужен AssetStore.
      Долгое время более практические проблемы, с которыми сталкивался среднестатистический разработчик в вакууме, не решались. Сколько в Unity не было нормального UI или поддержки IAP из коробки? А как давно решили старую проблему работы с текстом покупкой TextMeshPro?


      1. elmm
        24.10.2018 11:44

        По докладу в Берлине сложилось впечатление, что ml-agents это совсем рисёч проект, который делается небольшой командой без ясной цели для чего оно нужно, просто чтоб направление машинного обучения пощупать — вдруг что-то полезное получится.
        А так грех жаловаться, на фоне того, как они долго пилили всеми ожидаемые фичи — обновление на свежую версию дотнета, новую систему префабов.
        Может на юнити снизойдёт и они купят amplify shader — примеров подобных их поступков хватает.