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

О том, как мы делим качества и какие инструменты для этого используем в 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
5151
10 комментариев

Совершенно бесполезная статья.
По сути все что вы рассказали это: " У нас есть классный редактор для пресетов графики"
и "У нас есть классный редактор для назначения этих пресетов на группы устройств".

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

18
Ответить

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

Сперва у нас образовался прототип HD качества, которое работало на 60 FPS на iPhone 7. Затем мы подобрали примерно аналогичный телефон на Android — остановились на Adreno 530. И начали итерировать. Пока пилили разные пресеты, проверяли их работоспособность на таргет-девайсах с помощью профайлеров – нативных Arm Studio, XCode. XCode в этом плане уникален и дает очень подробные метрики. По оптимизации можно почитать другие наши статьи.

В итоге Adreno 530 уехал в LD пресет, потому что в какой-то момент мы поняли, что не хотим жертвовать качеством картинки ради него. Далее мы делали очень много замеров на публичных тестах. Туда к нам приходят тысячи пользователей. Финально список устройств формировался по результатам этих публичных тестов + наших внутренних данных профилирования.

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

7
Ответить

Классно, здорово, но донатно.

2
Ответить

Ух, крутяк! Спасибо огромное за статью!

1
Ответить

Спасибо за подробный материал! Надо изучать, а пока простой юзерский вопрос: для каких вообще iOS-устройств доступен пресет «Высокий»?

Ответить

HD-пресет пока на проде не поддерживается. Внутренние билды неплохо играются на девайсах с А10 и выше, однако на релизе, возможно, запустим на чуть более мощных устройствах.

На iOS устройствах в целом производительность гораздо выше. В случае HD мы ограничены малым запасом по RAM.

1
Ответить

Комментарий недоступен

Ответить