Советы по более эффективной работе с базой данных активов

JAVIER ABUD / UNITY TECHNOLOGIESContributor
Oct 6, 2020|10 Мин
Советы по более эффективной работе с базой данных активов
Эта веб-страница была переведена с помощью машинного перевода для вашего удобства. Мы не можем гарантировать точность или надежность переведенного контента. Если у вас есть вопросы о точности переведенного контента, обращайтесь к официальной английской версии веб-страницы.

Asset Import Pipeline v2 - это новый конвейер активов по умолчанию в Unity, заменяющий Asset Import Pipeline v1 с переписанной базой данных активов для быстрой смены платформ. Он закладывает основы для масштабируемого импорта активов с надежным отслеживанием зависимостей. Читайте дальше, чтобы узнать, как работает база данных активов, и откройте для себя несколько советов по экономии времени.

С момента выхода Unity 2019.3 новый конвейер импорта активов стал конвейером по умолчанию для новых проектов. В сочетании со многими другими улучшениями это закладывает основу для более надежного, производительного и масштабируемого конвейера импорта активов. Эта переработка изменила принцип работы папки Library, чтобы поддержать новые рабочие процессы.

Давайте посмотрим под капотом на ряд ситуаций, с которыми Вы можете столкнуться, и на то, как с ними эффективно справиться. Вы узнаете, как обнаружить и устранить некоторые "узкие места", которые стоят Вам времени и производительности проекта.

Советы, которые Вы найдете здесь, относятся к Unity 2019.3 и более поздним версиям. Помните, что если Ваш проект находится не в стадии прототипирования, а в стадии производства, для максимальной стабильности мы рекомендуем Вам использовать последнюю версию Unity с длительной поддержкой (LTS), Unity 2019 LTS.

Изучите время импорта в качестве отправной точки для оптимизации

Во-первых, давайте поговорим о некоторых вещах, которые происходят при создании нового проекта или открытии проекта, в котором еще нет Библиотеки активов.

Если Вы посмотрите на файл Editor.log, Вы заметите множество строк, которые выглядят следующим образом:

  • Начните импортировать ...
  • Импорт завершен ...

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

Вы можете использовать эту информацию для определения определенного типа "узких мест", а именно времени импорта активов.

Просматривая вывод каждой строки, Вы можете извлечь следующие данные:

  • Путь к активам
  • Продолжительность импорта
  • Расширение файла

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

Диаграмма распределения импорта по категориям

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

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

SimpleEditorLogParser

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

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

Это не только ускорит время импорта, но и повысит производительность Вашего конвейера непрерывной интеграции, если Вы делаете чистые ночные сборки или что-то подобное.

Как профилировать время запуска, чтобы выявить узкие места

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

По мере того, как проект становится сложнее и использует больше функций, его открытие занимает больше времени. Это может быть связано с большим количеством факторов, и до Unity 2019.3 не было возможности запустить профилировщик во время работы редактора.

Аргумент командной строки для профилирования при запуске

Среди нескольких аргументов командной строки, которые Вы можете задать при открытии Unity, аргумент командной строки -profiler-enable позволяет Вам профилировать редактор во время запуска.

Эта команда указывает Редактору начать запись данных профилирования для первого кадра приложения, то есть всего кода, который выполняется до появления Редактора. Использование этого аргумента поможет Вам увидеть, что происходит во время запуска, а что занимает время. Вы можете увидеть, является ли это системой в Unity, пакетом Asset Store или кодом, специфичным для Вашего проекта. Это поможет Вам понять, что делать дальше.

Изображение

На этом снимке видно, что открытие этого конкретного проекта занимает ~50 с.

На первый взгляд, загрузка базы данных AssetDatabase занимает 43 с. При дальнейшем рассмотрении становится ясно, что 14 с тратится на вызовы OnPostProcessAllAssets. Далее, код, выполняемый во время RegisterScriptsAndTryLoadingExistingUserAssemblies, добавляет до 10 с, и 5,7 с из них - это загрузка Домена, которая еще больше замедляется из-за вызовов Скриптов, имеющих атрибут [InitializeOnLoad].

Эти данные о запуске помогут Вам отследить узкие места в производительности и понять, где находится код - в Вашем проекте или в самом Unity.

Как найти результат импорта на диске

Папка Library теперь может содержать несколько результатов импорта одного и того же актива, поэтому проекты, использующие новый конвейер импорта активов, больше не имеют "простого" сопоставления GUID (глобального уникального идентификатора) и папки в папке Library.

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

Таким образом, это означает, что найти результаты импорта в папке Library больше не является тривиальной задачей. Вот пример, который показывает, где Вы можете найти результат импорта "Assets/Prefabs/MyPrefab.prefab":

public class LibraryPathsForAsset
{
    [MenuItem("AssetDatabase/OutputLibraryPathsForAsset")]
    public static void OutputLibraryPathsForAsset()
    {
        var assetPath = "Assets/Prefabs/MyPrefab.prefab";

        StringBuilder assetPathInfo = new StringBuilder();

        var guidString = AssetDatabase.AssetPathToGUID(assetPath);
        //The ArtifactKey is needed here as there are plans to
        //allow importing for different platforms without switching
        //platform, thus ArtifactKeys will be parametrized in the future
        var artifactKey = new ArtifactKey(new GUID(guidString));
        var artifactID = AssetDatabaseExperimental.LookupArtifact(artifactKey);

       //Its possible for an Asset to have multiple import results,
       //if, for example, Sub-assets are present, so we need to iterate
        //over all the artifacts paths
        AssetDatabaseExperimental.GetArtifactPaths(artifactID, out var paths);

        assetPathInfo.Append($"Files associated with {assetPath}");
        assetPathInfo.AppendLine();

        foreach (var curVirtualPath in paths)
        {
            //The virtual path redirects somewhere, so we get the
            //actual path on disk (or on the in memory database, accordingly)
            var curPath = Path.GetFullPath(curVirtualPath);
            assetPathInfo.Append("\t" + curPath);
            assetPathInfo.AppendLine();
        }

        Debug.Log("Path info for asset:\n"+assetPathInfo.ToString());
    }
}
Получите примеры GitHub gists

Здесь представлены два gists для разных версий Unity:

Образцы отличаются тем, что по мере развития реализации конвейера импорта активов ряд API был перенесен из пространства имен Experimental в собственное пространство имен AssetDatabase.

Как быстрее находить активы

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

Грубый подход

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

Диаграмма "Время, прошедшее с момента покупки, по отношению к общим активам

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

Изображение

Как видите, если в Вашем проекте сотни тысяч активов, поиск активов может заметно замедлить разработку. При 200 000 активах задержка на простой поисковый запрос составляет уже 200 мс.

Использование метода "грубой силы" приводит к появлению этой распространенной модели использования:

string[] assets = AssetDatabase.FindAssets ("t:texture2D");

if(assets.Length > 0)
{
    string path = AssetDatabase.GUIDToAssetPath (guids[0]);
    if (!string.IsNullOrEmpty (path))
    {
        Texture tex = AssetDatabase.LoadAssetAtPath<Texture>(path);
        //Do something with this texture
    }
}


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

Масштабируемый поиск

База данных AssetDatabase предоставляет возможность поиска пути актива по GUID. Вы можете думать об этом как о поиске чего-либо в словаре по ключу, а не как об итерации массива в поисках совпадения.

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

Как я могу найти GUID?

Вы можете найти GUID, например, в соответствующем .meta-файле актива:

fileFormatVersion: 2
guid: 9fc0d4010bbf28b4594072e72b8655ab
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

В данном случае GUID этого актива - 9fc0d4010bbf28b4594072e72b8655ab.

Учитывая эту информацию, Вы можете сделать следующее:

var path = AssetDatabase.GUIDToAssetPath("9fc0d4010bbf28b4594072e72b8655ab");
var asset = AssetDatabase.LoadAssetAtPath<Texture>(path);

Теперь Ваш актив готов к использованию.

Ускорение поиска с помощью Быстрого поиска и TypeCache

В качестве примечания, если Вы заинтересованы в ускорении поиска в Редакторе, Вам также следует обратить внимание на следующие инструменты:

Пакет быстрого поиска, который позволяет Вам искать в нескольких областях Unity.

TypeCache для поиска сценариев, созданных на основе известного Вам типа.

Что делать, если GUID изменился

Обычно GUID актива является постоянным.

Однако в некоторых ситуациях актив и его .meta-файл дублируются, вызывая конфликт, который база данных AssetDatabase разрешает следующими способами:

Если Вы дублируете папку внутри проекта в другое место в том же проекте

Многократный импорт пакета Asset Store или многократное копирование папки из другого проекта в Ваш проект

Вещи, которые приводят к медленному обновлению

Когда выполняется AssetDatabase.Refresh, множество систем должны работать вместе, чтобы представить Ваш проект в актуальном состоянии. В своем выступлении на конференции Unite Copenhagen я подробно описал различные этапы того, что происходит при вызове Refresh.

Определенные обратные вызовы, выполняемые во время Refresh, взаимодействуют с кодом. Это может повлиять на то, сколько времени потребуется для завершения операции Refresh.

Обратите внимание на обратные вызовы, которые происходят во время перезагрузки домена

Чем больше кода будет выполняться во время перезагрузки домена, тем медленнее будет работать Ваш редактор. Чтобы не сбиться с пути при итерации кода, тщательно продумайте, когда код должен быть выполнен и можно ли отложить его на более поздний срок в проекте.

Во время перезагрузки домена Ваш код будет выполнен, если он содержит любой из следующих методов:

1. Awake

2. OnEnable

3. OnValidate

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

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

Следите за этой темой на форумах Unity, чтобы быть в курсе новостей об улучшениях в этой области.

Заключение

Надеюсь, эти советы были Вам полезны. Сообщите нам, что еще Вы хотели бы узнать о Конвейере импорта активов или о Ваших болевых точках. Мы активно работаем над улучшением конвейера импорта активов, и мы хотим сделать Вашу итерацию максимально приближенной к мгновенной, чтобы Вы могли быть более продуктивными при работе с редактором и изменении активов и/или скриптов.