6.9 KiB
type, title, description, tags, actor, sources, generated, verified, status, stale_after
| type | title | description | tags | actor | sources | generated | verified | status | stale_after | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Technical Specification | GPU-Driven 3D Rendering Architecture with wGPU | Technical specification for GPU-driven 3D rendering architecture using wgpu, focusing on CPU-GPU workload distribution and performance optimization |
|
person/jerome |
|
true | current | 2027-01-31 |
Architecture de Rendu 3D GPU-Driven avec wGPU : Bonnes Pratiques & Guide d'Implémentation
Ce document sert de spécification technique et de trame d'implémentation pour l'architecture de rendu 3D pilotée par le GPU (GPU-Driven Rendering) utilisant wgpu. L'objectif est de déléguer un maximum de charges de calcul au GPU pour soulager le CPU et maximiser les performances de parallélisme.
État du document : ACTUEL (implémenté — Phase 3 du ROADMAP, 2026-07-20, DRAFT Étape 17). La répartition CPU/GPU, le compute pass (World Matrices + Frustum Culling), l'Indirect Draw Buffer et les buffers persistants en VRAM décrits ici sont en place :
shaders/gpu_driven.wgsl(deux entry pointscompute_matrices+cull, un module, layout explicite à 3 groupes) et les buffers de slots duRenderer(TransformSlot/MatSlot/BBoxSlot/DrawSlot/CullUniforms, capacité fixe de 256 slots). Écarts documentés (cf. DRAFT Étape 17) : (D1) un draw indirect par slot plutôt qu'une commande unique fusionnée ; (D12) 256 slots, slot matrice padded à 256 o (plafonduniformWebGPU) ; (D5) culling par sphère conservative dérivée de l'AABB locale du mesh, pas par l'AABB transformée exacte ; (D4) single buffer, pas de double-buffering. Piège connu (2026-09-22, D14) : l'ordre des arguments deselecten WGSL est l'inverse de la convention HLSL — l'avoir inversé a produit un bug « fenêtre noire » (entités visibles remises à 0), corrigé et vérifié par readback GPU. Documenté en tête degpu_driven.wgslet dansAGENTS.md.
- Répartition des Rôles : CPU vs GPU (La Source de Vérité)
Pour éviter les goulets d'étranglement dus aux allers-retours sur le bus PCIe, la règle d'or est la suivante : Le CPU est le cerveau logique, le GPU est l'exécutant visuel.
Côté CPU (Source de Vérité)
- Ce qu'il conserve : Les données logiques et les transformations brutes des objets (ex: Vec contenant la position, la rotation, et l'échelle).
- Ce qu'il fait : Il gère la logique de jeu, l'IA, le réseau et les interactions globales.
- Ce qu'il ne fait plus : Il ne calcule plus les matrices de transformation mondiales (World Matrices) en masse, et ne fait plus de tests de visibilité unitaires.
Côté GPU (Exécutant Autonome)
- Ce qu'il calcule : Les World Matrices, le Frustum Culling, et la génération des listes de dessin indirectes.
- Ce qu'il conserve : Les buffers de données persistants en VRAM (Storage Buffers) qui vivent d'une frame à l'autre sans jamais redescendre vers le CPU.
- Le Pipeline d'Exécution par Frame (Ordre des Passes)
L'exécution des tâches s'appuie sur une structure séquentielle stricte au sein d'un même CommandEncoder. Le driver et wGPU s'occupent des barrières de mémoire implicites entre chaque étape.
[ CPU : Envoi des Transforms bruts ]
↓
[ Pass 1 : Compute (Calcul World Matrices + Frustum Culling + Indirect Draw Buffer) ]
↓ (Barrière de mémoire automatique gérée par le driver)
[ Pass 2 : Render (Draw Indexed Indirect basé sur les objets visibles) ]
Étape par étape :
- Mise à jour CPU (Minimaliste) : Le CPU écrit les transformations brutes (Transform) modifiées dans un buffer GPU mappé (single buffer en phase initiale — la synchronisation est assurée par
queue.submit()qui garantit la séquence d'exécution). Double buffering sera ajouté uniquement si des artefacts apparaissent à haute fréquence (> 90 fps). - Pass de Calcul (Compute Pass) :
- Calcul des World Matrices : Un compute shader lit les transformations brutes et génère la matrice 4x4 finale pour chaque mesh.
- Frustum Culling GPU : Un compute pass dédié (
cull) compare la sphère bounding de chaque objet (D5 — conservative, dérivée de l'AABB locale du mesh et de l'échelle de l'entité) avec les 6 plans du frustum de la caméra. - Remplissage du Buffer Indirect : le pass
cullécrit le compte de sommets/indices de chaque objet dans sonDrawSlot(80 o) — mis à 0 si l'objet est cullé ou inactif (no-op).
- Pass de Rendu (Render Pass) :
- Le CPU émet un draw indirect par slot (écart D1 — la spécification initiale prévoyait une commande unique fusionnée) ; les slots à compte 0 (cullés/inactifs/vides) sont des no-ops.
- Le GPU pioche directement dans le buffer préparé par le compute pass et dessine uniquement les objets visibles, sans intervention du CPU.
- Stratégie de Synchronisation
- Sécurité de l'ordre : L'ordre d'appel des méthodes sur le CommandEncoder (begin_compute_pass suivi de begin_render_pass) garantit l'ordre d'exécution séquentiel sur le GPU.
- Barrières de mémoire : Le pilote insère automatiquement les barrières nécessaires pour s'assurer que le buffer de la WorldMatrix et le buffer Indirect sont complètement écrits par le compute shader avant d'être lus par le render pipeline.
- Éviter le Readback (map_async) : Sauf cas exceptionnel (débug ou interaction scriptée critique), aucune donnée géométrique ou de position ne doit remonter du GPU vers le CPU. Le CPU fait confiance à sa propre structure de données initiale pour la logique métier.
- Synthèse des Structures de Données en VRAM
L'implémentation utilise les buffers wGPU suivants (tous créés par le Renderer à l'initialisation, capacité fixe de 256 slots) :
| Buffer | Rôle | Type wGPU | Direction du flux |
|---|---|---|---|
| Transform Buffer | Positions/rotations/échelles brutes + flags par entité (64 o/slot) | Storage Buffer | CPU → GPU (chaque frame, write_buffer) |
| Matrix Buffer | World Matrices finales calculées (256 o/slot, padded — D12) | Storage + Uniform Buffer | GPU (Calculé) → GPU (Lu par le Render) |
| Bounding Box Buffer | Coins min/max de l'AABB de chaque mesh (32 o/slot) | Storage Buffer | CPU → GPU (quand l'ensemble des meshes change) |
| Indirect Draw Buffer | Comptes de draw par slot (80 o/slot, zéro = no-op) | Indirect + Storage Buffer | GPU (Rempli par Compute) → GPU (Lu par le Render) |
| CullUniforms | 6 plans du frustum + num_slots + culling (112 o) |
Uniform Buffer | CPU → GPU (chaque frame) — réservé au compute (group 2) |
Liens
- ARCHI_APP · ARCHI_RENDU · ARCHI_ARENES · FRAME_LOOP
- Documentation utilisateur : docs/user · README racine · ROADMAP
- Référence API :
cargo doc -p wsg-lib --no-deps