Analyse théorique et technique des goulots d'étranglement CPU de l'architecture de rendu de Godot 4.7
Godot

Analyse théorique et technique des goulots d'étranglement CPU de l'architecture de rendu de Godot 4.7

AuteurGameDev Base
Date28 octobre 2026
21 min de lecture

Architecture de rendu de Godot 4.7 : Analyse théorique et technique des goulots d'étranglement CPU

Cadre architectural général et points de synchronisation CPU-GPU

L'architecture de rendu moderne du moteur de jeu Godot, en particulier dans les versions prenant en charge les graphismes 3D et 2D avancés, repose sur une structure en couches complexe visant à assurer l'indépendance vis-à-vis de la plateforme et l'accès aux API graphiques de bas niveau (Vulkan, Direct3D 12, Metal). La pierre angulaire de cette architecture est une couche d'abstraction appelée RenderingDevice, utilisée par tous les moteurs de rendu modernes. RenderingDevice vise à adapter les API de différents backends graphiques à une interface commune, permettant ainsi la compilation pour plusieurs plateformes avec la même base de code. Cette couche gère les ressources GPU fondamentales et les opérations telles que les listes de commandes (draw_list, compute_list, raytracing_list), permettant aux entités visuelles de haut niveau gérées par le RenderingServer (maillages, matériaux, lumières) d'être envoyées efficacement au GPU. Bien que cette approche architecturale vise à réduire la duplication de code et à faciliter l'intégration de nouveaux backends, elle entraîne également une charge CPU importante cachée derrière chaque appel d'API. Chaque commande de rendu, changement de shader ou changement d'état nécessite une quantité importante de travail CPU pour que le GPU puisse valider la configuration actuelle ; cela rend indispensable l'utilisation de techniques de « regroupement » (batching) pour minimiser les appels d'API coûteux, qui constituent l'objectif principal des optimisations de rendu, et pour rendre les objets similaires en une seule opération .

La synchronisation entre le CPU et le GPU est d'une importance capitale pour le bon fonctionnement du processus de rendu, mais elle constitue également une source majeure de goulots d'étranglement CPU potentiels. Godot prend en charge des mécanismes de synchronisation synchrones et asynchrones. La classe RenderingDevice propose des points de synchronisation explicites qui mettent le CPU en attente. Par exemple, la méthode sync() bloque le thread appelant sans attendre que le GPU termine toutes les tâches, ce qui est coûteux en termes de performances . De même, la méthode force_sync() peut également provoquer une baisse des performances en créant un point de synchronisation CPU-GPU obligatoire . Les opérations asynchrones, telles que les fonctions comme texture_get_data_async utilisées pour lire une texture depuis le GPU, empêchent le CPU de 525 7 533 1619 533 21

gaspiller du temps libre en attendant que le GPU termine . Cependant, ces types d'opérations ont également un délai (frame delay), et ce délai peut consommer du temps CPU selon une valeur déterminée par le paramètre de projet rendering/ rendering_device/vsync/frame_queue_size . De plus, chaque appel à texture_get_data_async peut prendre environ 4 ms de temps CPU ; le lancement simultané d'un grand nombre de requêtes peut augmenter la charge CPU totale et entraîner une baisse des performances inverse . Cette situation démontre l'importance de trouver un équilibre prudent dans la programmation asynchrone.

L'une des découvertes les plus remarquables est que la logique de synchronisation présente dans le backend Vulkan possède un défaut fondamental. Dans les sources mentionnées, il a été démontré que la fonction RenderingDeviceVulkan::swap_buffers ne suit pas l'ordre correct, c'est-à-dire qu'elle n'attend pas que le GPU libère les ressources de la frame précédente lors de la préparation d'une nouvelle frame, ce qui peut entraîner des erreurs potentielles de type « use-after-free » et des situations de plantage graves telles que DEVICE_LOST . Ce défaut ne constitue pas seulement un problème de performance, mais se distingue également comme une lacune architecturale susceptible de compromettre la stabilité et la fiabilité du moteur. En conclusion, bien que la couche RenderingDevice assure l'indépendance vis-à-vis de la plateforme, chaque commande qui la traverse représente une charge CPU, et l'utilisation d'API asynchrones combinée à des erreurs dans la logique de synchronisation constitue un jeu d'équilibre délicat qui détermine les performances du CPU.

Méthode / Point de synchronisation

Description

Goulot d'étranglement CPU potentiel

RenderingDevice.submit() (Pour les périphériques locaux)

Envoie des listes de commandes au GPU depuis des threads de travail fonctionnant en dehors du thread de rendu principal.

Coût de la synchronisation des threads et de l'envoi des commandes.

RenderingDevice.sync() (Pour les périphériques locaux)

Attend l'achèvement des threads de travail fonctionnant en dehors du thread de rendu principal.

Blocage du CPU en attendant que le GPU termine son travail, entraînant une perte de temps CPU.

RenderingDevice.force_sync()

Déclenche un point de synchronisation CPU-GPU obligatoire.

Stalling du CPU en attendant que le GPU termine son travail actuel. Très coûteux.

RenderingDevice.texture_get_data_async()

Récupère les données d'une texture sur le GPU vers le CPU de manière asynchrone.

Latence, inactivité du CPU en attendant que le GPU prépare les données.

RenderingDevice.buffer_get_data_async()

Récupère les données d'un buffer sur le GPU vers le CPU de manière asynchrone.

Temps d'attente du CPU en attendant l'arrivée des données.

Erreur de synchronisation du backend Vulkan

swap_buffers peut commencer une nouvelle frame sans attendre dans le bon ordre.

Risque de « use-after-free » et DEVICE_LOST en essayant de libérer des ressources que le GPU n'a pas encore terminées.

Une autre couche importante de l'architecture est le RenderingServer. Ce serveur fonctionne indépendamment de l'arbre de scène basé sur les nœuds et est utilisé pour la création et la gestion de tout le contenu visuel . L'API du RenderingServer repose sur des identifiants uniques appelés RID (Resource ID) 32 33 32 4 533 21 32 33 5 4 821

Ces ID sont des pointeurs opaques plutôt que des références mémoire réelles, et permettent les opérations de création, de mise à jour et de suppression des ressources . Cette conception permet aux développeurs qui utilisent directement le RenderingServer en contournant l'arbre de scène de travailler avec un système plus efficace, débarrassé des coûts du système d'arbre (notifications de cycle de vie des nœuds, propagation des transformations, etc.) . Par exemple, mettre à jour la transformation d'un objet basé sur un RID avec instance_set_transform() est beaucoup plus rapide que de faire la même chose avec Node3D.set_transform() . Cette distinction offre un outil critique pour les applications souhaitant maximiser les performances du CPU.

Le RenderingServer permet également d'être appelé en toute sécurité depuis le thread d'application principal via des mécanismes tels que call_on_render_thread(Callable) . Cela permet au thread principal d'envoyer des tâches à un thread dédié aux opérations de rendu, mais ce processus d'envoi est également un point de synchronisation et consomme du temps CPU. Par conséquent, il ne faut pas oublier que si le RenderingServer offre une voie plus efficace, les mécanismes nécessaires pour l'utiliser comportent eux-mêmes des coûts CPU. Les mesures du temps CPU peuvent être effectuées avec des fonctions telles que get_frame_setup_time_cpu(), ce qui donne le temps CPU total passé à préparer une frame, un indicateur précieux pour comprendre l'impact global des opérations au sein du RenderingServer . En conclusion, l'architecture de rendu de Godot, bien qu'elle offre indépendance vis-à-vis de la plateforme et flexibilité, est une structure complexe qui nécessite de la précision dans la synchronisation CPU-GPU, comporte des erreurs potentielles et exige des stratégies d'optimisation appropriées.

Goulots d'étranglement CPU et compromis d'optimisation dans le processus de rendu 2D

Le processus de rendu 2D dans Godot présente des dynamiques de performance différentes par rapport au rendu 3D, car il fonctionne généralement avec des ensembles de géométrie plus simples et moins de matériaux. Les jeux 2D, en particulier dans les projets de type top-down ou side-scroller où des milliers de sprites sont visibles simultanément à l'écran, ont tendance à rester limités par le CPU . Cela montre que le goulot d'étranglement CPU le plus fondamental dans les processus de rendu 2D est l'envoi individuel de Draw Call au GPU pour des centaines ou des milliers d'objets distincts. Chaque Draw Call, lorsqu'il est transmis via l'interface API (OpenGL, Vulkan, etc.), nécessite une quantité importante de travail CPU pour valider l'état actuel du GPU (shader actif, matériau, format de sommet, etc.) . Ces opérations de validation sont qualifiées de frais généraux d'arrière-plan exécutés derrière l'API, et ce coût augmente de façon exponentielle avec le changement d'état effectué par objet . Par conséquent, l'objectif principal des optimisations de rendu 2D est de minimiser ces 721 22 22 8 8 11 1619 19

appels d'API coûteux et d'utiliser des techniques de « regroupement » (batching) pour rendre des objets similaires en une seule opération . Godot propose la fonctionnalité Use Batching comme solution à ce problème. Cette fonctionnalité fonctionne en combinant des sprites ayant des matériaux similaires, ou d'autres éléments 2D tels que Polygon2D ou CollisionPolygon2D, dans un seul tampon de sommets (vertex buffer) . Ainsi, de nombreux sprites distincts peuvent être rendus avec un seul Draw Call, ce qui entraîne une réduction significative du nombre de Draw Call et, par conséquent, de la charge de validation de l'API . Cette opération nécessite une certaine quantité de coût CPU instantané ; car à chaque frame, les transformations des sprites doivent être récupérées, les données de sommets préparées et placées dans un seul tampon . Cependant, ce coût CPU instantané est bien inférieur au gain obtenu par la réalisation de beaucoup moins de Draw Call, et améliore généralement les performances globales du CPU .

Cependant, la technique de regroupement elle-même risque de créer de nouveaux goulots d'étranglement potentiels du côté du CPU. Une méthode appelée « buffer orphaning » est notamment utilisée. Dans cette méthode, le même tampon de sommets peut être réutilisé des centaines de fois au cours d'une même frame . C'est un moyen efficace de réduire le coût d'allocation/désallocation de mémoire dans les scènes à contenu dynamique. Cependant, cette technique peut donner des résultats incohérents selon la plateforme et le pilote graphique en raison des incertitudes des normes OpenGL concernant la gestion dynamique des tampons . Certains pilotes ne parviennent pas à optimiser cette réutilisation constante et peuvent provoquer des blocages du pilote GPU (driver stalls), ce qui peut dégrader les performances . Cela souligne qu'une optimisation peut présenter un comportement spécifique à la plateforme et que des tests spécifiques doivent être effectués pour chaque projet.

Par exemple, un rapport d'utilisateur a montré que sur un GPU intégré Intel UHD 620 sous Windows, le batching faisait chuter les performances de 60 FPS à 45 FPS, alors qu'il n'y avait aucun impact négatif sur Linux avec le même matériel . Cela montre que bien que le batching 2D soit un outil d'optimisation puissant, les développeurs doivent évaluer soigneusement l'impact et les risques potentiels de cette fonctionnalité. Le coût CPU du regroupement reste généralement faible tant que des dizaines de milliers d'éléments ne sont pas traités, mais le goulot d'étranglement se déplace généralement au niveau du pilote graphique lors de l'étape de téléchargement du tampon de sommets .

L'exemple le plus frappant de goulot d'étranglement CPU 2D, que les développeurs peuvent facilement manquer, est la recréation du maillage à chaque frame lors de l'animation dans la classe Polygon2D. Cette situation est appelée « mesh churn » et peut créer un coût CPU massif. Dans un scénario de test, sur un objet Polygon2D animé avec AnimationPlayer, la libération (free) du maillage existant à chaque frame et sa réallocation (création) avec de nouvelles données de sommets ont entraîné un coût CPU de 15,7 millisecondes et un temps de frame de 35 ms/frame avant l'optimisation qui a fait passer le FPS de 28 à 83 . C'est un exemple vital montrant que l'optimisation des performances doit parfois se concentrer sur la manipulation des structures de données de base, ce qui n'est pas intuitif .(116 1 2 1 11 1 1 1 1 1 1130)

Lorsque l'optimisation est effectuée sous forme de création d'un nouveau maillage plutôt que de mise à jour des données de sommets existantes, des augmentations de performance dépassant 200 % ont été observées . Cela prouve à quel point l'impact de la gestion des structures de données sur les performances du CPU est critique. Les structures de données adaptées au cache (cache-friendly) et les mises à jour de données sans création de déchets sont un principe fondamental pour éviter les goulots d'étranglement CPU, en particulier dans le rendu 2D .

Le regroupement peut ne pas être en mesure de lutter contre les erreurs structurelles fondamentales telles que le mesh churn ; car le regroupement vise à regrouper des données de sommets déjà préparées, tandis que le mesh churn traite de manière erronée la façon dont ces données sont créées à chaque frame. En conclusion, les optimisations de rendu 2D sont un compromis visant à réduire le nombre de Draw Call. Le regroupement est l'outil le plus courant de ce compromis, mais il comporte des coûts et des risques spécifiques à la plateforme. Le plus grand risque CPU pour les développeurs est de créer des goulots d'étranglement certains tels que le « mesh churn » en gérant mal les structures de données. De telles erreurs doivent être évitées en se concentrant sur la pratique de manipulation de données de base, plutôt que sur des optimisations non intuitives et complexes.

Technique d'optimisation

Objectif

Coût CPU (Instantané)

Coût CPU (Long terme)

Risques et dépendances de plateforme

Réduction du nombre de Draw Call

Réduire le coût de validation de l'interface API et de changement d'état .

Faible (généralement négligeable)

Très élevé (Gain de performance)

Information non disponible

Regroupement 2D (Batching)

Rendre des sprites ayant des matériaux similaires avec un seul Draw Call en les regroupant dans un seul tampon de sommets .

Moyen (Préparation et regroupement des données de sommets)

Très élevé (Beaucoup moins de Draw Call)

Fluctuations de performance selon le pilote GPU, blocages du pilote dus au buffer orphaning .

Atlas de sprites

Placer plusieurs sprites sur une seule grande texture (atlas) .

Faible (Calcul des coordonnées de l'atlas)

Élevé (Réduit le nombre de Draw Call en diminuant le nombre de changements de matériaux).

Limitations de taille de l'atlas, complexité de la gestion de l'atlas.

Mesh Churn (Mauvaise pratique)

Libération et recréation du maillage à chaque frame en raison d'animations ou de changements dynamiques .

Très élevé (Libération/allocation constante des tableaux de sommets)

Négatif (Coût CPU massif, baisse de FPS)

Peut potentiellement se produire si une animation est effectuée avec n'importe quel objet 2D de la scène.

Goulots d'étranglement du CPU dans le processus de rendu 3D : Culling, physique et gestion des transformations

Le processus de rendu 3D implique un ensemble d'opérations intensives pour le CPU beaucoup plus complexe que la 2D, et les goulots d'étranglement découlent de cette complexité. Ces goulots peuvent généralement être divisés en trois catégories principales : le filtrage des objets invisibles (culling), la simulation et la synchronisation physique, et la gestion des objets et des lumières dans la scène. Le culling est un processus qui améliore considérablement les performances en empêchant l'envoi d'objets invisibles au GPU.

Cependant, ces opérations de culling peuvent générer une charge CPU importante, surtout lorsque les scènes 3D contiennent des centaines de milliers d'objets. Les opérations de culling sont généralement effectuées par le CPU. Tout d'abord, les objets situés en dehors du champ de vision (Frustum) de la caméra sont filtrés. Cette opération est généralement très rapide, mais elle est coûteuse lorsqu'elle est appliquée à des centaines de milliers d'objets, même dans de grandes scènes. Une technique plus sophistiquée, l'Occlusion Culling, est la technique de culling la plus efficace, mais aussi la plus coûteuse.

Godot effectue cette opération sur le CPU en utilisant une bibliothèque de raytracing logiciel appelée Embree. Le moteur rastérise les maillages d'occlusion (occluder meshes), marqués avec des nœuds OccluderInstance3D, dans un tampon de profondeur (depth buffer) basse résolution côté CPU. Ensuite, en examinant ce tampon, il vérifie si d'autres objets (occludees) sont complètement masqués. Si les limites de la boîte englobante (AABB) d'un objet sont entièrement cachées par un occluder, il n'est pas rendu. Ce processus est très intensif pour le CPU, en particulier pour les maillages d'occlusion complexes et les occluders mobiles dans la scène.

La mise en cache (baking) des occluders et la mise à jour des structures BVH (Bounding Volume Hierarchy) des occluders mobiles à chaque image génèrent une charge CPU importante. Par conséquent, l'occlusion culling fonctionne mieux dans des environnements intérieurs comportant plusieurs pièces, alors qu'il est moins utile dans les scènes ouvertes. De plus, l'utilité de l'occlusion culling dépend du rendu utilisé. Le rendu Forward+ effectue déjà un pré-passage de profondeur (depth prepass) pour réduire le sur-dessin (overdraw). Par conséquent, bien que l'occlusion culling offre un avantage supplémentaire, le rendu Mobile (qui n'effectue pas de depth prepass) permet de constater une différence de performance plus importante .11 14 1619 111 1 2 1130

Le deuxième goulot d'étranglement majeur du CPU est la simulation physique et sa synchronisation avec l'arborescence de la scène, ce qui est vital, en particulier pour les scènes dynamiques. Le passage de Jolt Physics en tant que moteur par défaut avec Godot 4.6 a constitué une amélioration majeure en termes de vitesse et de stabilité. Jolt propose des algorithmes de détection de collision plus avancés (GJK/EPA), des solveurs de contraintes plus stables et une prise en charge intégrée du multithreading.

Cela a conduit à une augmentation des FPS de 4,7 fois dans une scène contenant 5 000 corps physiques actifs. Cependant, malgré le gain de performance apporté par Jolt, le mécanisme de synchronisation d'état (state sync) qui se produit entre le PhysicsServer et l'arborescence de la scène peut lui-même constituer un goulot d'étranglement. Ce processus, appelé flush_queries, déclenche une fonction de rappel (callback) (_sync_body_state) pour chaque objet physique actif, mettant à jour les informations de transformation des nœuds (par exemple RigidBody3D) côté GDScript/C++.

En raison des appels Callable individuels effectués par objet, cela peut créer une charge CPU équivalente au coût de la fonction d'étape physique, en particulier dans les grandes scènes contenant plus de 3 000 objets .(23 2428 24 29 2428 24 2428 12 12 15) La solution à ce problème repose sur l'ajout de fonctions de "bulk query" (requête en masse) au lieu d'une API destinée aux requêtes individuelles, permettant d'obtenir les transformations, vitesses, etc., de tous les objets actifs sous forme de données brutes uniques. Cela éliminera le coût des rappels et l'encombrement de l'interface API, offrant une amélioration significative du CPU. Les développeurs peuvent désactiver cette synchronisation d'état automatique avec body_set_state_sync_callback(Callable()) et la contrôler manuellement .

Le troisième goulot d'étranglement important du CPU est l'envoi de la transformation mondiale de chaque objet au GPU dans les scènes dynamiques et l'appariement de chaque objet avec les lumières nécessaires à son éclairage. La mise à jour de la transformation d'un objet dynamique implique des appels au RenderingServer tels que instance_set_transform(). Cela peut réduire l'utilisation du CPU de l'étape 'Preparing to Render 3D scene' de 50 %, en particulier dans une scène contenant plus de 6 500 maillages dynamiques.

Cela signifie qu'il devrait être plus efficace que le système basé sur les RID sous le système de nœuds. L'appariement objet-lumière (Object-Light Pairing) est également effectué sur le CPU. Le fait que chaque objet effectue un contrôle de chevauchement (overlap) et de masque de visibilité avec chaque lumière de la scène est une opération coûteuse, surtout dans les scènes comportant un grand nombre de lumières et d'objets dynamiques. Ce calcul consomme du temps CPU même si le rendu final n'est pas effectué. La clé pour surmonter ces goulots d'étranglement est d'utiliser des API plus efficaces comme MultiMeshInstance.

MultiMeshInstance rend des milliers de maillages identiques avec des transformations différentes de manière beaucoup plus efficace, ce qui permet de représenter 6 500 maillages dynamiques avec un seul MMI, atteignant des performances comparables à un rendu à partir d'un plan. Cela correspond au gain de performance obtenu en descendant sous le SceneTree pour utiliser directement le RenderingServer .

En conclusion, les goulots d'étranglement du CPU en 3D sont constitués de systèmes plus larges et complexes tels que le culling, la physique et la gestion des objets/lumières dynamiques. Bien que l'occlusion culling soit une optimisation puissante, elle utilise intensivement le CPU. La physique et la gestion des transformations présentent des goulots d'étranglement potentiels, notamment en raison des limitations de l'API comme flush_queries. La clé pour surmonter ces goulots d'étranglement est d'utiliser des API plus efficaces comme MultiMeshInstance et, si possible, de descendre sous l'arborescence de la scène pour utiliser directement le RenderingServer . 15 15 15 27 22 27 27 22 2227

Catégorie de goulot d'étranglement

Processus spécifique

Source du coût CPU

Méthodes d'optimisation

Culling

Culling du champ de vision

Comparaison des AABB des objets avec le frustum.

Simple et rapide, efficace même dans les grandes scènes .

Culling

Occlusion Culling

Rastérisation avec Embree sur le CPU, création et mise à jour des BVH d'occlusion.

La technique de culling la plus coûteuse, se dégrade pour les maillages d'occlusion complexes .

Physique et synchronisation

Simulation physique (Jolt)

Complexité de l'algorithme de Jolt et gestion du multithreading.

Généralement efficace, mais peut encore représenter une utilisation CPU importante dans les grandes scènes .

Physique et synchronisation

Synchronisation d'état (flush_queries)

Coût du rappel Callable individuel pour chaque objet physique actif.

Fonctions de requête en masse (suggestion), désactivation de la synchronisation automatique .

Gestion des objets et des lumières

Mise à jour des transformations

Appels serveur tels que instance_set_transform() pour chaque objet dynamique.

Utilisation de MultiMeshInstance, utilisation directe du RenderingServer basé sur les RID .

Gestion des objets et des lumières

Appariement objet-lumière

Contrôle de chevauchement et de masque de chaque objet avec chaque lumière de la scène.

Réduire le nombre de lumières, limiter les lumières distantes/projetant des ombres, utiliser le LOD .

Structures de données, performance des scripts et stratégies d'optimisation fondamentales

La performance CPU du moteur Godot ne dépend pas seulement des étapes du pipeline de rendu, mais aussi profondément des principes fondamentaux suivis par le développeur lors de la structuration de la scène et de l'écriture des scripts. La base de la performance CPU, outre les algorithmes, est constituée par l'adéquation des structures de données utilisées avec le cache CPU. Les modèles d'accès mémoire aléatoires (random memory access) entraînent ce que l'on appelle des "cache misses", ce qui crée des temps d'inactivité où le CPU attend de récupérer des données depuis la RAM, plus lente. Par conséquent, maintenir les données de manière séquentielle et locale en mémoire augmente considérablement le taux de réussite du cache et, par extension, la performance.

Bien que les API de service de Godot (par exemple, RenderingServer) traitent ces types d'optimisations au niveau fonctionnel, il est crucial que les développeurs privilégient des structures de données adaptées au cache dans leur propre code (en particulier dans les GDExtension ou les structures GDScript complexes). L'exemple le plus frappant de goulot d'étranglement CPU en 2D est la reconstruction du maillage à chaque frame lors de l'animation dans la classe Polygon2D. Il s'agit d'un exemple vital montrant que l'optimisation de la performance doit parfois se concentrer sur la manipulation des structures de données de base, même si cela semble contre-intuitif. Lorsque l'optimisation est effectuée en créant un nouveau maillage plutôt qu'en mettant à jour les données de vertex existantes, des augmentations de performance dépassant 200 % ont été observées. 23 2428 12 15 2227 27 14 14 14 1130 11

Cela prouve à quel point l'impact de la gestion des structures de données sur la performance CPU est critique. De même, lorsqu'il y a un grand nombre de nœuds dans la scène (des milliers à des dizaines de milliers), la diffusion des notifications de mise à jour _process() et _physics_process() par le SceneTree à chaque nœud représente également un coût CPU. Gérer ces nœuds en dehors de la scène (Node.remove_child) ou en les mettant en pause (set_process(false)) peut réduire ce coût. Le choix du langage de script est également un facteur influençant considérablement la performance CPU. GDScript privilégie la facilité d'utilisation plutôt que la vitesse pure. Cela signifie qu'il est interprété via une VM (Machine Virtuelle) et n'est pas compilé directement en code machine.

Cela implique un coût CPU plus élevé par rapport au C# et surtout aux modules C++ (GDExtensions). Comme le C# est basé sur .NET, il offre de bonnes performances grâce à la compilation JIT, mais peut subir des attentes lors des cycles de GC (Garbage Collection). Le C++, quant à lui, est l'option la plus rapide mais nécessite des processus de déploiement plus complexes. Ces différences de performance exigent un choix judicieux du langage de script, en particulier pour les boucles fréquemment appelées ou les algorithmes critiques. L'utilisation d'un compilateur hors ligne a été suggérée pour augmenter la performance de GDScript. Cette approche permet de compiler le code GDScript en une bibliothèque partagée (par exemple .dll, .so) capable de communiquer avec l'ABI C lors de l'exportation, éliminant ainsi le coût supplémentaire de la VM. Cependant, il s'agit d'un obstacle obligatoire pour les plateformes ne prenant pas en charge la génération de code dynamique, comme iOS, WebAssembly et les consoles. Cette situation révèle une contradiction fondamentale entre le développement de jeux basé sur des scripts et le codage au sein du moteur nécessitant de hautes performances.

À la lumière de ces facteurs fondamentaux, les stratégies d'optimisation essentielles que les développeurs doivent suivre pour améliorer la performance CPU deviennent claires. La première consiste à éviter les coûts du SceneTree en utilisant directement RenderingServer et PhysicsServer. Le housekeeping effectué par le SceneTree pour chaque nœud (notifications de cycle de vie, propagation des transformations, etc.) peut générer une charge CPU importante dans les grandes scènes contenant des milliers d'objets. Créer des entités plus légères basées sur des RID en utilisant RenderingServer élimine largement ce coût. Cette approche est idéale, notamment pour les personnages IA, les particules ou d'autres objets à haute densité.

La deuxième stratégie consiste à maximiser les optimisations intégrées accélérées par le GPU, comme MultiMeshInstance. Lorsqu'il est nécessaire de rendre des milliers de modèles identiques (herbe, arbres, projectiles lancés, etc.), MultiMeshInstance offre la possibilité de rendre ces objets en un seul draw call, ce qui est l'un des moyens les plus efficaces d'éliminer les goulots d'étranglement CPU tels que la mise à jour des transformations et le couplage objet-lumière dans les scènes dynamiques. La troisième stratégie consiste à rendre les structures de données et les algorithmes adaptés au cache. Ne pas recréer constamment les données de vertex, aligner les données de manière linéaire en mémoire et éviter les allocations/conversions de mémoire inutiles réduit le temps d'inactivité du CPU. 14 14 14 14 14 14 17 17 17 1422 22 2327

Enfin, le profilage est le seul moyen fiable de déterminer où appliquer ces optimisations. Le profileur intégré de Godot, en particulier la section Rendering dans le Frame Time, peut être utilisé pour analyser les coûts CPU liés au rendu. Cependant, il a été rapporté que ce profileur ne mesure pas correctement les temps d'attente de certains services (physique, son). Pour des analyses plus approfondies, utiliser des horloges haute résolution comme Time.get_ticks_usec() pour chronométrer vos propres blocs de code ou utiliser des profileurs C++ externes comme Valgrind (Callgrind) peut donner des résultats plus précis pour détecter les points chauds (hotspots) du CPU.

Le profilage est l'application la plus concrète du principe "mesurer avant d'optimiser" et permet au développeur de dépenser son temps de la manière la plus efficace. Ces stratégies peuvent être considérées comme une gestion budgétaire pour augmenter la performance CPU : économiser sur les coûts de l'arbre de scène, acheter des ressources plus efficaces en utilisant RenderingServer et utiliser au mieux chaque cycle d'horloge du processeur en écrivant du code adapté au cache.

Stratégie d'optimisation

Objectif

Domaine d'application

Gain CPU attendu

Éviter les coûts du SceneTree

Éliminer les notifications de cycle de vie des nœuds, la propagation des transformations et autres charges liées à l'arbre de scène.

Objets à haute densité (IA, particules, projectiles).

Économie sur les coûts de l'arbre de scène, particulièrement important avec des milliers de nœuds.

Utilisation de MultiMeshInstance

Optimiser le rendu de milliers de maillages identiques avec des transformations différentes.

Herbe, arbres, projectiles lancés, ivoires.

Les coûts de mise à jour des transformations et de couplage objet-lumière disparaissent, le nombre de draw calls diminue drastiquement.

Adapter les structures de données au cache

Minimiser le temps d'inactivité du CPU en réduisant les cache misses.

Toute l'application, en particulier la manipulation de vertex, les algorithmes.

Utilisation plus efficace de chaque cycle d'horloge du CPU, augmentation globale de la performance.

Optimisation du langage de script

Éliminer les coûts CPU provenant de couches supplémentaires comme la VM ou le GC.

Algorithmes critiques, fonctions fréquemment appelées.

Ordre de performance : C++ (GDExtension) > C# > GDScript.

Focus via le profilage

Orienter l'effort d'optimisation en identifiant les zones ayant le plus d'impact sur la performance (points chauds).

Toute l'application.

Le temps et l'effort sont consacrés aux plus grands goulots d'étranglement CPU, évite les optimisations inutiles.

Synthèse : Interactions complexes déterminant la performance CPU et perspectives d'avenir

À l'issue de cette analyse approfondie, l'architecture de rendu côté CPU de Godot 4.7 apparaît comme un système dynamique résultant d'une interaction complexe de divers facteurs. 11 14 10 14 1014 1422 2327 1114 1417 1014. Comprendre les goulots d'étranglement CPU spécifiques, qui est l'objectif principal de la recherche, permet de tirer quelques conclusions fondamentales qui aideront les développeurs à appréhender le comportement du moteur, lorsqu'elles sont évaluées avec une synthèse générale. Premièrement, le bilan fondamental entre le CPU et le GPU est au cœur des optimisations de rendu.

En rendu 2D, le goulot d'étranglement CPU est plus courant car le temps nécessaire au CPU pour préparer une frame (préparation des draw calls, culling, etc.) est généralement plus long que le temps de pixel shading du GPU. Dans ce contexte, des techniques comme le batching 2D sont un compromis efficace qui transforme le coût CPU en coût GPU. En rendu 3D, la situation est plus complexe ; les goulots d'étranglement CPU et GPU peuvent se succéder. Alors que le CPU est occupé par les opérations de culling déterminant si les objets et les lumières doivent être rendus, le GPU est occupé par la profondeur et l'éclairage des polygones visibles. Cette relation duale montre que chaque optimisation a un impact ailleurs ; par exemple, l'occlusion culling peut solliciter le CPU pour réduire la charge de shading du GPU, mais cela peut entraîner des goulots d'étranglement CPU précis comme le mesh churn dans des cas tels que Polygon2D. Deuxièmement, chaque optimisation est un compromis et il est crucial que le développeur trouve le juste équilibre en fonction des besoins de sa scène. Alors que le batching 2D transforme le coût CPU en coût GPU, les LOD élevés (Niveaux de détail) sollicitent le GPU plutôt que le CPU. Les objets transparents ne peuvent pas être batchés comme les matériaux opaques car ils nécessitent un ordre de rendu back-to-front, ce qui les rend intrinsèquement plus coûteux.

Ces compromis peuvent produire des résultats différents pour chaque projet, ce qui signifie que la meilleure stratégie doit généralement être déterminée de manière expérimentale en tenant compte du contenu de la scène, de la capacité de la plateforme cible et des exigences esthétiques de l'utilisateur. Bien que les couches d'abstraction comme RenderingDevice et RenderingServer offrent aux développeurs plus de contrôle et de performance, elles créent en réalité un coût CPU supplémentaire. Ces couches, en recouvrant le SceneTree, révèlent la nécessité de passer à une approche plus efficace basée sur les RID et la complexité qu'elle entraîne (par exemple, gérer manuellement la transformation mondiale). Cela montre que la facilité offerte par l'abstraction nécessite une compréhension architecturale plus approfondie lorsque la performance est critique.

Troisièmement, la performance CPU dépend non seulement des algorithmes, mais aussi des caractéristiques fondamentales des structures de données utilisées. L'exemple de Polygon2D prouve à quel point une manipulation correcte des données est critique. Les données doivent être mises à jour plutôt que recréées à plusieurs reprises, alignées séquentiellement en mémoire et permettre une utilisation efficace du cache CPU. Ces principes sont vitaux, surtout lors de l'écriture de code haute performance basé sur C++ comme les GDExtensions. De plus, la structure basée sur une VM des langages de script comme GDScript entraîne un coût CPU naturel par rapport aux modules C++, ce qui peut rendre l'utilisation de GDExtension obligatoire, en particulier pour les algorithmes critiques en termes de performance. Ces facteurs structurels fondamentaux doivent être optimisés avant ou simultanément au pipeline de rendu lui-même.

En examinant les perspectives d'avenir, les avancées dans les technologies de rendu visent à modifier le rôle du CPU dans le pipeline de rendu. Les travaux sur des optimisations avancées comme le rendu piloté par GPU (GPU-driven rendering) visent à ce que le CPU envoie au GPU tous les dispatchs nécessaires pour préparer une frame (pour les objets, les lumières, les passes), puis attende que le GPU termine le processus. Cette architecture pourrait potentiellement offrir une augmentation majeure de la performance en minimisant le temps d'inactivité du CPU. Cependant, cette approche crée une obligation de maintenir certaines opérations sur le CPU, comme la passe de rendu transparent pilotée par le CPU, ce qui peut constituer un nouveau goulot d'étranglement pour l'architecture. De tels changements architecturaux entraîneront une restructuration à grande échelle et la modification des flux de jeu existants.

En conclusion, à la lumière des éléments fournis, l'architecture de rendu côté CPU de Godot représente une structure flexible conçue pour assurer l'indépendance vis-à-vis de la plateforme, tout en comportant des risques importants de performance CPU tels que des erreurs de synchronisation, des comportements spécifiques à la plateforme et des compromis d'optimisation complexes. En comprenant cette architecture et ces goulots d'étranglement, les développeurs peuvent suivre les stratégies appropriées pour créer des jeux plus efficaces, plus fluides et plus performants.211 2327 1124 16 1619 522 22 1130 14 14

*Information : Les images ont été générées par Google Gemini.

Godot4.7GDScriptDrawCallJoltPhysicsOptimizationVulkan
Outil de recherche approfondie
Qwen (Alibaba Cloud)
Qwen (Alibaba Cloud)
Outil de traduction
Google Gemini
Google Gemini

Partager