Doseur SRP : Accélérez votre rendu

En 2018, nous avons introduit une technologie de rendu hautement personnalisable que nous appelons Scriptable Render Pipeline (SRP). Une partie de cela est une nouvelle boucle de rendu de moteur de bas niveau appelée SRP Batcher qui peut accélérer votre processeur pendant le rendu de 1,2x à 4x, selon la scène. Voyons comment utiliser cette fonctionnalité au mieux !
Cette vidéo montre le pire des scénarios pour Unity: chaque objet est dynamique et utilise un matériau différent (couleur, texture). Cette scène montre de nombreux maillages similaires, mais elle fonctionnerait de la même manière avec un maillage différent par objet (donc l'instanciation GPU ne peut pas être utilisée). L'accélération est d'environ 4x sur PlayStation 4 (cette vidéo est sur PC, Dx11).
REMARQUE : lorsque nous parlons d'accélération x4, nous parlons du code de rendu du processeur (les marqueurs de profilage « RenderLoop.Draw » et « ShadowLoop.Draw »). Nous ne parlons pas de fréquence d'images globale (FPS).
L'éditeur Unity dispose d'un moteur de rendu très flexible. Vous pouvez modifier n’importe quelle propriété de matériau à tout moment au cours d’une image. De plus, Unity a historiquement été conçu pour les tampons non constants, prenant en charge les API graphiques telles que DirectX9. Cependant, ces fonctionnalités intéressantes présentent également quelques inconvénients. Par exemple, il y a beaucoup de travail à faire lorsqu'un DrawCall utilise un nouveau matériau. Donc, fondamentalement, plus vous avez de matériaux dans une scène, plus le CPU sera nécessaire pour configurer les données GPU.

Pendant la boucle de rendu interne, lorsqu'un nouveau matériau est détecté, le processeur collecte toutes les propriétés et configure différents tampons constants dans la mémoire du GPU. Le nombre de tampons GPU dépend de la manière dont le Shader déclare ses CBUFFER.
Lorsque nous avons développé la technologie SRP, nous avons dû réécrire certaines parties du moteur de bas niveau. Nous avons vu une excellente opportunité d’intégrer nativement de nouveaux paradigmes, tels que la persistance des données GPU. Nous avons cherché à accélérer le cas général où une scène utilise de nombreux matériaux différents, mais très peu de variantes de Shader.
Désormais, les boucles de rendu de bas niveau peuvent rendre les données matérielles persistantes dans la mémoire du GPU. Si le contenu du matériel ne change pas, il n'est pas nécessaire de configurer et de télécharger le tampon sur le GPU. De plus, nous utilisons un chemin de code dédié pour mettre à jour rapidement les propriétés du moteur intégré dans une grande mémoire tampon GPU. Maintenant, le nouveau diagramme ressemble à ceci :

Ici, le processeur gère uniquement les propriétés du moteur intégré, appelées transformation de matrice d'objet. Tous les matériaux ont des CBUFFER persistants situés dans la mémoire du GPU, qui sont prêts à être utilisés. Pour résumer, l’accélération vient de deux choses différentes :
- Chaque contenu matériel est désormais persistant dans la mémoire du GPU
- Un code dédié gère un grand GPU CBUFFER « par objet »
Votre projet doit utiliser soit le pipeline de rendu léger (LWRP), soit le pipeline de rendu haute définition (HDRP) ou votre propre SRP personnalisé. Pour activer le SRP Batcher dans HDRP ou LWRP, utilisez simplement la case à cocher dans l'inspecteur d'actifs SRP.

Si vous souhaitez activer/désactiver SRP Batcher lors de l'exécution, pour évaluer les avantages en termes de performances, vous pouvez également basculer cette variable globale à l'aide du code C# :
GraphicsSettings.useScriptableRenderPipelineBatching = vrai;
Pour qu'un objet soit rendu via le chemin de code SRP Batcher, deux exigences sont requises :
1. L'objet doit être dans un maillage. Il ne peut pas s'agir d'une particule ou d'un maillage écorché.
2. Vous devez utiliser un Shader compatible avec le SRP Batcher. Tous les shaders allumés et non allumés dans HDRP et LWRP répondent à cette exigence.
Pour qu'un Shader soit compatible avec SRP :
- Toutes les propriétés du moteur intégré doivent être déclarées dans un seul CBUFFER nommé « UnityPerDraw ». Par exemple, unity_ObjectToWorld ou unity_SHAr.
- Toutes les propriétés des matériaux doivent être déclarées dans un seul CBUFFER nommé « UnityPerMaterial ».
Vous pouvez voir l’état de compatibilité d’un Shader dans le panneau Inspecteur. Cette section de compatibilité s'affiche uniquement si votre projet est basé sur SRP.

Dans une scène donnée, certains objets sont compatibles avec SRP Batcher, d'autres non. Mais la scène est toujours rendue correctement. Les objets compatibles utiliseront le chemin de code SRP Batcher, et d'autres utiliseront toujours le chemin de code SRP standard.

Si vous souhaitez mesurer l'augmentation de la vitesse avec SRP Batcher dans votre scène spécifique, vous pouvez utiliser le script C# SRPBatcherProfiler.cs. Ajoutez simplement le script dans votre scène. Lorsque ce script est en cours d'exécution, vous pouvez basculer l'affichage de la superposition à l'aide de la touche F8. Vous pouvez également activer et désactiver SRP Batcher pendant la lecture à l'aide de la touche F9. Si vous activez la superposition en mode PLAY (F8), vous devriez voir de nombreuses informations utiles :

Ici, tout le temps est mesuré en millisecondes (ms). Ces mesures de temps montrent le CPU dépensé dans les boucles de rendu Unity SRP.
REMARQUE : le temps correspond au temps cumulé de tous les « RenderLoop.Draw » et « Shadows.Draw »." marqueurs appelés pendant une trame, quel que soit le propriétaire du thread. Lorsque vous voyez « 1,31 ms SRP Batcher code path », peut-être que 0,31 ms sont dépensés thread principal, et 1 ms est réparti sur tous les travaux graphiques.
Dans ce tableau, vous pouvez voir une description de chaque paramètre de la superposition visible en mode PLAY, de haut en bas :

NOTE: Nous hésitons à ajouter des FPS en bas de la superposition car vous devez être très prudent quant aux mesures FPS lors de l'optimisation. Premièrement, le FPS n'est pas linéaire, donc voir le FPS augmenter de 20 % ne vous indique pas immédiatement à quel point vous avez optimisé votre scène. Deuxièmement, le FPS est global sur l’image. Le FPS (ou timing global des images) dépend de bien d'autres choses que le rendu, comme le gameplay C#, la physique, l'élimination, etc.
Vous pouvez obtenir SRPBatcherProfiler.cs à partir d'un modèle de projet SRP Batcher sur GitHub.
Voici quelques scènes Unity prises avec SRP Batcher OFF et ON pour voir l'accélération dans diverses situations.

Livre des morts, HDRP, PlayStation 4. x1.47 accéléré. Veuillez noter que le FPS ne change pas, car cette scène est liée au GPU. Il vous reste 12 ms pour faire d'autres choses côté CPU. L'accélération est presque la même sur PC.

Exemple de FPS, HDRP, PC DirectX 11. X1.23 accélère. Veuillez noter qu'il reste encore 1,67 ms pour le chemin de code standard en raison de l'incompatibilité de SRP Batcher. Dans ce cas, des maillages skinnés et quelques particules rendues à l'aide de blocs de propriétés matérielles.

Attaque de bateau, LWRP, PlayStation 4. Accélération x2.13.
SRP Batcher fonctionne sur presque toutes les plateformes. Voici un tableau indiquant la plate-forme et la version minimale Unity requise. Unity 2019.2 est actuellement en version alpha ouverte.

Le chemin de code rapide de SRP Batcher est pris en charge en VR, uniquement avec le mode « SinglePassInstanced ». L'activation de la VR n'ajoutera aucun temps CPU (grâce au mode SinglePassInstanced)
Comment puis-je savoir que j'utilise SRP Batcher de la meilleure façon possible ?
Utilisez SRPBacherProfiler.cs et vérifiez d’abord que SRP Batcher est activé. Ensuite, regardez le timing du « chemin de code standard ». Cela devrait être proche de 0 et tout le temps devrait être dépensé dans le « chemin de code SRP Batcher ». Parfois, il est normal qu'un certain temps soit passé dans le chemin de code standard si votre scène utilise quelques maillages ou particules skinnés. Découvrez notre projet SRP Batcher Benchmark sur GitHub.
SRPBacherProfiler affiche une synchronisation similaire, que SRP Batcher soit activé ou désactivé. Pourquoi?
Tout d’abord, vous devez vérifier que presque tout le temps de rendu passe par le nouveau chemin de code (voir ci-dessus). Si c’est le cas et que les chiffres sont toujours similaires, regardez le numéro de « chasse d’eau ». Ce nombre de « vidange » devrait diminuer considérablement lorsque le SRP Batcher est activé. En règle générale, divisé par 10 est vraiment bien, divisé par 2 est presque bien. Si le nombre de flushs ne diminue pas beaucoup, cela signifie que vous avez encore beaucoup de variantes de Shader. Essayez de réduire le nombre de variantes de Shader. Si vous avez fait beaucoup de shaders différents, essayez d’en créer un « uber » avec plus de paramètres. Avoir des tonnes de paramètres de matériaux différents est alors gratuit.
Le FPS global n'a pas changé lorsque j'ai activé le SRP Batcher. Pourquoi?
Vérifiez les deux questions ci-dessus. Si SRPBatcherProfiler indique que le « temps de rendu du processeur » est deux fois plus rapide et que le FPS n’a pas changé, alors la partie rendu du processeur n’est pas votre goulot d’étranglement. Cela ne signifie pas que vous n'êtes pas limité par le processeur - au contraire, vous utilisez peut-être trop de gameplay C# ou trop d'éléments physiques. Quoi qu'il en soit, si le « temps de rendu du processeur » est deux fois plus rapide, c'est toujours positif. Vous avez probablement remarqué sur la vidéo du haut que même avec une accélération de 3,5x, la scène est toujours à 60 FPS. C'est parce que nous avons activé VSYNC. SRP Batcher a réellement économisé 6,8 ms côté CPU. Ces millisecondes pourraient être utilisées pour une autre tâche. Cela peut également simplement économiser la batterie du mobile.
Il est important de comprendre ce qu'est un « lot » dans le contexte de SRP Batcher. Traditionnellement, les gens ont tendance à réduire le nombre de DrawCall pour optimiser le coût de rendu du processeur. La véritable raison est que le moteur doit configurer de nombreuses choses avant de lancer le tirage. Et le coût réel du processeur provient de cette configuration, et non du DrawCall du GPU lui-même (il s'agit simplement de quelques octets à insérer dans le tampon de commande du GPU). SRP Batcher ne réduit pas le nombre de DrawCalls. Cela réduit simplement le coût de configuration du GPU entre les DrawCalls.
Vous pouvez le constater dans le flux de travail suivant :

Sur la gauche se trouve la boucle de rendu SRP standard. Sur la droite se trouve la boucle SRP Batcher. Dans le contexte de SRP Batcher, un « lot » est simplement une séquence de « Bind », « Drawing », « Bind », Draw »... Commandes GPU.
Dans le SRP standard, le SetShaderPass lent est appelé pour chaque nouveau matériau. Dans le contexte de SRP Batcher, SetShaderPass est appelé pour chaque nouvelle variante de shader.
Pour obtenir des performances maximales, vous devez conserver ces lots aussi grands que possible. Vous devez donc éviter tout changement de variante de shader, mais vous pouvez utiliser n'importe quel nombre de matériaux différents s'ils utilisent le même shader.
Vous pouvez utiliser Unity Frame Debugger pour examiner la longueur des « lots » du SRP Batcher. Chaque lot est un événement dans le débogueur de trame appelé « lot SRP », comme vous pouvez le voir ici :

Voir l'événement SRP Batch sur la gauche. Voir également la taille du lot, qui correspond au nombre d'appels de tirage (109 ici). C'est un lot assez efficace. Vous voyez également la raison pour laquelle le lot précédent a été cassé (« Le nœud utilise des mots-clés de shader différents »). Cela signifie que les mots-clés du shader utilisés pour ce lot sont différents des mots-clés du lot précédent. Cela signifie que la variante du shader a changé et que nous devons interrompre le lot.
Dans certaines scènes, la taille de certains lots peut être vraiment faible, comme celle-ci :

La taille du lot est de seulement 2. Cela signifie probablement que vous avez trop de variantes de shaders différentes. Si vous créez votre propre SRP, essayez d'écrire un shader « uber » générique avec un minimum de mots-clés. Vous n’avez pas à vous soucier du nombre de paramètres matériels que vous mettez dans la section « propriété ».
NOTE: Les informations du SRP Batcher dans Frame Debugger nécessitent Unity 2018.3 ou une version supérieure.
Note: Cette section est destinée aux utilisateurs avancés qui écrivent leur propre boucle de rendu scriptable et leur propre bibliothèque de shaders. Les utilisateurs de LWRP ou HDRP peuvent ignorer cette section, car tous les shaders que nous fournissons sont déjà compatibles avec SRP Batcher.
Si vous écrivez votre propre boucle de rendu, vos shaders doivent suivre certaines règles pour parcourir le chemin du code SRP Batcher.
Tout d’abord, toutes les données « par matériau » doivent être déclarées dans un seul CBUFFER nommé « UnityPerMaterial ». Que sont les données « par matériau » ? Généralement, toutes les variables que vous avez déclarées dans la section « propriété du shader ». Ce sont toutes les variables que votre artiste peut modifier à l’aide de l’inspecteur de GUI utilisateur des matériaux. Par exemple, regardons un shader simple comme :
Properties
{
_Color1 ("Color 1", Color) = (1,1,1,1)
_Color2 ("Color 2", Color) = (1,1,1,1)
}
float4 _Color1;
float4 _Color2;Si vous compilez ce shader, le panneau d'inspection du shader vous montrera :

Pour résoudre ce problème, déclarez simplement toutes vos données « par matériau » comme ceci :
CBUFFER_START(UnityPerMaterial)
float4 _Color1;
float4 _Color2;
CBUFFER_ENDSRP Batcher a également besoin d'un CBUFFER très spécial nommé « UnityPerDraw ». Ce CBUFFER doit contenir toutes les variables du moteur intégré à Unity .
L'ordre de déclaration des variables à l'intérieur de « UnityPerDraw » CBUFFER est également important. Toutes les variables doivent respecter une disposition que nous appelons « Fonctionnalité de bloc ». Par exemple, la « fonctionnalité de bloc Position spatiale » doit contenir toutes ces variables, dans cet ordre :
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
float4 unity_WorldTransformParams;Vous n’êtes pas obligé de déclarer certaines de ces fonctionnalités de bloc si vous n’en avez pas besoin. Toutes les variables du moteur intégrées dans « UnityPerDraw » doivent être float4 ou float4x4. Sur mobile, les utilisateurs peuvent souhaiter utiliser real4 (valeur à virgule flottante codée sur 16 bits) pour économiser de la bande passante GPU. Toutes les variables UnityPerDraw ne peuvent pas utiliser « real4 ». Veuillez vous référer à la colonne « Pourrait être réel4 ».
Voici un tableau décrivant toutes les fonctionnalités de bloc possibles que vous pourriez utiliser dans le CBUFFER « UnityPerDraw » :

NOTE: Si l'une des variables d'un bloc de fonctionnalités est déclarée comme real4 ( half ), alors toutes les autres variables potentielles de ce bloc de fonctionnalités doivent également être déclarées comme real4.
CONSEIL 1 : vérifiez toujours l’état de compatibilité d’un nouveau shader dans l’inspecteur. Nous vérifions plusieurs erreurs potentielles (déclaration de mise en page UnityPerDraw, etc.) et affichons pourquoi elles ne sont pas compatibles.
CONSEIL 2 : Lorsque vous écrivez votre propre shader SRP, vous pouvez vous référer au package LWRP ou HDRP pour consulter leur déclaration UnityPerDraw CBUFFER pour vous inspirer.
Nous continuons d'améliorer SRP Batcher en augmentant la taille des lots dans certaines passes de rendu (en particulier les passes d'ombre et de profondeur).
Nous travaillons également sur l'ajout d'une utilisation automatique des instances GPU avec SRP Batcher. Nous avons commencé avec le nouveau moteur de rendu DOTS utilisé dans notre démo MegaCity. L'accélération dans l'éditeur Unity est assez impressionnante, passant de 10 à 50 FPS.
Éditeur MegaCity avec SRP Batcher et moteur de rendu DOTS. La différence de performances est si énorme que même la fréquence d’images globale est multipliée par cinq.
NOTE: Pour être précis, cette accélération massive lors de l'activation du SRP Batcher concerne uniquement l'éditeur, car l'éditeur n'utilise actuellement pas les tâches graphiques. L'accélération en mode lecteur autonome est d'environ x2.
MegaCity dans l'éditeur. Si vous pouviez lire la vidéo à 60 Hz, vous ressentiriez l'accélération lors de l'activation de SRP Batcher.
NOTE: SRP Batcher avec moteur de rendu DOTS est encore expérimental et en développement actif.
