Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

О том, как мы делим качества и какие инструменты для этого используем в War Robots Remastered.

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Релиз War Robots состоялся еще в 2014 году, и за 7 лет существования проекта графическая часть в нем постоянно развивалась. Но в то же время команда постоянно сталкивалась с ограничениями из-за минимальных требований к девайсам. Оперируя таким большим проектом, у которого немало устройств входит в low-end сегмент, нельзя просто взять и запилить крутой современный графен, не потеряв при этом часть аудитории.

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Так у нас появилась задача: сделать всем красиво и хорошо. Поэтому мы решили делать War Robots Remastered — с блэк-джеком, обновлением графического пайплайна и разделением ассетов на разные качества.

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

На тот момент билд War Robots под Android со всеми ресурсам весил порядка 700 МБ и включал сотни единиц контента. У нас было 13 карт, 81 мех, более ста пушек, десяток дронов и еще куча всякой мелочи. Не то, чтобы все это было категорически необходимо в проекте, но если уж начал пилить контент, то иди в своем увлечении до конца.

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

У нас уже был менеджер качества. Он состоял из динамических пресетов, которые могли переключать определенные параметры — например, лодирование или дальность отрисовки — в зависимости от текущих показателей FPS. Это была длиннющая портянка с настройками и пресетами, почти полностью завязанная на логику «Роботов», и каждое поле в настройке рисовалось кодом. Выглядело это монструозно, так как из-за большого количества настроек число фолдаутов в инспекторе достигало более 9000.

Динамический скейлинг параметров игры давал свои преимущества, но у него также было и несколько минусов, из-за которых нам пришлось от него отказаться. В первую очередь — из-за количества пресетов. Со временем из-за разной архитектуры поддерживаемых устройств количество пресетов качества накапливалось, пока не достигло 15 штук. В реальности мало кто обращал на это внимание и, конечно, никто не тестировал все 15 пресетов на каждом устройстве в продакшене. К тому же, поддерживать такое количество пресетов довольно сложно и с точки зрения разработки: никогда не знаешь, когда старый Quality Manager возьмет и переключит параметры внутри текущего качества — и какие именно. Вдобавок, когда мы начали работу над новым кастомным рендер-движком и на основе него запрофилировали все 15 качеств, оказалось, что разброс по производительности между ними не превышает 15-20%.

Все это нас не устраивало, так что мы решили изменить подход к формированию пресетов и запилить новый Quality Manager. А задачу эту передали нашему отделу кросс-проектной разработки, именуемому Platform Team.

Теперь поговорим о том, что же такое новый Quality Manager в War Robots, какие задачи он решает и как устроен.

Так что это за менеджер качества такой

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

Главными сущностями у нас являются:

  • QualitySetting — набор параметров, объединенных в общую группу;
  • Пресет, состоящий из выбранных уровней QualitySetting разного типа.

Quality Manager состоит из двух частей: runtime-часть с API для инициализации и переключения качеств и editor-часть с GUI, которые позволяют все это конфигурировать.

Editor

Начнем с editor-части. Ее задача — предоставление интерфейса для конфигурации настроек качества и пресетов и минимизация количества кода, необходимого со стороны клиента. В идеале мы хотели заставить клиентщиков описывать только структуры настроек качества, а всю работу по отрисовке оставить на стороне Quality Manager.

Вот так выглядит наше окно, разделенное на три вкладки: настройки качества, пресеты, группы устройств:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Давайте подробнее разберемся, как с этим работать, и начнем с вкладки Quality Settings:

Схема работы следующая: клиентские разработчики создают класс с набором полей и настраивают уровни качества для него в окне Quality Manager. Он, в свою очередь, «из коробки» умеет отрисовывать примитивные типы, Enum, Nullable, массивы, списки, интерфейсы и собственные классы c полями вышеперечисленных типов, включая другие классы. На случай, когда необходимо отрисовывать для поля кастомный GUI, в QM предусмотрена возможность помечать поля атрибутом, в классе которого реализована отрисовка этого поля.

В QM сразу включен ряд реализаций для кастомной отрисовки полей: IntSliderView, FloatSliderView, IntPopupView, PresetIndexView, PresetNameView, QualitySettingIndexView, QualitySettingNameView. Этого скромного набора нам хватило для интеграции QM в War Robots и переезда со старого QM на новый. Кода в проекте стало ощутимо меньше, а тот, который остался, стал заметно проще, понятнее и описывал именно то, что он и должен был описывать — данные и логику работы с ними.

У нас уже есть классы настроек качества и данные для их уровней, так что пора формировать из них пресеты. Для этого отправляемся на первую вкладку окна QM — Presets.

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Тут все достаточно тривиально: мы заводим необходимое количество пресетов, задаем им имена и выставляем для них уровни настроек качества. Готово.

Теперь, когда у нас есть пресеты, осталось задать параметры, по которым будет определяться, какой пресет использовать на девайсе.

Третья вкладка — Device Groups.

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

На этой вкладке мы формируем группы девайсов. Для этого мы используем несколько параметров: объем ОП, частоту процессора, модель GPU и модель девайса для совсем точного попадания. Все параметры не являются обязательными, и можно для группы указать только часть из них. Так, например, для устройств на iOS самый простой вариант — составить карту по модели устройств. На Android же большую часть покроют группы, объеденные по популярным моделям GPU, а в остальных случаях можно указать минимальные требования по объему оперативной памяти и частоте процессора.

Для группы — помимо пресета, который будет выбран по умолчанию — мы также задаем список доступных этой группе устройств пресетов для того, чтобы пользователь на low-end девайсе не смог поменять настройки на ultra high, что может привести к крешам по OOM на старте приложения и блокировать тем самым возможность изменить настройки обратно.

Итак, конфиг QM готов. Сериализуется он в JSON, что дает возможность его легко читать и править без окна QM, а также доставлять на клиент с сервера.

Помимо описанного функционала, QM позволяет:

  • работать с несколькими конфигурациями;
  • добавлять к пресетам кастомные данные, не относящиеся к уровню настроек качеств (мало ли);
  • конфигурировать базовые настройки, не относящиеся к пресетам (раздел Custom Data).

Runtime

API рантайм-части достаточно простой и включает основные методы для работы с QM:

  • инициализация (в том числе и обновление текущего инстанса QM из нового конфига);
  • выбор пресета (по индексу, имени, объекту пресета из конфига);
  • выбор уровня настройки качества (по индексу, имени, объекту настройки качества из конфига);
  • сброс пресета на дефолтное значение;
  • сохранение/удаление данных о выбранном пресете и уровне настроек качества (стейта).

Так инициализируется инстанс QM:

var exampleStateController = new ExampleStateController(); var qualityManager = QualityManager.Initialize(exampleStateController);

Помимо основных методов, инстанс содержит ивенты о смене пресета и уровня настройки качества. Логика применения настроек качества лежит на клиентской стороне — для этого у класса QualitySetting есть виртуальный метод Apply, который вызывается при смене уровня настройки качества.

Помимо базовых методов API, в QM есть набор методов для «мягкого» переключения уровней качества. В случае, когда в рантайме мы сталкиваемся с ситуацией, при которой девайс должен тянуть заданный ему пресет, но при этом происходит падение FPS из-за посторонних факторов, можно ослабить нагрузку, понизив одну из настроек качества, или наоборот. Для этого в QM есть отдельный тип настроек качества, у которого есть виртуальный метод CanBeSwitchedTo, позволяющий определить, можно ли сменить текущий уровень настройки на новый. Так мы можем в рантайме поэтапно даунскейлить качество, чтобы стабилизировать FPS, или наоборот — попробовать дать девайсу нарисовать лучшую картинку, пока не начнем ловить падение FPS.

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

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

При смене пресета/уровня настройки качества QM сохраняет стейт и при последующей инициализации использует уже его. По умолчанию данные сохраняются в PrefsManager, и сохраненный стейт используется до тех пор, пока не будет изменена версия конфига QM. При необходимости можно реализовать свой вариант IStateController и использовать эту реализацию при инициализации QM, чтобы определить, когда можно использовать стейт от старой версии конфига, а когда смена конфига должна приводить к сбросу пресета на дефолтный.

Конфиг для QM лежит в папке ресурсов и по умолчанию грузится из билда. Дополнительно к этому мы предусмотрели механизм удаленного обновления конфига с CDN. Для этого файл конфигурации выкладывается на CDN, а на клиент приходит ссылка на него с указанием хеша конфига. По хешу система определяет, нужно ли ей обновить конфиг перед инициализацией или на девайсе уже есть актуальная версия конфига. Так мы в любой момент при необходимости можем поменять настройки QM у всех пользователей.

С QM разобрались. Давайте теперь более предметно рассмотрим, как мы сформировали список настроек качества, пресетов и групп устройств на проекте War Robots.

Как все это работает в игре

Мы решили зафиксировать несколько четко установленных пресетов, а динамики достигать так же, как и в консольных играх, — за счет скейлинга картинки. Современные девайсы обладают экранами с высоким разрешением, однако GPU в них стоят, конечно, далеко не RTX 3090, так что было бы наивно полагать, что они будут справляться с 60 FPS в нативных Quad HD или даже 4k. Собственно, мы сразу ограничили плотность пикселей сверху, проитерировавшись до значения в 350 ppi.

Изначально при работе над ремастером мы фокусировались на двух качествах — HD (high definition) и LD (low definition). Весь контент, который мы переделывали, основывался на них, и все инструменты по автоматической генерации исходили тоже из них. Однако вскоре мы поняли, что нам понадобится дополнительный уровень качества, который будет нацелен на устройства с небольшим, по нашим меркам, объемом RAM. Так родился еще один пресет качества — ULD (ultra low definition).

Так выглядит игра в качестве HD:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Так — в LD:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

А так — в ULD:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Каждый пресет мы разграничили не только по качеству, но и по максимально возможному FPS. Сейчас мы позволяем выбирать некоторым устройствам 60 FPS, что достигается благодаря отдельной настройке внутри нового QM:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Как мы видим, каждый пресет имеет настройку TargetFPSQualitySettings и внутри нее два уровня, отвечающие за максимально возможный FPS на устройстве. Затем «глобальные» пресеты качеств также разделяются: например, есть качество LD, а есть LD60. Это значит, что пользователи, устройства которых попадают в группу LD60 (по названию устройства — на iOS или по GPU — на Android), получают возможность в настройках включить 60 FPS:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Какое-то время мы рассуждали, нужно ли включать пользователям 60 FPS по умолчанию, но пришли к тому, что не стоит: это значительно повысит использование батареи мобильного устройства, а по нашим внутренним данным на разных проектах на такую частоту кадров переключаются 5-15% аудитории (у которой эта настройка вообще доступна).

Также внутри QM содержится важнейший параметр — с каким «тэгом» ресурсной системы работать:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Так, для ULD качества используется тег LD_ULD, который содержит набор ресурсов, упакованных нашей ресурсной системой для этих качеств. Объединение этих двух качеств дало нам большую экономию на дубликатах ресурсов, которые складываются в Asset Bundles.

Таким вот образом «собирается» каждое качество: это всего лишь набор уровней настроек.

Для того, чтобы применить настройки в рантайме, используется система наследования классов QM. Разберем пример применения настроек рендера:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

Как пример, для ULD настройки рендера используется MasterTextureLimit = 1. Давайте посмотрим, как мы можем применить его к нашей игре.

Каждая настройка должна переопределять абстрактный класс WRQualitySetting, что и делает наш пользовательский класс RenderingQualitySetting. Благодаря этому он может перегружать метод Apply. Собственно, при вызове этого метода мы можем делать что угодно. В этот момент мы знаем, что наши ресурсы загружены, а рендер-пайплайн уже готов к работе.

Вызов метода Apply происходит в двух случаях:

  • инициализация игры — в этот момент мы поднимаем с диска пресет качества, который использует клиент, и применяем его;
  • переключение качества в настройках игры — в этом случае после ряда проверок контроллер окна вызывает незамысловатый код:
private void OnConfirmPopupButtonClick() { var supportedPresetData = _supportedPresetsData.Find(x => x.IndexInUiPresetsLists == _selectedPresetIndex.Value); AnalyticsUtils.QualityPresetChanged(supportedPresetData.QmPreset.Name); ApplicationContext.QualityService.QualityManager.SetCurrentPreset(supportedPresetData.QmPreset); // ... reload game … // }

Вызов API QualityManager ApplicationContext.QualityService.QualityManager.SetCurrentPreset вызовет, в свою очередь, череду изменений внутри конфигураций и в конце переключит и применит все настройки, которые были зарегистрированы в QM.

Наверное, один из самых важных этапов, который был произведен перед переходом на новый QM — это чистка старых параметров качества.

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

Мы смогли выделить основные необходимые для нас параметры, которыми хотелось управлять, а также разделили их на условные группы:

Менеджер качества, или как не спалить лоу-энд девайсы ультра-графикой

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

На практике довольно сложно законфигурировать все устройства идеально, и бывают ситуации, когда на разных устройствах с одинаковым, казалось бы, SoC, игра ведет себя совершенно по-разному. Могут быть некачественные детали (например, дешевая память с низкими характеристиками) или некорректно написанные драйверы. В этом случае девайс всегда можно выделить отдельно и подобрать настройки под него. При создании QM мы учли подобные случаи, и мы можем производить конфигурацию пресетов не только per-GPU но и per-device (это активно используется, например, на Apple-устройствах).

Отдельно стоит отметить, что у нас имеется возможность как хранить манифест QM внутри игры, так и на CDN, и доставлять его в клиент динамически на старте. Определение наиболее актуального происходит простым определением наличия ссылки на нашем мета сервере. Если она есть, то конфиг всегда берется с сервера. Также на мета-сервере имеется возможность разделять конфиги по версиям клиента, поскольку у нас бесшовные обновления, и несколько версий клиента живут в проде одновременно.

Подводя итоги: так в чем преимущества нового менеджера качеств?

Новый Quality Manager дал нам довольно большие возможности:

  • мощность управления конфигурациями из старой системы;
  • простота тестирования;
  • возможность менять параметры буквально на лету через сервер;
  • упрощение разработки графического пайплайна.

Также QM удобным образом позволил нам разделить настройки качества и выдать хорошую графику на телефонах, которые ее поддерживают, при этом сохранив приближенную к старой на слабеньких устройствах с таким же FPS, как в оригинальной игре.

Дмитрий Самсонов
Senior Platform Developer
Павел Зинов
Head of Client Department
51
9 комментариев