Ce document explique comment optimiser les performances d'un jeu en utilisant des outils pour identifier et résoudre les goulots d'étranglement du processeur et du GPU.
Optimisation du processeur
Si l'analyse montre que le jeu est limité par le processeur, il est essentiel de poursuivre l'investigation. Pour ce faire, vous devez identifier les threads ou les API spécifiques qui provoquent des goulots d'étranglement et réduisent le nombre de FPS.
Pour l'optimisation du processeur, une solution universelle n'est généralement pas efficace. Au lieu de cela, vous devez identifier la charge de travail la plus exigeante en fonction du jeu ou de la scène, puis optimiser la logique et les fonctions concernées.
Outils de trace de timing du moteur de jeu
Les outils suivants peuvent vous aider à effectuer cette analyse :
Des insights incroyables
Dans les projets Unreal Engine, l'outil Unreal Insight facilite l'analyse des informations de trace temporelle pour les threads individuels qui composent un frame.
À titre d'illustration, GameThread utilise généralement la plus grande partie du temps CPU, principalement attribuable au temps de tick. De plus, une partie importante du temps de tic est consommée par les tâches associées à FActorComponentTickFunction.
Pour optimiser FActorComponentTick, il est impératif d'exclure les calculs et d'implémenter le culling pour les personnages et les objets positionnés en dehors du champ de vision de la caméra. De plus, l'utilisation d'animations basées sur le niveau de détail (LOD, Level of Detail) peut améliorer davantage les performances.
Profileur Unity (Unity)
L'analyse à l'aide du profileur Unity révèle que le thread principal consomme plus de 45 ms, avec PostLateUpdate.FinishFrameRendering qui occupe 16,23 ms, ce qui en fait l'opération la plus gourmande en temps. Dans ce contexte, plusieurs appels d'Inl_RenderCameraStack sont observés. Il est conseillé de vérifier si les caméras activées sont nécessaires et de les optimiser en conséquence.
Outils de profilage au niveau du système
Utilisez les outils de profilage suivants :
Perfetto
À l'aide de la trace Perfetto, vous pouvez déterminer les affectations de cœur de processeur et les détails d'exécution de chaque thread sur un appareil Android. Cela vous permet d'identifier les goulots d'étranglement des performances en analysant les données d'exécution des threads.
Cas de surcharge du processeur
La trace indique que la charge de travail sur les threads GameThread et RenderThread provoque des retards dans la file d'attente QueuePresent du thread RHI, ce qui entraîne un scénario lié au processeur, basé sur VSync.
Cas de surcharge du GPU
La trace indique que l'exécution du GPU lui-même dépasse 25 ms, ce qui signifie qu'il s'agit d'un scénario lié au GPU.
Simpleperf
Pour identifier les fonctions qui utilisent le plus de ressources du processeur, vous pouvez utiliser simpleperf. Pour obtenir des résultats optimaux, nous vous recommandons de trier ces fonctions afin de prioriser et de traiter en premier celles qui sont le plus utilisées.
Simpleperf vous aide à examiner les données sur les fonctions qui utilisent le plus de temps CPU. Pour optimiser l'utilisation du processeur, commencez par les fonctions qui en consomment le plus. Dans cet exemple, USkeletalMeshComponent, qui est associé à l'animation dans ActorComponentTickFunctions, utilise le plus de ressources du processeur.
Optimisation du GPU
Si l'analyse montre que le jeu est lié au GPU, une investigation plus approfondie est essentielle. Pour cela, vous devez utiliser différents outils et techniques d'optimisation et d'analyse des GPU.
Pour optimiser le GPU, utilisez un débogueur de frames afin d'analyser le pipeline de rendu et les appels de dessin pour chaque scène. De plus, vous devez bien comprendre l'architecture GPU et le comportement du pipeline pour identifier les opérations inutiles ou les zones à optimiser.
Les sections suivantes expliquent les méthodes et les outils d'optimisation des GPU.
Éliminer les RenderPasses inutiles
Pour améliorer les performances d'affichage et réduire la charge de travail du GPU, éliminez les passes de rendu inutiles. Cela inclut tout pass de rendu qui ne comporte pas d'appels de dessin ou dont la sortie n'est pas utilisée dans le frame final.
Utilisez un débogueur de GPU, tel que RenderDoc, pour analyser le pipeline de rendu et identifier les possibilités d'optimisation.
Aucun appel de dessin : vérifiez si la passe de rendu inclut des appels de dessin. S'il n'y a pas d'appels de dessin, supprimez le pass.
Sortie inutilisée : vérifiez si les passes suivantes accèdent aux sorties de la passe de rendu (couleur ou profondeur, par exemple) ou les affichent. Si ce n'est pas le cas, supprimez-le.
Cartes fusionnables : identifiez les cartes que vous pouvez fusionner :
- Même framebuffer ou mêmes pièces jointes
- Opérations de chargement ou de stockage compatibles
- Aucune barrière de dépendance entre les deux
Minimiser les opérations de chargement ou de stockage
Les opérations de chargement ou de stockage sont gourmandes en ressources, car elles utilisent beaucoup de mémoire.
Réduisez au minimum les opérations de chargement et de stockage inutiles. N'effectuez ces actions que lorsque des pièces jointes sont requises dans un RenderPass. Sinon, remplacez-les par des opérations Clear ou Don't care pour réduire la surcharge.
Optimiser les performances
Utilisez un débogueur de GPU, tel que RenderDoc, pour analyser le pipeline de rendu et identifier les opportunités d'optimisation suivantes :
Chargement : si une pièce jointe de passe de rendu n'utilise pas les données d'une passe ou d'une pièce jointe précédente, une opération de chargement n'est pas nécessaire. Dans ce cas, l'utilisation de
Don't careouClearpeut réduire la surcharge.Store : si une pièce jointe de passe de rendu n'est pas utilisée après la passe de rendu actuelle, l'opération de stockage est inutile. Dans ce cas, utilisez
Don't careouClear.Remplacer : détermine si les paramètres de chargement ou de stockage actuels peuvent être remplacés par
ClearouDon't Caresans affecter le frame final.
Éviter la suppression pour activer Early-Z
Early-Z améliore les performances sur les plates-formes mobiles. Toutefois, une instruction discard dans un nuanceur désactive automatiquement Early-Z. Si l'instruction discard
n'est pas essentielle, supprimez-la.
Accélération Early-Z
Cette optimisation réduit considérablement les opérations du nuanceur de fragments et améliore les performances du GPU.
Early-Z Test de profondeur et de stencil
Optimiser les performances
Utilisez un débogueur de GPU, tel que RenderDoc, pour analyser le pipeline de rendu et identifier les opportunités d'optimisation suivantes :
Utilisation de
discarddans les nuanceurs de fragment : le mot clédiscardempêche le GPU d'effectuer des tests de profondeur anticipés, car la visibilité du fragment n'est pas connue à l'avance.Modification de
gl_FragDepth: la modification dynamique degl_FragDepthchange la profondeur d'un fragment, ce qui désactive l'optimisation Early-Z, car la profondeur finale est inconnue avant le traitement du fragment.Alpha-to-coverage activé : lorsque l'alpha-to-coverage est activé (souvent utilisé dans le rendu MSAA), la couverture des fragments dépend des valeurs alpha. Cela peut retarder les tests de profondeur et désactiver Early-Z.
Optimiser le format de texture
La sélection optimale du format de texture réduit la consommation de mémoire, améliore l'efficacité de la bande passante et améliore les performances de rendu. L'utilisation de formats de précision excessivement élevée peut gaspiller des ressources GPU sans apporter d'avantages visuels.
Optimiser les performances
Utilisez un débogueur de GPU, tel que RenderDoc, pour analyser le pipeline de rendu et identifier les opportunités d'optimisation suivantes :
- Utilisez
D24S8au lieu deD32S8pour les tampons de profondeur et de stencil : l'utilisation deD24S8pour les tampons de profondeur et de stencil réduit la consommation de mémoire de 20 % par rapport àD32S8, avec peu ou pas de différence notable dans la qualité visuelle pour la plupart des applications. - Utilisez la compression
ASTCpour les textures de couleur : la compressionASTCréduit considérablement l'utilisation de la mémoire de texture (jusqu'à huit fois moins par rapport aux formats non compressés) tout en préservant une qualité visuelle élevée. - Utilisez des formats demi-précision flottante plutôt que des formats pleine précision flottante : utilisez
R16FouRG16Fpour réduire la bande passante mémoire et la consommation de stockage. Ces formats sont bien adaptés aux tampons de post-traitement.
Optimiser la complexité de la géométrie
La réduction de la complexité géométrique améliore les performances de rendu, en particulier sur les appareils mobiles dont les capacités de GPU sont limitées. Cela implique d'utiliser un nombre réduit de sommets et de triangles, de consolider les objets pour diminuer les appels de dessin et d'éliminer la géométrie non rendue ou inutile. Des techniques telles que la simplification du maillage, le niveau de détail (LOD) et le culling de frustum ou d'occlusion peuvent réduire considérablement la charge de travail du GPU et augmenter la fréquence d'images.
Optimiser les performances
Utilisez des outils de profilage et des débogueurs de GPU, tels que RenderDoc, Android GPU Inspector ou d'autres analyseurs de performances, pour identifier les goulots d'étranglement liés à la géométrie.
Réduisez le nombre de triangles : minimisez l'utilisation de polygones, en particulier pour les objets petits ou éloignés.
Utilisez le niveau de détail (LOD) : des maillages plus simples sont utilisés automatiquement en fonction de la distance de la caméra.
Fusionner les petits maillages : consolidez les objets statiques pour réduire les appels de dessin et la surcharge du processeur.
Écrêtage du frustum et de l'occlusion : évitez d'afficher les objets qui se trouvent en dehors du champ de vision ou qui sont masqués par d'autres éléments.
Supprimer les pièces jointes inutiles
Les pièces jointes du pass de rendu (par exemple, couleur, profondeur, stencil) consomment de la bande passante mémoire et des ressources GPU, même si elles ne sont pas utilisées. La suppression des pièces jointes inutiles ou redondantes améliore les performances et réduit la consommation d'énergie, en particulier sur les plates-formes mobiles.
Optimiser les performances
Utilisez des outils de profilage et des débogueurs GPU, tels que RenderDoc, Android GPU Inspector ou d'autres analyseurs de performances, pour identifier les goulots d'étranglement liés à la géométrie.
- Vérifiez l'utilisation réelle : y a-t-il des appels de dessin ou des nuanceurs qui écrivent dans l'attachement ou le lisent ?
- Analysez la sortie du frame : utilisez
RenderDocou des utilitaires comparables pour déterminer si la pièce jointe contribue à l'image finale. - Envisagez d'utiliser des pièces jointes transitoires ou fictives : les pièces jointes transitoires ou une opération de stockage "Don't Care" doivent être utilisées pour les données temporaires qui ne nécessitent pas de stockage persistant.
Optimiser la précision des nuanceurs
L'utilisation d'une précision excessivement élevée (par exemple, highp au lieu de mediump ou lowp) dans les nuanceurs augmente la charge de travail du GPU, la consommation d'énergie et la pression des registres, en particulier sur les GPU mobiles. En utilisant la précision adéquate la plus faible pour les variables (par exemple, les positions, les couleurs, les UV), vous pouvez améliorer les performances sans impact visuel perceptible.
Optimiser les performances
Utilisez des outils de profilage et des débogueurs GPU tels que RenderDoc, Android GPU Inspector ou d'autres analyseurs de performances pour identifier les goulots d'étranglement liés à la géométrie.
Examinez le code du nuanceur : évaluez les variables du nuanceur et vérifiez que la haute précision n'est utilisée que lorsque cela est nécessaire, par exemple pour les calculs de profondeur ou d'espace écran. Utilisez une précision moyenne ou faible pour les couleurs, les coordonnées UV ou les valeurs qui ne nécessitent pas une grande précision.
Utilisez des débogueurs de GPU : les utilitaires de diagnostic, tels que RenderDoc ou les profileurs de GPU mobiles (AGI, Mali/GPU Inspector, par exemple), identifient l'utilisation élevée des registres ou les blocages de nuanceurs associés à des problèmes de précision.
Activer la suppression des faces arrière
Il est souvent inutile d'afficher les triangles qui ne sont pas orientés vers la caméra (faces arrière) pour les objets solides.
Optimiser les performances
L'utilisation de VK_CULL_MODE_NONE peut avoir un impact négatif sur les performances, car elle oblige le GPU à afficher les faces avant et arrière, ce qui augmente la charge de travail de rendu.
Réduire les superpositions dans les scènes d'UI
Éliminez les appels de dessin et les passes de rendu inutiles, en particulier dans les scènes d'UI, pour améliorer les performances de rendu et réduire la charge de travail du GPU. Par exemple, dans une scène d'UI où le monde entier est rendu avant de superposer l'UI sur l'écran, le rendu du monde devient redondant.
Optimiser les performances
Utilisez un débogueur de GPU, tel que RenderDoc, pour analyser le pipeline de rendu et identifier les opportunités d'optimisation suivantes :
- Vérifiez l'absence de surtirage superflu. Dans les contextes d'interface utilisateur, où l'intégralité de l'écran peut être affichée, vérifiez que les passes de rendu précédentes ne sont pas inutilement surdessinées.
- Activez les tests de profondeur et le tri pour optimiser les performances.
- Envisagez d'afficher les éléments de l'avant vers l'arrière.