Files
wsg/docs/tech/ARCHI_CPU_GPU.md
T
Jérôme Bousquié 3a424afe8c GPU culling
2026-09-22 15:48:15 +02:00

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
architecture
rendering
gpu
cpu
performance
wgpu
person/jerome
by at
human:jerome 2026-07-31T00:00:00Z
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 points compute_matrices + cull, un module, layout explicite à 3 groupes) et les buffers de slots du Renderer (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 (plafond uniform WebGPU) ; (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 de select en 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 de gpu_driven.wgsl et dans AGENTS.md.

  1. 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.
  1. 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 son DrawSlot (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.
  1. 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.
  1. 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