Améliorations des temps de construction des shaders et de l'utilisation de la mémoire dans LTS 2021

DAMIAN NACHMAN / UNITY TECHNOLOGIESSenior Technical Product Manager
Dec 28, 2022|13 Min
Améliorations des temps de construction des shaders et de l'utilisation de la mémoire dans LTS 2021
Cette page a été traduite automatiquement pour faciliter votre expérience. Nous ne pouvons pas garantir l'exactitude ou la fiabilité du contenu traduit. Si vous avez des doutes quant à la qualité de cette traduction, reportez-vous à la version anglaise de la page web.

À mesure que l'ensemble des fonctionnalités disponibles du pipeline de rendu scriptable (SRP) d'Unity continue de croître, la quantité de variantes de shaders traitées et compilées au moment de la construction augmente également. Parallèlement à la prise en charge continue d'API graphiques supplémentaires et à une sélection toujours croissante de plates-formes cibles, les améliorations du SRP continuent de s'étendre.

Les shaders sont compilés et mis en cache après une build initiale (« propre »), accélérant ainsi les builds incrémentielles (« chaudes ») ultérieures. Bien que les builds propres prennent généralement plus de temps, les longs délais de build à chaud peuvent constituer un problème courant lors du développement et de l'itération d'un projet.

Traitement et compilation des variantes de shader pendant la construction du projet

Pour résoudre ce problème, l’équipe de gestion des shaders d’Unity a travaillé dur pour fournir des solutions significatives et évolutives. Cela a permis de réduire considérablement les temps de création des shaders et l'utilisation de la mémoire d'exécution pour les projets créés à l'aide d'Unity 2021 LTS et des versions ultérieures.

Pour en savoir plus sur ces nouvelles optimisations, y compris les versions concernées, les rétroportages et les chiffres issus de nos tests internes, passez directement aux sections traitant du préfiltrage des variantes de shader et du chargement dynamique des shaders. À la fin de cet article de blog, nous abordons également nos projets futurs visant à affiner davantage la gestion des variantes de shaders dans son ensemble, à travers la création, la construction et l’exécution du projet.

Avant de nous plonger dans les améliorations intéressantes apportées au système de shaders d'Unity, profitons également de l'occasion pour passer rapidement en revue les concepts de compilation conditionnelle de shaders, de variantes de shaderset de suppression de variantes de shaders.

Fonctionnalités du shader conditionnel

Les fonctionnalités de shader conditionnel permettent aux développeurs et aux artistes de contrôler et de modifier facilement les fonctionnalités d'un shader à l'aide de scripts, de paramètres de matériaux, ainsi que de paramètres de projet et de graphiques. De telles fonctionnalités conditionnelles servent à simplifier la création de projets, permettant aux projets d'évoluer efficacement en minimisant le nombre de shaders que vous devrez créer et maintenir.

Une fonctionnalité de matériau Clear Coat activée par l'artiste au moment de la création, en activant un mot-clé shader_feature

Les fonctionnalités de shader conditionnel peuvent être implémentées de différentes manières :

  • Branchement statique (au moment de la compilation)
  • Compilation des variantes de shaders
  • Branchement dynamique (d'exécution)

Bien que la ramification statique évite la surcharge d'exécution du shader liée à la ramification au moment de l'exécution, elle est évaluée et verrouillée au moment de la compilation et ne fournit pas de contrôle d'exécution. La compilation de variantes de shader, quant à elle, est une forme de ramification statique qui fournit un contrôle d'exécution supplémentaire. Cela fonctionne en compilant un programme de shader unique (variante) pour chaque combinaison possible de branches statiques, afin de maintenir des performances GPU optimales lors de l'exécution.

De telles variantes sont créées en déclarant et en évaluant conditionnellement les fonctionnalités du shader via les mots-clés shader_feature et multi_compile . Les variantes de shader correctes sont chargées au moment de l'exécution en fonction des mots-clés actifs et des paramètres d'exécution. La déclaration et l’évaluation de mots-clés de shader supplémentaires peuvent entraîner une augmentation du temps de génération, de la taille du fichier et de l’utilisation de la mémoire d’exécution.

Dans le même temps, la ramification dynamique (basée sur l'uniforme) évite entièrement la surcharge de compilation des variantes de shader, ce qui permet des builds plus rapides et une réduction de la taille des fichiers et de l'utilisation de la mémoire. Cela peut générer une itération plus fluide et plus rapide pendant le développement.

D'autre part, la ramification dynamique peut avoir un impact important sur les performances d'exécution du shader en fonction de la complexité du shader et du périphérique cible. Les branches asymétriques, où un côté de la branche est beaucoup plus complexe que l’autre, peuvent avoir un impact négatif sur les performances. Cela est dû au fait que l’exécution d’un chemin plus simple peut néanmoins entraîner les pénalités de performances du chemin plus complexe.

Lorsque vous introduisez des fonctionnalités de shader conditionnel dans vos propres shaders, ces approches et compromis doivent être gardés à l’esprit. Pour des informations plus détaillées, consultez la documentation sur les conditionnels du shader, la ramification du shaderet les variantes du shader .

Suppression des variantes de shaders

Pour atténuer l’augmentation du temps de traitement et de compilation des shaders, la suppression des variantes de shaders est utilisée. Son objectif est d'exclure les variantes de shader inutiles de la compilation en fonction de facteurs tels que :

  • Matériel inclus et mots-clés activés
  • Paramètres du projet et du pipeline de rendu
  • Décapage scriptable

Lors de l'énumération des variantes de shader, l'éditeur filtrera automatiquement tous les mots-clés déclarés avec shader_feature qui ne sont pas activés par les matériaux référencés et inclus dans la build. Par conséquent, ces mots-clés ne généreront aucune variante supplémentaire.

Par exemple, si la propriété de matériau Clear Coat n'est activée par aucun matériau utilisant le shader URP Complex Lit, toutes les variantes de shader qui implémentent la fonctionnalité Clear Coat seront supprimées en toute sécurité au moment de la construction.

En attendant, les mots-clés multi_compile invitent les développeurs et les joueurs à contrôler librement les fonctionnalités du shader au moment de l'exécution en fonction des paramètres et des scripts du lecteur disponibles. Le revers de la médaille est que ces mots-clés ne peuvent pas être automatiquement supprimés par l’éditeur dans la même mesure que les mots-clés shader_feature . C'est pourquoi ils produisent généralement un plus grand nombre de variantes.

Le stripping scriptable est une API C# qui vous permet d'exclure des variantes de shader de la compilation pendant la phase de construction via des mots-clés et des combinaisons non requis lors de l'exécution. Les pipelines de rendu utilisent un décapage scriptable afin de supprimer les variantes inutiles en fonction des paramètres du pipeline de rendu du projet et des ressources de qualité incluses dans la build.

Faible qualité Haute qualité Variante multiplicateur Lumière principale/Ombres portées : Désactivé Activé 2x Lumière principale/Ombres portées : Activé 1x Lumière principale/Ombres portées : Off Off 1x

Afin de maximiser les effets de la suppression des variantes de shader de l'éditeur, nous vous recommandons de désactiver toutes les fonctionnalités liées aux graphiques et les paramètres du pipeline de rendu qui ne sont pas utilisés au moment de l'exécution. Veuillez vous référer à la documentation officielle pour en savoir plus sur la suppression des variantes de shader.

Préfiltrage des variantes de shader

La suppression des variantes de shader réduit considérablement la quantité de variantes de shader compilées, en fonction de facteurs tels que les ressources de qualité du pipeline de rendu dans la build. Cependant, le décapage est actuellement effectué à la fin de l'étape de traitement du shader. La simple énumération de toutes les variantes possibles peut encore prendre beaucoup de temps, quelle que soit la compilation.

Afin de réduire les temps de traitement des variantes de shader (et de construction du projet), nous introduisons désormais une optimisation significative du décapage des variantes de shader intégré au moteur. Grâce au préfiltrage des variantes de shader, les temps de construction propres et à chaud sont considérablement réduits.

L'optimisation fonctionne en introduisant l'exclusion précoce des mots-clés multi_compile , selon les attributs de préfiltrage pilotés par les paramètres du pipeline de rendu. Cela réduit la quantité de variantes énumérées pour un éventuel décapage et une compilation, ce qui à son tour réduit le temps de traitement du shader - avec des temps de construction à chaud réduits jusqu'à 90 % dans les exemples les plus drastiques.

Le préfiltrage des variantes de shader est apparu pour la première fois dans 2023.1.0a14et a été rétroporté vers 2022.2.0b15 et 2021.3.15f1.

Réduction du temps de traitement des shaders pour les projets chauds dans URP Boat Attack
Réduction du temps de traitement des shaders pour les projets chauds dans URP Terrain Demo

Le préfiltrage des variantes permet également de réduire les temps de construction initiaux/nettoyants en appliquant le même principe.

Réduction du temps de traitement des shaders pour des projets propres dans URP Terrain Demo
Réduction du temps de traitement des shaders pour les projets chauds dans URP Terrain Demo
Chargement dynamique des shaders

Historiquement, l'environnement d'exécution d'Unity chargeait tous les objets shader du disque vers la mémoire du processeur pendant le chargement de la scène et des ressources. Dans la plupart des cas, un projet et une scène construits incluent beaucoup plus de variantes de shaders que nécessaire à un moment donné pendant l'exécution de l'application. Pour les projets utilisant une grande quantité de shaders, cela entraîne souvent une utilisation élevée de la mémoire du shader lors de l'exécution.

Le chargement dynamique des shaders résout le problème en offrant un contrôle utilisateur précis sur le comportement de chargement des shaders et l'utilisation de la mémoire. Cette optimisation facilite la diffusion de blocs de données de shader dans la mémoire, ainsi que l'éviction des données de shader qui ne sont plus nécessaires au moment de l'exécution, en fonction d'un budget de mémoire contrôlé par l'utilisateur. Cela vous permet de réduire considérablement l'utilisation de la mémoire du shader sur les plates-formes avec des budgets de mémoire limités.

Les nouveaux paramètres de chargement des variantes de shader sont désormais accessibles depuis les paramètres du lecteurde l'éditeur. Utilisez-les pour remplacer le nombre maximal de morceaux de shader chargés et la taille de morceau par shader (Mo).

Éditeur > Paramètres du projet > Lecteur > Paramètres de chargement des variantes de shader

Avec l'API C# suivante désormais disponible, vous pouvez remplacer les paramètres de chargement des variantes de shader à l'aide de scripts d'éditeur, tels que :

Vous pouvez également remplacer la quantité maximale de morceaux de shader chargés au moment de l'exécution à l'aide de l'API C# via Shader.maximumChunksOverride. Cela vous permet de remplacer le budget de mémoire du shader en fonction de facteurs tels que la mémoire système et graphique totale disponible interrogée lors de l'exécution.

Le chargement dynamique des shaders a été déployé dans la version 2023.1.0a11 et a été rétroporté vers les versions 2022.2.0b10, 2022.1.21f1et 2021.3.12f. Dans le cas de Boat Attackde Universal Render Pipeline (URP), nous avons observé une réduction de 78,8 % de l'utilisation de la mémoire d'exécution pour les shaders, de 315 Mio (par défaut) à 66,8 Mio (chargement dynamique). Vous pouvez en savoir plus sur cette optimisation dans l' annonce officielle.

Chargement de shader dynamique utilisé dans URP Boat Attack, entraînant une réduction de 78,8 % de l'utilisation de la mémoire du shader lors de l'exécution.
Quelle est la prochaine étape ?

Au-delà des changements critiques mentionnés ci-dessus, nous travaillons à améliorer la génération et la suppression des variantes de shader du pipeline de rendu universel. Nous étudions également des améliorations supplémentaires de la gestion globale des variantes de shaders d'Unity. L'objectif ultime est de faciliter l'augmentation du nombre de fonctionnalités du moteur, tout en garantissant une création de shader minimale et une surcharge d'exécution.

Certaines de nos enquêtes en cours impliquent la déduplication des ressources de shader sur des variantes similaires, ainsi que des améliorations globales des mots-clés de shader et des API de collection de variantes de shader. L’objectif est de fournir plus de flexibilité et de contrôle sur le traitement des variantes de shader et les performances d’exécution.

À l’avenir, nous explorons également la possibilité d’utiliser des outils intégrés à l’éditeur pour le traçage et l’analyse des variantes de shader afin de fournir les détails suivants sur l’utilisation des variantes de shader :

  • Quels shaders et mots-clés produisent le plus de variantes ?
  • Quelles variantes sont compilées mais inutilisées lors de l’exécution ?
  • Quelles variantes sont supprimées mais demandées au moment de l'exécution ?

Vos commentaires ont été déterminants jusqu’à présent, car ils nous aident à prioriser les solutions les plus significatives. Veuillez consulter notre feuille de route publique pour voter sur les fonctionnalités qui correspondent le mieux à vos besoins. Si vous souhaitez voir des modifications supplémentaires, n'hésitez pas à soumettre une demande de fonctionnalitéou à contacter directement l'équipe dans ce forum de shaders.