Оптимизация использования памяти AssetBundle
Загрузка AssetBundles может потреблять память в зависимости от форматов сжатия и шаблонов доступа.
При загрузке AssetBundle Unity выделяет память и под ассеты, содержащиеся в нём, и под его внутренние данные. К основным типам внутренних данных загруженного AssetBundle относятся:
- Загрузка кэша: Хранит последние страницы файла AssetBundle. Используйте
AssetBundle.memoryBudgetKBдля управления его размером. - TypeTrees: Определяет серийную схему объектов.
- Содержание: Перечисляет ассеты в AssetBundle.
- таблица предварительной загрузки: Перечисляет зависимости каждого ассета.
TypeTrees
TypeTree — внутренняя структура данных Unity, описывающая устройство данных внутри сериализованного двоичного объекта. С точки зрения системы сериализации она играет роль схемы объектов Unity.
Каждый серийизованный файл в AssetBundle содержит TypeTree для каждого типа объекта в этом файле. Вы можете использовать информацию TypeTree для десериализации файлов, определения типов которых могли измениться после серийизации (например, при добавлении, удалении или изменении полей).
Когда Unity загружает AssetBundle, он загружает все TypeTrees и хранит их в памяти в течение срока жизни AssetBundle. TypeTree нагрузка памяти зависит от количества уникальных типов объектов, загруженных из AssetBundles, и их сложности. Каждый AssetBundle содержит полный набор TypeTrees для своих объектов. Unity делит идентичные TypeTrees между AssetBundles, чтобы уменьшить использование памяти при загрузке нескольких AssetBundles.
Уменьшить TypeTree памяти
Вы можете уменьшить требования к памяти AssetBundle TypeTrees следующими способами:
- Отключите TypeTrees. Это исключает информацию TypeTree из AssetBundle и уменьшает AssetBundles. Однако, без информации TypeTree, при загрузке более старого AssetBundles с более новой версией Unity или при изменении скрипта в вашем проекте, вы можете получить ошибки сериализации или неопределенное поведение.
- Используйте простые типы данных, чтобы уменьшить сложность TypeTree.
Чтобы проверить влияние TypeTrees на размер AssetBundles, создайте их с и без отключенного TypeTrees и сравните их размеры. Используйте BuildAssetBundleOptions.DisableWriteTypeTree для отключения TypeTrees в AssetBundle.
Примечание: Некоторые платформы требуют TypeTrees и игнорируют настройку DisableWriteTypeTree. Кроме того, не все платформы поддерживают TypeTrees.
Если вы отключаете или удаляете TypeTrees, то обратите внимание на следующее:
- Совместимость: Безопасно лишь снимать TypeTrees когда ты всегда перестраиваешь AssetBundles вместе с сборкой Player для обеспечения совместимости типа. Это общее для AssetBundles распространяются в
StreamingAssetsпапка. - Редактор Загрузка: Ошибка регистрируется, если вы пытаетесь загрузить AssetBundles без TypeTrees в режиме воспроизведения. Это происходит потому, что типы Редактора и Игрока отличаются. Например, MonoBehaviours имеет дополнительные поля в Редакторе.
- Отладка: Unity предоставляет ограниченные инструменты для анализа совместимости без TypeTrees.
TypeTreeHashв манифесте AssetBundle может помочь, а инструментbinary2text(с флагом-typeinfo) показывает необработанные детали TypeTree для сравнения.
СОДЕРЖАНИЕ
Содержание — это сопоставление в AssetBundle, которое можно использовать для поиска каждого явно включенного ассета по имени. Размер данных в содержании увеличивается с количеством ассетов, явно включенных в AssetBundle, и длиной строковых имен, используемых для их сопоставления. Свойство addressableNames представляет имя строки, и если оно не определено, вместо него используется путь к ассету.
Чтобы минимизировать количество памяти, выделяемой для хранения данных содержания, минимизируйте количество AssetBundles, загружаемых в данный момент времени.
Методы GetAllAssetNames и GetAllScenePaths выкладывают карту таблицы содержания.
Таблица предварительной нагрузки
В таблице предварительной загрузки перечислены все объекты, от которых зависит ассет. При загрузке ассета из AssetBundle Unity использует эту таблицу для автоматической загрузки всех необходимых зависимостей.
Каждый ассет имеет свою собственную таблицу предварительной загрузки. Например, таблица предварительной загрузки префаб включает записи для всех компонентов, присоединенных к префаб, материалов и текстур, на которые делается ссылка, и любых других ассетов, используемых префаб.
Каждая запись предварительной загрузки использует 64 бита памяти и может ссылаться на объекты в других AssetBundles.
Когда ассет ссылается на другой ассет, который в свою очередь ссылается на другие ассеты, таблица предварительной загрузки может стать большой, поскольку она содержит дублирующиеся записи, где ассеты имеют общие зависимости. Если два ассета оба ссылаются на третий ассет, то таблицы предварительной загрузки обоих ассетов содержат записи для загрузки третьего ассета.
Таблицы предварительной загрузки могут содержать дублирующиеся записи, где ассеты имеют общие зависимости. Когда ассет ссылается на другой ассет, который в свою очередь ссылается на другие ассеты, Unity хранит эту информацию в таблице предварительной загрузки каждого ассета. Это может повлиять на использование памяти.
Воздействие больших таблиц предварительной загрузки можно уменьшить следующими способами:
- Проекты, которые используют только AssetBundles: Явно добавить любой часто используемый ресурс, имеющий много объектов, в AssetBundle, так что информация о предварительной загрузке этого ресурса будет совместно использоваться.
- Проекты, которые используют Addressables, или Scriptable Build Pipeline: Избегайте прямых или косвенных ссылок из явно включенных ассетов на большие иерархии объектов.
Временная память AssetBundles
Unity эффективно управляет памятью, но временные AssetBundles в памяти создаются в следующих сценариях:
- LZMA-сжатый AssetBundles загружается через
AssetBundle.LoadFromFile,LoadFromMemoryилиLoadFromStreamAPIs. - AssetBundles, которые не используют кэширование и загружаются без версии или хэш-аргумента.
- AssetBundles загружается через
UnityWebRequestAssetBundleна платформах без кэширования AssetBundle, таких как Веб.
Временные файлы существуют до завершения чтения и вызова AssetBundle.Unload. Эти копии в памяти могут значительно увеличить использование RAM и время загрузки.
Соображения, касающиеся кэширования
- LZ4 По умолчанию: Когда
Caching.compressionEnabledявляетсяtrue(по умолчанию), загруженный AssetBundles пересжимается до LZ4 и хранится в кэше. Это преобразование обычно повышает производительность загрузки. - Несжатый: Когда
Caching.compressionEnabledявляетсяfalse, временный AssetBundles хранится несжатым, потенциально увеличивая использование RAM, если первоначальный AssetBundle был сжат.
CRC проверки и эффективность
CRC проверка на наличие файлов LZ4 может повлиять на время загрузки, поскольку весь файл должен быть декомпрессирован. Это использует минимальное количество памяти, поскольку Unity декомпрессирует каждый кусок по отдельности, а не весь файл сразу.
Проверки CRC для LZMA файлов не требуют дополнительных затрат, так как полное декомпрессирование неотъемлемо. Для более подробной информации см. Загрузка AssetBundles.
Рекомендуется выполнять проверку CRC только один раз, во время загрузки, а не при каждой загрузке AssetBundle, особенно на платформах со слабым CPUs.
Подробное использование памяти
Помимо временного в памяти AssetBundles, память потребляют другие структуры:
- LZ4 и несжатые AssetBundles: Когда LZ4на основе AssetBundle открыта, куски распечатываются по требованию. Небольшой кэш (
ArchiveStorageReader.m_CachedBlocks) помогает управлять последовательными чтениями. AssetBundles, этот кэш может по-прежнему использоваться. - Кэш Persistent Manager для
SerializedFile-файлов:PersistentManagerиспользует общий кэш (PooledFileCacherManager) для страниц данных, считываемых изSerializedFilesвнутри пакетов AssetBundle. Его размер по умолчанию составляет 1 МБ. - Архив и метаданные AssetBundle: Дополнительные структуры данных для монтированных Unity архивов и AssetBundles, которые потребляют небольшое количество памяти, такие как объект
AssetBundle, содержащий таблицу содержания, и объектPreloadData. PersistentManager.Remapper: отслеживает связи между экземпляром IDs и исходными файлами. Может использовать значительную память для AssetBundles, содержащего очень большие Префабы (иерархии объектов), так как он никогда не уменьшается в размере после выделения. Объекты в сценах здесь не отслеживаются.- Инстансированные объекты: После загрузки ассетов и сцен из AssetBundles, инстансированные объекты Unity сами будут потреблять память.
Когда Unity загружает весь AssetBundle в память
Следующая информация обобщает использование файлов в памяти и преобразования форматов при загрузке AssetBundles различных форматов с помощью доступного APIs. Это напрямую влияет на потребление памяти во время выполнения.
Файловая загрузка APIs
-
AssetBundle.LoadFromFile, LoadFromFileAsync
- Сжатие LZMA: Преобразование в LZ4 и открытие файла в памяти.
- LZ4 сжатие и несжатие: Чтение содержимого непосредственно из файла.
-
AssetBundle.LoadFromMemory, LoadFromMemoryAsync
- Сжатие LZMA и несжатие: Преобразование в LZ4 и открытие файла в памяти.
- LZ4 сжатие: Чтение содержимого непосредственно из памяти.
-
AssetBundle.LoadFromStream, LoadFromStreamAsync
- Сжатие LZMA: преобразование в LZ4 и открытие файла в памяти
- LZ4 сжатие и несжатие: Чтение содержимого непосредственно из потока файлов.
Веб-загрузка APIs
-
UnityWebRequestAssetBundle.GetAssetBundle (Очистить Кэш,
Caching.compressionEnabled = true)- LZMA сжатие: Скачать и поток кэш-файл (преобразование LZMA в LZ4), затем загрузить из кэша
- LZ4 сжатие: скачать и поток в файл кэша (без преобразования), затем загрузить из кэша
- Несжатый: Загрузка и поток в файл кэша (преобразование в LZ4), затем загрузить из кэша
-
UnityWebRequestAssetBundle.GetAssetBundle (Схема,
Caching.compressionEnabled = true)- Все форматы сжатия: чтение содержимого непосредственно из кэша LZ4 файла
-
UnityWebRequestAssetBundle.GetAssetBundle (без кэширования)
- Сжатие LZMA: загрузить, конвертировать в LZ4 и открыть файл в памяти
- LZ4 сжатие и несжатое: Загрузка и поток в памяти на основе временный файл (без преобразования)
Примечание: Преобразование и открытие файлов в памяти включает открытие исходного кода, проверку его формата, преобразование его в LZ4 (или несжатый, если Caching.compressionEnabled является false) в качестве файла архива в памяти, открытие этого файла в памяти и, наконец, удаление его после разгрузки и отсутствия других читателей. Этот процесс может быть неэффективным использованием памяти и времени загрузки.
Сокращение использования памяти во время выполнения в AssetBundles со многими ассетами
При загрузке ресурсов Unity поддерживает загрузку по полному относительному пути проекта, по имени файла или по имени файла без расширения. Последние два варианта реализуются путем создания дополнительных строковых таблиц. В AssetBundles с большим количеством загружаемых ресурсов эти дополнительные строковые таблицы могут потреблять значительную часть памяти.
Чтобы уменьшить эти накладные расходы, рекомендуется всегда загружать ассеты с использованием их точного ключа (пути относительно проекта или имени Addressables) и отключать дополнительные возможности сопоставления во время сборки:
BuildAssetBundleOptions.DisableLoadAssetByFileNameBuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension
Дополнительные сведения см. в документации по параметру name в AssetBundle.LoadAssetAsync.