Conseils pour travailler plus efficacement avec la base de données des actifs

Le pipeline d'importation d'actifs v2 est le nouveau pipeline d'actifs par défaut dans Unity, remplaçant le pipeline d'importation d'actifs v1 avec une base de données d'actifs réécrite pour permettre un changement rapide de plateforme. Il jette les bases d'importations d'actifs évolutives avec un suivi robuste des dépendances. Poursuivez votre lecture pour découvrir le fonctionnement de la base de données des actifs et quelques conseils pour gagner du temps.
Depuis la sortie de Unity 2019.3, le nouveau pipeline d'importation d'actifs est le pipeline par défaut pour les nouveaux projets. Combinées à de nombreuses autres améliorations, elles jettent les bases d'un pipeline d'importation d'actifs plus fiable, plus performant et plus évolutif. Cette réécriture a modifié le fonctionnement du dossier Bibliothèque afin de prendre en charge de nouveaux flux de travail.
Jetons un coup d'œil sous le capot à un certain nombre de situations que vous pouvez rencontrer et à la manière de les gérer efficacement. Vous apprendrez à repérer et à traiter certains des goulets d'étranglement qui vous font perdre du temps et de la performance dans vos projets.
Les conseils que vous trouverez ici s'appliquent à Unity 2019.3 et aux versions ultérieures. N'oubliez pas que si votre projet est en production au-delà du prototypage, pour une stabilité maximale, nous vous recommandons d'utiliser la dernière version Long-Term Support (LTS) d'Unity, Unity 2019 LTS.
Tout d'abord, parlons de certaines choses qui se produisent lorsque vous créez un nouveau projet ou que vous ouvrez un projet dans lequel votre bibliothèque d'actifs n'est pas encore présente.
Si vous regardez le fichier Editor.log, vous remarquerez un grand nombre de lignes qui ressemblent à ce qui suit :
- Commencer à importer ...
- Importation terminée ...
C'est ainsi que le pipeline d'importation d'actifs laisse une trace de ses opérations afin qu'elles puissent être inspectées ultérieurement.
Vous pouvez utiliser ces informations pour identifier un certain type de goulot d'étranglement, à savoir les délais d'importation des actifs.
En examinant la sortie de chaque ligne, vous pouvez extraire les données suivantes :
- Trajectoire des actifs
- Durée de l'importation
- Extension de fichier
En analysant ces données, nous pouvons ensuite les classer par extension. Une fois que vous savez quel importateur a importé quel bien, vous pouvez regrouper ces données dans un diagramme à secteurs qui vous montrera quels types de biens prennent le plus de temps à importer. Par exemple :

Ces données peuvent vous donner une idée plus précise des goulets d'étranglement de votre projet.
Dans cet exemple particulier, les textures, les modèles et les préfabriqués sont les principales sources de perte de temps, ce qui constitue un point de départ pour déterminer quels actifs pourraient être optimisés.
Téléchargez cet exemple de projet SimpleEditorLogParser et utilisez-le comme base pour votre propre analyseur.
Le fait de savoir quelle catégorie d'actifs prend le plus de temps à importer peut vous aider à planifier l'orientation de vos efforts d'optimisation. Si l'importation de textures est la catégorie qui prend le plus de temps, examinez les textures qui prennent le plus de temps à importer et envisagez de supprimer les textures qui ne sont pas censées se retrouver dans la version finale.
Cela permettra non seulement d'accélérer vos temps d'importation, mais aussi d'améliorer les performances de votre pipeline d'intégration continue si vous effectuez des constructions nocturnes propres ou quelque chose de similaire.
Il est important de pouvoir ouvrir rapidement vos projets. Les minutes nécessaires pour redémarrer l'éditeur ou ouvrir différents projets au cours de la journée peuvent représenter une perte de temps précieuse.
Au fur et à mesure qu'un projet devient plus complexe et utilise plus de fonctionnalités, il prend plus de temps à ouvrir. Cela peut être dû à un grand nombre de facteurs, et avant Unity 2019.3, il n'y avait aucun moyen de faire fonctionner le profileur pendant le lancement de l'éditeur.
Parmi les différents arguments de ligne de commande que vous pouvez fournir à l'ouverture d'Unity, l'argument de ligne de commande -profiler-enable vous permet de profiler l'éditeur lors de son lancement.
Cette commande indique à l'Éditeur de commencer à enregistrer les données de profilage pour la première image de l'application, c'est-à-dire tout le code exécuté jusqu'à ce que l'Éditeur soit visible. L'utilisation de cet argument peut vous aider à voir ce qui se passe pendant le démarrage et ce qui prend du temps. Vous pouvez voir s'il s'agit d'un système dans Unity, d'un paquet de l'Asset Store ou d'un code spécifique à votre projet. Cela peut vous aider à déterminer la marche à suivre.

Dans cette capture, vous pouvez voir que l'ouverture de ce projet particulier prend environ 50 secondes.
À première vue, le chargement de la base de données AssetDatabase prend 43 secondes. Un examen plus approfondi montre que 14 s sont consacrées à des appels à OnPostProcessAllAssets. Plus bas, le code qui s'exécute pendant RegisterScriptsAndTryLoadingExistingUserAssemblies ajoute jusqu'à 10 s, dont 5,7 s pour le chargement du domaine, qui est encore ralenti par les appels aux scripts ayant l'attribut [InitializeOnLoad].
Ces données de démarrage peuvent vous aider à repérer les goulets d'étranglement en matière de performances, et à voir si le code se trouve dans votre projet ou dans Unity lui-même.
Le dossier Bibliothèque peut désormais contenir plusieurs résultats d'importation pour le même actif, de sorte que les projets qui utilisent le nouveau pipeline d'importation d'actifs n'ont plus de correspondance "simple" entre le GUID (identifiant unique global) et le dossier dans le dossier Bibliothèque.
Les fichiers du dossier Bibliothèque sont basés sur le hachage de leur contenu et sur un certain nombre de dépendances statiques et dynamiques. Cela permet à Unity de disposer de fonctionnalités telles que le changement rapide de plateforme, la déduplication des actifs et le fait d'ignorer une importation si le hachage d'un actif est déjà présent dans le dossier de la bibliothèque.
Cela signifie qu'il n'est plus facile de trouver des résultats d'importation dans le dossier Bibliothèque. Voici un exemple qui vous montre où vous pouvez trouver le résultat de l'importation de "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());
}
}
Voici deux gists pour différentes versions d'Unity :
Les échantillons sont différents parce qu'au fur et à mesure que la mise en œuvre du pipeline d'importation d'actifs a évolué, un certain nombre d'API ont été déplacées de l'espace de noms expérimental vers l'espace de noms propre à la base de données d'actifs.
Il arrive souvent que vous souhaitiez trouver un atout particulier dans votre projet et faire quelque chose avec le résultat. Vous pouvez même effectuer cette opération plusieurs fois lorsque vous exécutez le code de l'éditeur.
L'appel à AssetDatabase.FindAssets permet de parcourir l'ensemble du projet pour répondre à la requête que vous lui avez donnée. Au fur et à mesure que les projets prennent de l'ampleur, cela peut devenir un goulot d'étranglement pour les performances, car le temps nécessaire pour effectuer une recherche dans des projets de taille différente augmente de façon linéaire.

Comme on pouvait s'y attendre, plus un projet comporte de ressources, plus le temps de recherche est long. Heureusement, le temps nécessaire pour trouver chaque actif reste relativement stable dans le temps.

Comme vous pouvez le constater, si votre projet comporte des centaines de milliers d'actifs, la recherche d'actifs peut entraîner un ralentissement notable du développement. Avec 200 000 actifs, il y a déjà un délai de 200 ms pour une simple requête de recherche.
L'utilisation de la force brute permet d'obtenir ce modèle d'utilisation courant :
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
}
}Essentiellement, ce code parcourt l'ensemble du projet pour trouver une texture et en faire quelque chose.
La base de données AssetDatabase permet de consulter le chemin d'accès d'un bien par son GUID. Vous pouvez considérer cela comme une recherche dans un dictionnaire par clé au lieu d'itérer un tableau pour trouver une correspondance.
L'avantage de cette approche par rapport à la force brute est que l'AssetDatabase n'a pas besoin de parcourir l'ensemble du projet pour trouver l'actif. Il peut simplement utiliser le GUID comme index de base de données et suivre ce chemin pour charger le bien en mémoire.
Vous pouvez trouver le GUID dans le fichier .meta correspondant à un actif, par exemple :
fileFormatVersion : 2
guide : 9fc0d4010bbf28b4594072e72b8655ab
Importateur par défaut :
externalObjects : {}
userData :
assetBundleName :
assetBundleVariant :
Dans ce cas, le GUID de ce bien est 9fc0d4010bbf28b4594072e72b8655ab.
Compte tenu de ces informations, vous pouvez procéder comme suit :
var path = AssetDatabase.GUIDToAssetPath("9fc0d4010bbf28b4594072e72b8655ab");
var asset = AssetDatabase.LoadAssetAtPath<Texture>(path);
Votre bien est maintenant prêt à être utilisé.
Par ailleurs, si vous souhaitez accélérer la recherche dans l'éditeur, vous devriez également connaître les outils suivants :
Le module de recherche rapide vous permet d'effectuer des recherches dans plusieurs domaines de l'Unity.
TypeCache pour rechercher des scripts dérivés d'un type que vous connaissez.
Normalement, le GUID d'un bien est constant.
Cependant, dans certaines situations, un bien et son fichier .meta sont dupliqués, ce qui provoque un conflit que l'AssetDatabase résout de la manière suivante :
Si vous dupliquez un dossier à l'intérieur d'un projet vers un autre endroit du même projet
Importer plusieurs fois un paquet Asset Store ou copier plusieurs fois un dossier d'un autre projet dans votre projet
Lorsque AssetDatabase.Refresh s'exécute, de nombreux systèmes doivent travailler ensemble pour présenter votre projet dans un état valide. Dans mon exposé à Unite Copenhagen, je détaille les différentes étapes de ce qui se passe lorsque l'on fait appel à Refresh.
Les rappels particuliers exécutés au cours d'une actualisation interagissent avec le code. Cela peut avoir une incidence sur la durée de l'opération d'actualisation.
Plus le code exécuté lors du rechargement d'un domaine est important, plus l'utilisation de l'éditeur est lente. Pour rester dans le flux lors de l'itération sur le code, réfléchissez bien au moment où le code doit être exécuté et s'il peut être reporté à plus tard dans le projet.
Lors du rechargement d'un domaine, votre code sera exécuté s'il contient l'une des méthodes suivantes :
1. Réveil
2. Activé
3. OnValidate
Votre code dans ces méthodes devrait idéalement être très rapide ou être différé pour être exécuté à un autre moment (pas pendant une actualisation, par exemple). En effet, ces rappels sont censés vous aider à restaurer certains états, mais comme il n'existe aucune restriction sur ce qui peut être fait dans le cadre de ces appels, tout code qui n'est pas mis à l'échelle (c'est-à-dire tout ce qui traverse l'ensemble du projet) ralentit votre vitesse d'itération pour les scripts tant que l'éditeur est ouvert.
Une autre approche consiste à utiliser EditorApplication.delayCall, où votre code est exécuté lors du prochain tick de l'éditeur, après que la base de données AssetDatabase ait eu la possibilité de détecter et d'importer toutes les modifications sur le disque.
Suivez ce fil de discussion sur les forums Unity pour rester informé des améliorations dans ce domaine.
J'espère que ces conseils vous ont été utiles. Faites-nous savoir ce que vous aimeriez savoir sur le pipeline d'importation d'actifs ou quels sont vos problèmes. Nous travaillons activement à l'amélioration du pipeline d'importation d'actifs, et nous souhaitons que votre itération soit aussi instantanée que possible, afin que vous puissiez être plus productif lorsque vous travaillez avec l'éditeur et que vous modifiez des actifs et/ou des scripts.
