Version: 2019.4
Профилирование
Practical guide to optimization for mobiles

Оптимизации

Just like on PCs, mobile platforms like iOS and Android have devices of various levels of performance. You can easily find a phone that’s 10x more powerful for rendering than some other phone. Quite easy way of scaling:

  1. Убедитесь, что он хорошо работает на базовой конфигурации
  2. Используйте больше “красивостей” для высокопроизводительных устройств:
    • Resolution
    • Post-processing
    • MSAA
    • Anisotropy
    • Shaders
    • Fx/particles density, on/off

Фокус на GPU

Графическая производительность связана с филлрейтом, пиксельной и геометрической сложностью (количеством вершин). Их можно уменьшить. Здесь может помочь Occlusion culling, т.к. Unity не будет отображать объекты, не входящие в область обзора.

На мобильных, по сути, связь скорости заполнения (скорость заполнения = пиксели экрана * сложность шейдера * овердрафт) и более сложных шейдеров является наиболее распространенной причиной проблем. Поэтому рекомендуется использовать мобильные шейдеры, поставляемые с Unity или писать свои, как можно более простые. Если это возможно, заменяйте пиксельные шейдеры на вершинные.

If reducing the Texture Quality in Quality settings makes the game run faster, you are probably limited by memory bandwidth. So compress textures, use mipmaps, reduce texture size, etc.

LOD (Level of Detail) - make objects simpler or eliminate them completely as they move further away.

Хорошая практика

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

Unity оптимизирован под OpenGL ES 2.0, используется шейдерный язык GLSL ES(схожий с HLSL). Чаще всего шейдеры пишутся в HLSL (известен также как Cg). Это перекрестие, сделанное в GLSL ES для мобильных платформ. Также вы можете писать на GLSL, если хотите, но при этом придерживайтесь OpenGL платформ (mobile + Mac). При использовании float/half/fixed типов в HLSL, они в конечном итоге будут highp/mediump/lowp точными определителями в GLSL ES.

Вот контрольный список для хороший практики:

  1. Количество материалов должно быть как можно более низким. Это делает батчинг для Unity более легким.
  2. Используйте атласы текстур (большие изображения, содержащие более маленькие) вместо большого количества отдельных текстур. Это сделает загрузку более быстрой.
  3. Используйте Renderer.sharedMaterial вместо Renderer.material если используете атласы текстур и общие материалы.
  4. Рендер пиксельного освещения дорог.
    • Use light mapping instead of realtime lights where ever possible.
    • Adjust pixel light count in Quality settings. Essentially only the directional light should be per pixel, everything else - per vertex. Certainly this depends on the game.
  5. Experiment with Render Mode of Lights in the Quality settings to get the correct priority.
  6. Избегайте cutout шейдеров (альфа тест), если это действительно необходимо.
  7. Сведите к минимуму прозрачную (альфа смешивание) часть экрана.
  8. Постарайтесь избежать ситуаций, когда несколько источников света освещают какой либо объект.
  9. Попробуйте уменьшить общее количество шейдерных проходов (тени, пиксельное освещение, отражения).
  10. Критический порядок визуализации. В общем случае:
    • fully opaque objects roughly front-to-back.
    • alpha tested objects roughly front-to-back.
    • skybox.
    • alpha blended objects (back to front if needed).
  11. Пост обработка дорога для мобильных, используйте аккуратно.
  12. Партиклы: уменьшите овердрафт, используйте как можно более простые шейдеры.
  13. Двойной буфер для мешей, умножаемый каждый кадр:
void Update (){
  // flip between meshes
  bufferMesh = on ? meshA : meshB;
  on = !on;
  bufferMesh.vertices = vertices; // modification to mesh
  meshFilter.sharedMesh = bufferMesh;
}

Shader optimizations

Проверка границ филлрейта (fillrate) проста: если вы уменьшите разрешение, игра будет идти быстрее? Если да, то ваш филлрейт ограничен.

Попробуйте уменьшить сложность шейдеров с помощью следующих методов:

  • Избегайте шейдеров с альфа-тестом, вместо этого используйте альфа-смешанные версии.
  • Используйте простой, оптимизированный код шейдеров (например, как у шейдеров “Mobile”, которые поставляются с Unity).
  • Избегайте дорогих математических функций в коде шейдеров (pow, exp, log, cos, sin, tan и т.д.). Вместо этого постарайтесь использовать заранее вычисленные текстуры.
  • Для лучшей производительности, сделайте количество точных рассчетов (float, half, fixedin Cg) как можно более низким.

Фокус на CPU

Часто бывает, что обработка пикселей в игре ограничивается процессором. Таким образом, в конечном итоге остаются неиспользуемые мощности, особенно на многоядерных процессорах. Таким образом, часто целесообразно перекладывать следующие функции с GPU на CPU (Unity их поддерживает): mesh skinning, батчинг маленьких объектов, обновления геометрии частиц.

Это следует делать с осторожностью. Если вы не связаны по графическим вызовам (draw calls), то батчинг на самом деле помешает производительности. Он сделает culling менее эффективным и сделает многие объекты зависимыми от освещения.

Хорошая практика

  • FindObjectsOfType (и вцелом геттерные функции Unity) очень медленные, поэтому используйте их с умом.
  • У неподвижных объектов активируйте свойство Static. Это позволит им участвовать во внутренних оптимизациях (таких как статический батчинг).
  • Произведите множество циклов процессора для обеспечения лучшей сортировки occlusion culling (отсечение невидимых поверхностей).

Физика

Физика может сильно нагрузить процессор. Можно проследить это с помощью профайлера редактора. Если физика сильно нагружает процессор:

  • Настройте Time.fixedDeltaTime (в Project settings -> Time) так, чтобы он был как можно более высоким. Если ваша игра с медленным движением, то, вероятно, вам понадобится меньше фиксированных обновлений, чем игре с быстрым движением. Быстрый темп игры нуждается в более частых расчетах, поэтому, чтобы не было сбоев с коллизиями, fixedDeltaTime должен быть ниже.
  • Physics.solverIterationCount (Physics settings).
  • Используйте как можно меньше объектов типа Cloth.
  • Rigidbodies используйте только там, где это необходимо.
  • Вместо меш коллайдеров старайтесь использовать примитивные коллайдеры.
  • Никогда не двигайте статический коллайдер (т.е. коллайдер без Rigidbody), так как это сильно скажется на производительности. В профайлере это отобразится как “Static Collider.Move”, но на самом деле будет обрабатываться в Physics.Simulate. Если понадобится, добавьте RigidBody и установите isKinematic в true.
  • В Windows вы можете использовать NVidia’s AgPerfMon набор инструментов для профилирования, чтобы получить больше необходимых деталей.

Android

GPU

These are the popular mobile architectures. This is both different hardware vendors than in PC/console space, and very different GPU architectures than the “usual” GPUs.

  • ImgTec PowerVR SGX - основанный на тайлах: визуализирует все в маленьких тайлах (16x16), тени только на видимых пикселях
  • NVIDIA Tegra - классический: визуализирует все
  • Qualcomm Adreno - тайловый: визуализирует все в тайле, поддерживает большие тайлы (256к). Adreno 3xx может быть переключен в традиционный.
  • ARM Mali - тайловый: визуализирует все в тайле, поддерживает маленькие тайлы(16x16)

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

Используйте специфичное для платформы сжатие текстур.

Что еще почитать

Разрешение экрана

Версия android

iOS

GPU

Only PowerVR architecture (tile based deferred) to be concerned about.

  • ImgTec PowerVR SGX. Tile based, deferred: render everything in tiles, shade only visible pixels.

Это означает:

  • Карты освещения уже не так необходимы.
  • Antialiasing and aniso are cheap enough, not needed on iPad 3 in some cases.

И минусы:

  • Если вершинные данные каждого кадра (количество вершин * хранение требуется после вершинных шейдеров) превышает внутренние буферы, выделенные драйвером, то для лучшей производительности сцена должна быть разделена. После этого драйвер должен выделить буфер большего размера, или же вам нужно будет снизить количество вершин. Это становится видно на iPad2 (iOS 4.3) на отметке 100 000 вершин с довольно сложными шейдерами.
  • TBDR нуждается в большем количестве транзисторов, выделяемых на тайлинг и отложенные части, оставляя концептуально меньше транзисторов для “чистой производительности”. Очень трудно (практически невозможно) выделить в GPU время на отрисовку вызова в TBDR, что делает профилирование сложным.

Что еще почитать

Разрешение экрана

iOS версия

Динамические объекты

Asset Бандлы

  • Asset Бандлы кешируются на устройстве до определенного предела
  • создаем используя Editor API
  • Загружаем используя WWW API: WWW.LoadFromCacheOrDownload или как ресурс: AssetBundle.CreateFromMemory или AssetBundle.CreateFromFile
  • Выгружаем используя AssetBundle.Unload. Там есть опция для выгрузки бандла, но перед этим сохраните загруженный ассет. Также можно удалить все загруженные ассеты, даже если на них есть ссылки в сцене
  • Resources.UnloadUnusedAssets выгружает все ассеты, на которые больше нет ссылок в сцене. Не забывайте убивать ссылки на ассеты, если они вам не нужны. Публичные и статические переменные не убираются сборщиком мусора.
  • Resources.UnloadAsset выгружают специфические ассеты из памяти. Если нужно, они могут быть повторно загружены с диска.

Если предел на количество одновременно загружаемых ассет бандлов на iOS? (т.е. можем ли мы безопасно загрузить более 10 ассет бандлов одновременно (или каждый кадр)?)

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

Ресурсы

  • Ассеты должны быть признаны Unity, быть размещенными в билде.
  • Добавьте к файлу .bytes если вам нужно чтобы Unity распознавал его как файл с двоичными данными.
  • К текстовым файлам добавьте .txt, в этом случае Unity будет его распознавать как текстовый ассет
  • Ресурсы преобразуются в формат платформы во время сборки.
  • Resources.Load()

Issue checklist

  • Текстуры без надлежащего сжатия
  • Разные решения для различных случаев, не забудьте сжать текстуры если вам это нужно.
  • ETC/RGBA16 - по умолчанию для android, но вы можете настроить его в зависимости от GPU. Лучший подход - использовать ETC везде, где это возможно. Альфа текстуры могут использовать два ETC файла с одним каналом для альфы.
  • PVRTC - по умолчанию для iOS, хорош в большинстве случаев
  • Текстуры, имеющие включенные Get/Set пиксели удваивают размер, деактивируйте Get/Set, если вам это нужно
  • Текстуры, загруженные в JPEG/PNG во время исполнения будут несжаты
  • Большие mp3 файлы помечайте как decompress on load
  • Добавьте загрузку сцены
  • Неиспользуемые ассеты остаются неочищенными в памяти.
  • Если есть случайные сбои, попробуйте запустить на devkit или на устройстве с 2 GB памяти (например, на Ipad 3).

Иногда ничего нет в консоли, просто случайный сбой

  • Fast script call и stripping могут привести к случайным сбоям на iOS. Попробуйте без них.
Профилирование
Practical guide to optimization for mobiles