Optimisation du rendu CPU de Godot : comment le profilage a transformé deux goulots d'étranglement en un gain sérieux
Public- 15 Sep, 2026

De l'extérieur, l'optimisation du rendu ressemble souvent à de la magie noire, mais en réalité le processus est bien plus méthodique qu'il n'y paraît. Les développeurs de Godot ont expliqué en détail comment ils optimisent le code CPU du moteur de rendu, à l'aide d'exemples concrets de l'année écoulée. Tout le travail repose sur un principe clé : tout moteur doit équilibrer la charge entre le CPU et le GPU, car la performance finale est toujours dictée par le plus lent des deux. Aucun réglage du CPU ne compense des shaders inefficaces. L'équipe du moteur fait constamment de tels compromis — par exemple, le batching 2D dégrade légèrement la performance du GPU, mais améliore fortement celle du CPU, et comme les jeux 2D atteignent généralement les limites du CPU en premier, le batching est presque toujours un gain net. Les jeux 3D, à l'inverse, sont le plus souvent limités par le GPU, c'est pourquoi Godot effectue l'occlusion culling sur le CPU pour soulager le GPU. De plus, toute optimisation économise la batterie sur les plateformes à alimentation limitée.
Le cycle de base de l'optimisation CPU est simple : identifier le goulot d'étranglement, comprendre le fonctionnement du système, apporter une modification puis mesurer à nouveau. Re-mesurer est important, car les optimisations se comportent souvent de manière contre-intuitive. Ajouter une branche à l'intérieur d'une boucle pour sauter un travail superflu semble plus rapide, mais cela peut bloquer la vectorisation du compilateur et tout ralentir. De même, mettre en cache une valeur pour éviter des calculs redondants peut produire l'effet inverse si le vrai goulot d'étranglement est la lecture en mémoire : dans ce cas, faire deux fois le travail se révèle plus rapide. Trouver le coupable est l'étape la plus difficile, et elle commence par un profileur. Godot en intègre deux : l'un pour GDScript, l'autre pour le moteur de rendu. En revanche, pour le code du moteur lui-même, les développeurs ont besoin d'un outil externe. L'article utilise Superluminal — un profileur par échantillonnage qui se connecte à une instance Godot en cours d'exécution, enregistre les données au fil du fonctionnement du moteur, montre quel code s'exécute à quel moment, et a récemment gagné le support de Linux. Godot prend également en charge la compilation avec Tracy pour le profilage basé sur le traçage.
Le premier exemple est né d'une conversation avec un développeur du jeu Heidi's Legacy: Mountains Calling, qui a constaté que le code d'animation de Godot était si lent que le contenu a dû être limité à environ vingt personnages animés. La configuration impliquait un AnimationPlayer animant les sommets d'un Polygon2D affiché sur un Viewport au-dessus d'un Sprite3D — une technique inhabituelle mais pas coûteuse en soi. Le profilage d'une reproduction minimale (testée sur un Ryzen 5 9600X) a immédiatement révélé le coupable. La classe Polygon2D passait l'essentiel de son temps à libérer l'ancienne surface du maillage et à en allouer une nouvelle à chaque image, car les données des sommets changent sur le CPU à chaque image. Le graphe d'appels et l'onglet « Source and Disassembly » ont pointé les lignes exactes qui reconstruisaient le maillage interne, confirmant ce remue-ménage inutile.
La correction a exploité une API de bas niveau de Godot pour mettre à jour manuellement les données des sommets sur place. Lorsque le nombre de sommets reste constant, il n'est pas nécessaire de libérer et de réallouer le maillage — il suffit de charger les nouvelles données de sommets dans le maillage existant. L'essentiel de la pull request consistait à décider quels cas étaient sûrs pour une mise à jour sur place et lesquels nécessitaient réellement une recréation. Résultat : les performances des Polygon2D animés ont environ triplé, le temps d'image passant de 35 ms à 13 ms, et la scène passant de 28 FPS à 83 FPS. Après le changement, le rendu représentait environ 50 % du temps d'image, l'animation environ 30 %, et la mise à jour du Polygon2D 20 %. L'auteur a noté que des gains supplémentaires sont possibles — ne réimporter que les régions modifiées du maillage au lieu de tout le maillage —, mais de telles mises à jour partielles nécessitent un vrai cas de test pour les guider, et puisque ce jeu anime presque chaque sommet à chaque image, le suivi des changements partiels serait probablement un net négatif.
Le second cas visait le pipeline de compilation des shaders D3D12 de Godot, pris en charge après des signalements de chargement lent. L'étape la plus lente du pipeline était la transpilation, et le passage de Vulkan à DXIL imposait une pénalité notable au temps de chargement, car Vulkan peut consommer directement le SPIR-V. Le traçage dans Superluminal a révélé la cause : un multithreading dense avec de grands creux où seuls deux ou trois threads étaient actifs. En zoomant, on voyait que ces creux survenaient lorsque tous les threads demandaient simultanément davantage de mémoire de tas au système d'exploitation — ils restaient en attente de l'allocation depuis un tas global unique. La correction a donné à chaque thread son propre tas au lieu de solliciter sans cesse le tas global commun, éliminant ainsi les blocages. Cela a économisé 11 secondes de temps de chargement dans la démo TPS — du temps pendant lequel le CPU était totalement inactif. Les deux exemples soulignent la même leçon : avec le bon outil, la solution devient évidente dès que le problème est correctement identifié.