Le profilage pour les jeux mobiles avec Unity et ARM

Unity Learn s'attaque aux problèmes de performances mobiles avec les outils de profilage d'Unity et d'ARM. Approfondissez la manière de profiler avec Unity, d'optimiser les baisses de performance et les trucs et astuces pour tirer le meilleur parti de vos ressources de jeu.
Dans ce blog, nous examinons comment identifier les problèmes de performance dans un jeu mobile grâce aux outils de profilage d'Unity et d'ARM. Nous présentons également les meilleures pratiques pour optimiser le contenu des jeux mobiles.
Afin d'identifier les problèmes de performance de votre jeu, vous devez d'abord le tester sur une série d'appareils différents. La meilleure façon de procéder consiste à établir un profil de performance sur un appareil réel. Des outils tels que Unity Profiler et Frame Debugger peuvent vous donner un aperçu de la manière dont les éléments de votre jeu utilisent leurs ressources. En outre, des outils comme Arm Mobile Studio vous permettent de capturer les données d'activité du compteur de performance de l'appareil, afin que vous puissiez voir exactement comment votre jeu utilise les ressources du CPU et du GPU. Bien que l'appareil que nous avons utilisé soit équipé d'un GPU Mali, les concepts présentés ici s'appliquent également à d'autres GPU mobiles.
Le jeu que nous testons est un RPG d'action, dans lequel le joueur doit combattre des vagues d'ennemis PNJ avec des attaques de mêlée et des sorts. Ce type de jeu peut rapidement être limité par le GPU sur un appareil mobile, avec un nombre croissant d'ennemis à l'écran ainsi que de multiples effets visuels de particules et de post-traitement.
Nous avons fait passer le jeu par le Profiler d'Unity afin d'identifier tout ralentissement des performances. Nous avons trouvé quelques suspects de haute priorité, post Processing, et corrigé les pics de Timestep et d'instanciation.
Les effets de post-traitement ont été l'une des principales causes des mauvaises performances du jeu au niveau de l'unité centrale.

De tous les effets de post-traitement, la passe de floraison, qui fait briller les zones lumineuses de la scène, a été la plus éprouvante.
Dans la capture d'écran ci-dessus, vous pouvez voir que la caméra de rendu prend beaucoup de temps et dépasse la limite du cadre. Le thread principal attend ensuite que les commandes de rendu soient terminées avant de préparer l'image suivante. Regardons le débogueur de Unity Frame pour comprendre ce qui se passe.

La première chose à remarquer dans le Frame Debugger est que le jeu est rendu à la résolution plein écran de l'appareil. Pour un appareil mobile moyen, cela exerce une pression excessive sur le GPU de l'appareil, compte tenu de la complexité du contenu. Réduire la résolution à quelque chose de plus raisonnable comme 1080p ou même 720p réduirait considérablement les coûts de rendu du jeu, en particulier les effets de post-traitement.

Le point suivant à observer est que l'effet de floraison se produit dans 25 appels de tirage pour la pyramide de floraison. Chaque appel de dessin représente un tampon cible dont la taille commence à la moitié de la résolution du périphérique plein écran. Cette résolution est ensuite réduite de moitié à chaque itération. La réduction de la résolution initiale du rendu est un moyen de réduire le nombre potentiel d'itérations. Une autre solution consisterait à modifier le code source de l'effet bloom pour réduire le nombre d'itérations et imposer une limite raisonnable. Toutefois, dans ce cas, il serait préférable de désactiver les effets de post-traitement pour l'instant, en raison du temps considérable que prend le traitement de ces effets. Du moins, jusqu'à ce que le reste du jeu puisse être exécuté sans problème à 30 images par seconde.
Une autre amélioration du projet consisterait à réduire la fréquence de l'intervalle fixe Timestep. Nous pouvons voir qu'il est actuellement suffisamment court pour être appelé plusieurs fois par image ; par défaut, Unity le fixe à 0,02 ou 50Hz. Vous pouvez essayer une valeur de Timestep fixe de 0,04 pour les titres mobiles visant 30 FPS. La raison en est qu'à 0,333, ce qui correspondrait à 30 FPS, il y a un risque qu'une image ait un pic dans le temps et que l'on se retrouve avec deux appels dans l'image suivante. Cela signifie qu'il faut plus de temps - et on ne peut jamais rompre le cycle d'un cadre légèrement plus long. L'utilisateur peut également définir le pas de temps maximum autorisé afin d'éviter qu'un rattrapage ne prenne plus de temps que prévu.
Cette durée Timestep affecte les scripts utilisant la fonction FixedUpdate et tous les systèmes internes d'Unity qui se mettent à jour sur l'intervalle de mise à jour fixe, par exemple, la physique et l'animation.

Dans le cadre de ce projet, seuls la physique et la Cinemachine ont fortement contribué au temps nécessaire, environ 3 ms par appel ; un appel signifie que le système a été entièrement mis à jour (bien que le fait d'être appelé 5 fois supplémentaires ait pu ajouter jusqu'à 15 ms par image de temps perdu).

Cela est dû à la lenteur des effets de post-traitement. Le fait de les désactiver réduit le temps passé, mais la recommandation précédente de réduire la fréquence fixe de Timestep afin d'éviter tout travail inutile pour l'unité centrale reste valable.
Lors de l'établissement du profil, des pics ont pu être observés dans la durée de la trame. Le suivi de ces problèmes dans la vue hiérarchique du profileur de CPU montre qu'ils proviennent de l'instanciation des PNJ.

La solution la plus courante consiste à instancier les personnages à l'avance et à les maintenir dans un état d'inactivité, dans une sorte de réserve d'objets. Ces PNJ peuvent ensuite être prélevés dans le pool sans coût d'instanciation. S'il en faut davantage, la piscine peut être agrandie en fonction des besoins.
Le même problème se pose lorsque des capacités sont utilisées, car elles instancient également des objets.

La mise en commun d'objets est le moyen le plus simple de résoudre ces problèmes. Cela peut affecter les temps de chargement, mais permet d'obtenir une fréquence d'images beaucoup plus fluide au moment de l'exécution, ce qui est le moindre des deux maux dans ce cas.
Nous avons également utilisé Arm Mobile Studio pour mieux comprendre le comportement du jeu. Grâce aux outils de Mobile Studio, nous pouvons obtenir des données de contre-activité pour le CPU et le GPU, afin de voir exactement comment le jeu utilise les ressources de l'appareil.
Vous pouvez télécharger gratuitement ARM Mobile Studio ici. Quatre outils sont inclus :
- Performance Advisor - pour générer des rapports faciles à lire et obtenir des conseils d'optimisation
- Streamline - un profileur de performance complet pour saisir toute l'activité des guichets
- Mali Offline Compiler - pour vérifier les performances d'un programme de shaders sur un GPU Mali
- Analyseur graphique - pour déboguer les appels à l'API graphique et analyser le rendu du contenu.
Le conseiller en performances nous fournit un résumé rapide des performances du jeu et est destiné à être utilisé comme un bilan de santé régulier. Il est rapide de générer un rapport, en particulier si vous l'intégrez dans un flux de travail d'intégration continue, parallèlement à votre système de construction nocturne. Le conseiller en performances nous fournit un résumé rapide des performances du jeu et est destiné à être utilisé comme un bilan de santé régulier. Il est rapide de générer un rapport, en particulier si vous l'intégrez dans un flux de travail d'intégration continue, parallèlement à votre système de construction nocturne.

Pendant les deux premières minutes du jeu, Performance Advisor nous indique que nous n'avons que 17 images par seconde en moyenne. La section verte au début du graphique d'analyse de la fréquence d'images indique où le jeu est en train de se charger, puis soudainement, le graphique devient bleu, indiquant que le jeu est lié à des fragments, et il reste ainsi tout au long de l'analyse. Cela signifie que le GPU de l'appareil a du mal à traiter les charges de travail liées aux fragments, ce qui suggère que le jeu demande trop de travail ou qu'il ne traite pas les pixels de manière efficace.
Comme nous avons ajouté des annotations de région au jeu, le graphique d'analyse de la fréquence d'images affiche nos noms de région personnalisés. Lorsque le graphique présente un marqueur étiqueté "S", Performance Advisor a pris une capture d'écran du jeu pour nous aider à identifier ce qui se passe à l'écran à ce moment-là. Vous pouvez configurer les captures d'écran pour qu'elles soient effectuées lorsque le FPS tombe en dessous d'une valeur spécifiée. Ici, comme le nombre d'images par seconde reste faible tout au long de la journée, Performance Advisor prend une capture d'écran à l'intervalle par défaut de toutes les 200 images.
Consultez le tableau des cycles par image du GPU, où nous avons ajouté un budget de 28 millions de cycles par image pour cet appareil. Nous avons estimé qu'il s'agit du nombre maximal de cycles que cet appareil devrait pouvoir gérer, tout en atteignant une fréquence d'images de 30 FPS. On constate ici que le nombre de cycles du GPU dépasse largement ce budget, et que le nombre de cycles augmente avec le temps.

Le conseiller en performances fournit des conseils d'optimisation lorsqu'il détecte un problème. Si nous examinons le graphique des cycles de shaders par image, nous constatons que le nombre de cycles du moteur d'exécution est élevé. Dans un cœur de nuanceur Mali, le moteur d'exécution est responsable du traitement des opérations arithmétiques. Performance Advisor a signalé ce problème et nous conseille de réduire les calculs dans les shaders.

Il existe une solution simple pour remédier à ce problème. Vous pouvez réduire la précision des variables du shader à mediump, plutôt qu'à highp, sans changement notable à l'écran. Cela permettra de réduire considérablement le coût des shaders. Pour plus d'informations sur la manière de procéder, reportez-vous à la section Types de données et précision des shaders dans notre documentation. De plus, comme nous l'avons découvert plus tôt avec le débogueur d'images d'Unity, le jeu est actuellement rendu à la résolution plein écran de l'appareil. Toute modification apportée pour réduire la résolution de rendu du jeu (à 1080p ou 720p) réduira également le coût de l'ombrage des fragments.
Nous avions fixé un budget de 500 000 vertices par image pour ce dispositif. Le budget est dépassé au bout d'environ 45 secondes et le nombre augmente régulièrement au fil du temps.

En examinant le graphique des primitives par image, on remarque que le nombre total de primitives traitées augmente avec le temps, même si le nombre de primitives visibles reste relativement constant. Dans les deux premières minutes du jeu, les seuls nouveaux objets créés sont les PNJ ennemis, qui sont ensuite détruits dans un éclair par notre héros. Cela suggère que lorsque les ennemis sont détruits, leur géométrie est toujours présente, même si elle n'est pas visible.

Il y a plusieurs raisons pour lesquelles le GPU peut ne pas être en mesure de répondre aux exigences du jeu, c'est pourquoi nous devons explorer davantage l'outil de profilage d'ARM avec Streamline. Streamline nous en dira plus sur cette lourde charge de travail fragmentaire, et en examinant les autres compteurs, nous pourrons trouver des indices sur la manière d'alléger la charge.
En examinant la même section du jeu dans Streamline, nous pouvons explorer une série de graphiques qui montrent l'activité du compteur GPU pour les différentes étapes du traitement de la géométrie et des pixels. Cela montre comment le contenu du jeu est traité par le GPU et s'il y a des traitements inutiles.
Les GPU basés sur la technologie Mali adoptent une approche basée sur les tuiles pour traiter les charges de travail graphiques, où l'espace de l'écran est divisé en tuiles, et où chaque tuile est traitée dans l'ordre jusqu'à son achèvement. Pour chaque tuile, le traitement géométrique est exécuté en premier, puis les pixels sont colorés lors du traitement des pixels.

Nous savons déjà que le GPU de l'appareil est au maximum de ses capacités avec les charges de travail liées aux fragments, nous devons donc chercher des moyens de réduire la pression exercée sur l'étape de traitement des pixels.
L'un des moyens de réduire la charge de traitement des pixels consiste à diminuer la complexité de la géométrie qui est envoyée au traitement des pixels. La géométrie qui est complètement hors écran ou qui fait face au fond de l'écran est éliminée avant le traitement des pixels, mais les petits triangles qui ne couvrent que partiellement les carrés de 2×2 pixels peuvent nuire à l'efficacité des fragments et ont un coût élevé en termes de bande passante par pixel de sortie.
Les graphiques Mali Geometry Usage et Mali Geometry Culling Rate de Streamline montrent l'efficacité avec laquelle le GPU traite la géométrie. Nous pouvons voir le nombre de primitives envoyées au GPU et le nombre d'entre elles qui sont éliminées lors du traitement de la géométrie. Les travaux qui sont éliminés à ce stade ne seront pas transmis au traitement des pixels. C'est une bonne nouvelle, mais nous pourrions organiser le contenu de manière plus efficace, de sorte que les primitives non visibles ne soient pas transmises du tout.

Dans le graphique de l'utilisation de la géométrie au Mali, nous pouvons voir que 1,07 million de primitives entrent dans le traitement géométrique (ligne orange) dans le délai sélectionné (environ 0,05 seconde), mais que 700 000 primitives sont éliminées à ce stade (ligne rouge).
Le tableau du taux d'abattage de la géométrie malienne montre pourquoi ils sont abattus. Environ la moitié d'entre eux sont éliminés par le test d'orientation (ligne orange), ce qui est normal puisqu'il s'agit des triangles orientés vers l'arrière de nos objets 3D. Ce qui est plus inquiétant, c'est que 31,9 % des primitives sont éliminées par le test de l'échantillon (ligne violette) - idéalement, ce chiffre devrait être inférieur à 5 %. Le test d'échantillonnage indique que ces primitives étaient trop petites pour être tramées, n'atteignant pas un seul point d'échantillonnage, et qu'elles étaient donc considérées comme invisibles. Cela peut se produire lorsque des objets au maillage complexe sont placés loin de la caméra et que les triangles du maillage sont trop petits pour être visibles. Des chiffres plus élevés peuvent indiquer que les mailles de l'objet du jeu sont trop complexes pour leur position à l'écran.
Ce problème s'aggrave pour les primitives qui sont suffisamment grandes pour passer le test de l'échantillon mais qui ne couvrent que quelques pixels. Ces "microtriangles" sont transmis au traitement des pixels et leur traitement est coûteux. En effet, lors de l'ombrage de fragments, les triangles sont tramés en parcelles de deux pixels sur deux, appelées "quads". Les petits triangles ne touchent qu'un sous-ensemble de pixels à l'intérieur d'un quadrilatère, mais le quadrilatère entier doit être envoyé pour traitement. Cela signifie que le fragment shader fonctionnera avec des voies inactives dans le matériel, ce qui rendra l'exécution du shader moins efficace.

Pour vérifier si nous avons un problème avec les microtriangles, nous pouvons utiliser le tableau des propriétés de la charge de travail de Mali Core dans Streamline pour contrôler l'efficacité de la couverture. Idéalement, ce pourcentage devrait être inférieur à 10 %. On constate ici que dans certaines sections, le taux de couverture partielle (ligne verte) est très élevé, supérieur à 70 %. Cette valeur indique que le contenu présente une forte densité de microtriangles, ce qui confirme le problème signalé précédemment par le taux élevé d'élimination des échantillons.

La géométrie qui se retrouve à l'écran doit être dimensionnée en fonction de sa position. Un élément de décor complexe et éloigné n'a pas besoin d'être très détaillé, car il ne contribue pas beaucoup à la scène. Nous pourrions utiliser des maillages de niveau de détail (LOD)pour les objets qui sont plus éloignés de la caméra, afin de réduire la complexité et d'économiser de la puissance de traitement et de la bande passante DRAM. Ou bien, au lieu d'utiliser la géométrie, nous pourrions utiliser des textures et des cartes normales pour créer des détails de surface pour les objets.
Grâce au rapport du Performance Advisor, nous avons découvert que nos shaders étaient peut-être trop coûteux et que nous aurions intérêt à réduire leur précision. Dans Streamline, nous pouvons utiliser le tableau d'utilisation variable de Mali pour voir le nombre de cycles où l'interpolation 32 bits (haute précision) ou 16 bits (moyenne précision) est active. On constate ici que l'interpolation 32 bits est utilisée dans la plupart des cycles. Les variables de 16 bits interpolent deux fois plus vite que les variables de 32 bits et utilisent la moitié de l'espace dans les registres du nuanceur pour stocker les résultats de l'interpolation. Il est donc recommandé d'utiliser autant que possible des entrées variables de taille moyenne (16 bits) pour les nuanceurs de fragment.

Pour explorer les shaders, nous pouvons utiliser l'outil de compilation statique hors ligne d'ARM Mobile Studio, afin de générer une analyse rapide du programme de shaders.
Pour ce faire, vous devez récupérer le code des shaders dans le fichier compilé que vous donne Unity, puis lancer Mali Offline Compiler sur ce fichier :
1. Dans Unity, sélectionnez le shader que vous souhaitez analyser, soit directement dans votre dossier d'actifs, soit en sélectionnant un matériau, en cliquant sur l'icône d'engrenage et en choisissant Sélectionner le shader.
2. Choisissez Compiler et affichez le code dans l'inspecteur. Le code compilé s'ouvre dans votre éditeur de code par défaut. Ce fichier contient plusieurs variantes du code des shaders.
3. Copiez une variante de nuanceur de sommets ou de fragments de ce fichier dans un nouveau fichier et donnez-lui une extension.vert ou .frag. Les nuanceurs de sommet commencent par #ifdef VERTEX et les nuanceurs de fragment par #ifdef FRAGMENT. Ils se terminent par leur #endif respectif. (N'incluez pas les déclarations #ifdef et #endif dans le nouveau fichier).
4. Dans un terminal de commande, exécutez Mali Offline Compiler sur ce fichier, en spécifiant le GPU que vous voulez tester. Par exemple : malioc -c Mali-G72 myshader.frag Reportez-vous à la section Démarrer avec Mali Offline Compiler pour plus d'instructions.
Nous avons choisi d'analyser le fragment shader responsable de l'effet de dissolution qui se produit lorsque les PNJ ennemis meurent. Voici le rapport de Mali Offline Compiler, avec les sections intéressantes mises en évidence :

Nous constatons que seuls 2 % des calculs arithmétiques sont effectués efficacement avec une précision de 16 bits. Le shader fonctionnera plus efficacement si nous réduisons la précision de highp à mediump. Cela permet de réduire la consommation d'énergie et la pression du registre, et de doubler les performances. Dans certaines situations, une précision élevée est toujours nécessaire, par exemple pour les calculs de position et de profondeur, mais dans de nombreux cas, la différence à l'écran est peu perceptible lorsque l'on réduit la précision à une précision moyenne.
Le rapport fournit une ventilation approximative du coût du cycle pour les principales unités fonctionnelles du cœur du nuanceur de Mali. On constate ici que l'unité arithmétique est la plus utilisée.
Dans la section des propriétés du shader, nous voyons que ce shader contient un calcul uniforme qui ne dépend que de constantes littérales ou de valeurs uniformes. Cela produit le même résultat pour chaque thread dans un appel de dessin ou une distribution de calcul. Idéalement, ce type de calcul uniforme devrait être transféré dans la logique d'application de l'unité centrale.
Nous pouvons également constater que le nuanceur peut modifier le masque de couverture des fragments qui détermine quels points d'échantillonnage de chaque pixel sont couverts par un fragment, en utilisant l'instruction discard pour éliminer les fragments en dessous d'un seuil alpha. Les nuanceurs dont la couverture est modifiable doivent utiliser une mise à jour ZS tardive, ce qui peut réduire l'efficacité des tests ZS précoces et de l'ordonnancement des fragments pour les fragments ultérieurs à la même coordonnée. Dans la mesure du possible, il convient de réduire l'utilisation des instructions de rejet et de l'alpha-to-coverage dans les nuanceurs de fragment. Reportez-vous au guide des meilleures pratiques de l'ARM pour obtenir des conseils sur l'utilisation des déclarations de rejet.
Dans l'analyseur graphique d'ARM Mobile Studio, vous pouvez voir tous les appels à l'API graphique effectués par l'application et les parcourir un par un pour voir comment la scène est construite. Cela permet d'identifier les objets trop complexes par rapport à leur taille à l'écran et à la distance qui les sépare de la caméra. Voici quelques exemples que nous avons trouvés dans ce jeu :
La maçonnerie située dans le coin le plus éloigné de la scène est construite à l'aide de la géométrie et utilise 2064 sommets. Le détail n'est pas extrêmement visible dans le résultat final, il s'agit donc d'un traitement inutile.

Nous avons constaté le même problème pour les dalles de sol, qui comportent chacune 1170 sommets, mais même si l'objet est proche de la caméra, la scène ne bénéficie pas vraiment de cette complexité. Il serait plus efficace d'utiliser une carte de normalité ici, pour représenter les bosses et les arêtes angulaires, plutôt que de la construire avec des triangles. En outre, nous pouvons voir que ces objets sont dessinés à l'aide d'appels distincts. La réduction du nombre d'appels au dessin en regroupant les objets ou en utilisant l'instanciation d'objets peut améliorer les performances.

Un autre exemple est celui des statues à l'arrière de la scène - 6966 sommets chacune. Vous pouvez voir que le maillage est assez complexe, ce qui donnera un excellent résultat visuel lorsque le joueur s'approchera des statues, mais de cette position de la caméra, elles sont à peine perceptibles. L'utilisation de Mesh LOD pour représenter ces objets à une telle distance de la caméra permettrait d'économiser beaucoup de puissance de traitement.

N'oubliez pas que la réduction de la complexité pour de nombreux objets similaires se traduit par une économie considérable dans le traitement de la géométrie, ce qui réduit par la suite la quantité d'ombrage des fragments nécessaire. Cela permettra non seulement de réduire la charge de travail des fragments et d'augmenter le nombre d'images par seconde, mais aussi de réduire l'empreinte d'installation de l'APK.
Nous avons découvert plusieurs domaines dans lesquels nous pourrions apporter des modifications au jeu afin d'en améliorer les performances. Voici celles que nous avons choisi de mettre en œuvre, et comment nous l'avons fait.
Fixed Timestep est un intervalle indépendant de la fréquence des images qui contrôle le moment où les calculs physiques et les événements FixedUpdate() sont effectués. Par défaut, cette fonction est réglée pour fonctionner à 50 FPS. Si les 50 voire 60 FPS sont supportables sur les appareils mobiles haut de gamme, les appareils plus grand public tournent à 30 FPS, ce que vise ce titre. Allez dans Édition > Paramètres du projet, puis dans la catégorie Temps, pour définir la propriété de l'intervalle de temps fixe à 0,04. Cela permet de s'assurer que les calculs de physique, FixedUpdate() et les mises à jour sont tous synchronisés.

Après les ajustements apportés au Timestep fixe dans Unity, la partie mise à jour fixe de la boucle principale du jeu n'était appelée qu'une fois par image, pour une moyenne de 1,5 ms. Il s'agit d'une amélioration considérable par rapport aux 12 ms qu'il fallait auparavant, et d'une solution simple à un problème de performance courant.
Au démarrage de l'application, les données de tous les objets référencés par les scènes intégrées ou dans le dossier Resources sont chargées dans le cache d'ID d'instance. Ces actifs sont traités comme un grand ensemble d'actifs, de sorte que les métadonnées et les informations d'indexation sont toujours chargées en mémoire. Une fois qu'un bien de ce lot est utilisé, il ne peut plus être déchargé de la mémoire.
La méthode recommandée pour gérer les actifs et les ressources dans le but d'améliorer votre consommation de mémoire est le système d'actifs adressables, qui offre un moyen efficace de décharger de la mémoire le contenu qui n'est plus nécessaire.
Dans notre environnement, de nombreux objets apparaissent plusieurs fois. Les murs, les carreaux de sol et les autres accessoires de l'environnement sont tous dupliqués pour construire cette scène. Nous pouvons économiser des appels de dessin en activant l'instanciation GPU sur le matériau des objets. L'instanciation GPU rend des maillages identiques avec un petit nombre d'appels de dessin, et permet à chaque instance d'avoir des paramètres différents, tels que la couleur ou l'échelle. Cette modification peut améliorer les performances de l'unité centrale. Ci-dessous, vous pouvez voir les données de Performance Advisor avant que l'instanciation du GPU ne soit activée.

Et ici, vous pouvez voir la même partie de l'application, mais avec l'instanciation GPU activée - un gain petit mais mesurable vers notre objectif de 30 fps.

Les textures de rendu sont un moyen d'ajouter des éléments 3D à votre interface utilisateur, ainsi que de nombreux autres cas d'utilisation. Si vous avez une caméra qui effectue un rendu dans la texture de rendu, veillez à désactiver la caméra lorsqu'elle n'est pas à l'écran. Il n'est pas nécessaire de rendre quelque chose que l'utilisateur ne verra pas. Utilisez l'analyseur graphique ou le débogueur de trames d'Unity pour vous assurer que ces textures ne sont pas mises à jour en dehors de l'écran.
Plutôt que d'alourdir la charge de travail de l'unité centrale en créant et en détruisant sans cesse les mêmes objets, essayez la mise en commun d'objets. La mise en commun d'objets est un modèle de conception qui vous invite à créer les objets dont vous aurez besoin à l'avance, en chargeant le travail de l'unité centrale. Ensuite, plutôt que de les détruire, vous pouvez les ajouter au pool pour qu'ils soient réutilisés lorsqu'un objet du même type est à nouveau nécessaire. Il s'agit d'un moyen fantastique de soulager la puissance de traitement de l'unité centrale, afin qu'elle puisse se consacrer à des tâches plus importantes pour votre jeu.
Avec le passage au pooling d'objets, il n'y a pas de pic lié aux vagues d'ennemis apparaissant à l'écran que l'on peut identifier dans les captures d'Unity Profiler, ni d'effet perceptible sur le taux d'images par seconde.
Lorsqu'un maillage est à l'écran, le GPU passe du temps à rendre tous les triangles du maillage, quelle que soit leur taille. Dans les jeux où la caméra ou les objets peuvent se déplacer, cela crée souvent une situation où vous pouvez utiliser une grande partie des ressources du GPU pour rendre des triangles de maillages qui sont trop petits pour être vus dans l'image. Pour y remédier, utilisez des maillages de niveau de détail (LOD). Cela permet à votre jeu d'utiliser des maillages moins complexes lorsque la caméra s'éloigne des objets, ce qui diminue la complexité du maillage que le GPU doit rendre et réduit le nombre de vertex par image, ce qui donne des triangles plus grands à traiter par pixel. Cela permet non seulement d'améliorer l'efficacité, mais aussi de conserver l'intégrité artistique de la scène.

Pour obtenir d'autres conseils sur l'optimisation des ressources, n'oubliez pas de consulter les guides de l'artiste de jeu d' ARM.
Lorsque vous savez que certains éléments ayant les mêmes propriétés matérielles seront utilisés dans la même scène, vous pouvez les regrouper par lots. Combiner leurs données de texture dans un atlas de texture unique, ce qui permettra d'économiser les appels de dessin en les dessinant en une seule fois, et de réduire l'encombrement lors de la compression, par rapport à plusieurs fichiers distincts.
Lorsque vous écrivez vos propres shaders personnalisés ou que vous utilisez Shader Graph, vous pouvez décider de la précision à utiliser : float ou half. Le choix de la moitié, dans la mesure du possible, permet d'obtenir des shaders plus performants - mais n'oubliez pas que vous devrez probablement utiliser des flottants pour tout ce qui concerne les positions dans l'espace mondial ou les calculs de profondeur !

Lorsque vous commencez à planifier les effets de post-traitement pour votre projet, vous avez le choix entre deux options : l'ancien jeu de fonctionnalités Integrated ou le nouveau jeu de fonctionnalités Post Processing v2. Ci-dessous, vous pouvez voir le jeu utilisant l'ensemble des fonctionnalités intégrées.

Toutes les 3-4 images, nous voyons un pic dans V-Sync, où le système attend que l'image soit rendue. Cela fait chuter le jeu en dessous de 30 FPS, de manière constante, et gaspille de l'énergie sur l'appareil. Ici, cependant, vous pouvez voir les données du profileur du jeu en utilisant les mêmes effets, cette fois-ci, avec l'ensemble des fonctionnalités de Post Processing v2.

Ce graphique de profilage est bien meilleur, car Post Processing v2 est optimisé pour fonctionner sur du matériel mobile. Utilisez-le dans votre projet pour obtenir les meilleures performances en matière de post-traitement.
L'ajout d'effets de post Processing à votre jeu peut ajouter une belle couche de polissage et de profondeur visuelle à votre projet. Mais il est également important de trouver un équilibre entre ces effets et les performances. En effet, ces effets peuvent coûter cher. La désactivation de ces fonctions sur les appareils grand public permet d'économiser beaucoup d'énergie et d'éviter que l'appareil ne chauffe dans les mains des joueurs.
Une fois les autres optimisations mises en place, nous pouvions encore observer des pics dans certains domaines. En utilisant la recherche binaire, en activant et en désactivant des éléments, nous avons fini par trouver deux choses : L'une d'entre elles était la pile de post-traitement utilisée. Cela a permis de réduire le temps total, mais le taux de rafraîchissement s'est finalement stabilisé une fois l'anticrénelage désactivé, à tel point qu'une partie du Post Processing a pu être maintenue, même sur les appareils les moins performants que nous avons utilisés pour les tests.

Après avoir optimisé le jeu, nous l'avons de nouveau fait passer par Arm Mobile Studio, afin de repérer d'éventuelles différences. Le rapport du conseiller en performances montre maintenant que nous avons atteint une moyenne de 28,9 FPS (contre 17 auparavant), et réduit le nombre global de fragments. L'activité des fragments est encore élevée dans certaines sections du jeu, nous avons donc encore du travail à faire, mais avec de bonnes données pour guider nos recherches, nous devrions être en mesure d'optimiser ces sections afin d'améliorer encore les performances.

Le nombre de vertices par image est maintenant bien en dessous de notre budget de 500 000, et vous pouvez voir des creux réguliers lorsque les PNJ ennemis sont détruits.

L'utilisation de la géométrie et le culling sont désormais beaucoup plus efficaces, le nombre de primitives visibles représentant un pourcentage beaucoup plus sain du nombre de primitives en entrée. Le test de la face est responsable d'environ 50 % des primitives éliminées, comme prévu, et les primitives éliminées par le test de l'échantillon sont inférieures à 10 %, ce qui montre que nous avons réduit le nombre de très petits triangles.

En utilisant le Profiler et le Frame Debugger d'Unity, ainsi qu'Arm Mobile Studio, nous avons pu découvrir de multiples façons d'améliorer les performances et de réduire la pression exercée sur le CPU et le GPU d'un appareil mobile. Certains des problèmes que nous avons découverts pourraient être évités pour les prochains titres, en respectant un ensemble de bonnes pratiques en matière de contenu.
Bien entendu, nous ne voulons pas que les optimisations réduisent la qualité des images à l'écran. Voici comment la version optimisée du jeu se présente par rapport à la version originale.
Les tests de performance interviennent souvent assez tard dans le cycle de développement. C'est une bonne chose de trouver d'autres possibilités d'optimisation, mais que se passe-t-il si vous n'avez pas le temps de résoudre les problèmes avant la date limite de publication ? Il est beaucoup plus pratique de concevoir le contenu de manière optimale dès le départ. Il peut être utile de fixer des budgets de contenu en fonction de la complexité des maillages, de la complexité des shaders et de la compression des textures, afin de donner à votre équipe les meilleures chances de concevoir efficacement pour les mobiles. Voici quelques ressources qui pourraient aider votre équipe :
- Guide ARM pour les développeurs Unity
- Guides du développeur pour les meilleures pratiques sur le Mali
- Cours d'apprentissage Unity Learn, Optimisation de l'art 3D pour les applications mobiles
Une fois que vous savez que la plupart de vos applications et actifs respectent un ensemble de bonnes pratiques, vous pouvez effectuer des tests de performance réguliers tout au long de votre cycle de développement, afin de détecter les problèmes suffisamment tôt pour les résoudre.
Les équipes qui utilisent un système d'intégration continue peuvent profiter des tests de performance automatisés, disponibles avec l'édition professionnelle d'ARM Mobile Studio. Cette édition peut être exécutée sur plusieurs appareils dans une ferme d'appareils, et permet d'éviter les tâches fastidieuses de profilage manuel. Les données rapportées peuvent même être introduites dans n'importe quelle base de données compatible JSON, afin que vous puissiez créer des tableaux de bord visuels et des alertes pour surveiller l'évolution des performances dans le temps et détecter les problèmes plus rapidement.
Le profileur intégré à Unity est un excellent point de départ. Pour savoir comment profiler votre application, consultez la documentation d'Unity. Vous pouvez également explorer Frame Debugger, qui vous permet d'étudier la construction d'un cadre individuel.
Téléchargez gratuitement Arm Mobile Studio sur le site web d'Arm Developer et consultez les guides de démarrage pour Performance Advisor, Streamline, Mali Offline Compiler et Graphics Analyzer, afin d'être rapidement opérationnel.
Pour obtenir de l'aide supplémentaire sur le profilage avec le Profiler et le Frame Debugger d'Unity, n'hésitez pas à poser des questions dans notre forum.
Pour obtenir une assistance supplémentaire lorsque vous travaillez avec des appareils Mali ou Arm Mobile Studio, rendez-vous sur le forum Graphics and Gaming d'Arm, où vous pouvez poser des questions et où Arm se fera un plaisir de vous aider.
