correction contradictions

This commit is contained in:
Jérôme Bousquié
2026-07-31 20:43:57 +02:00
parent 8d61e4231e
commit 81b825970a
2 changed files with 56 additions and 17 deletions
+56 -17
View File
@@ -14,8 +14,11 @@ wsg_lib est un moteur de rendu modulaire basé sur wgpu. Il adopte une architect
## 1. Philosophie et Principes
- **Abstraction vs Transparence** : Le moteur masque la complexité (wgpu, winit, gestion des Frame) via `App`, tout en exposant les briques élémentaires pour les utilisateurs avancés.
- **Approche orientée Scène** : Le rendu repose sur la composition d'une `Scene` contenant les entités, matériaux et géométries.
- **Pipeline Data-Driven** : Les ressources (Shaders, Meshes, Materials) sont découplées. Le `PipelineCache` gère automatiquement la compilation et la réutilisation des pipelines GPU.
- **Architecture GPU-Driven** : Le CPU est le cerveau logique (gestion de la scène, IA, réseau), le GPU est l'exécutant visuel. Le moteur délègue au GPU le calcul des World Matrices, le Frustum Culling et la génération des listes de dessin indirectes — évitant ainsi les goulets d'étranglement PCIe.
- **Pipeline à deux passes** : Chaque frame suit un ordre strict : **Compute Pass** (calculs GPU) → **Render Pass** (dessin indirect). Les barrières de mémoire sont gérées automatiquement par le driver.
- **Ressources persistantes en VRAM** : Les buffers essentiels (Transform, Matrix, BoundingBox, Indirect Draw) vivent d'une frame à l'autre sans redescendre vers le CPU. Le Double Buffering évite les conflits lecture/écriture.
- **Lecture seule pendant render()** : La Scene est immuable durant le Render. L'utilisateur ne modifie que dans `update()` ; toute tentative de mutation pendant le rendu bloque les données du GPU.
- **Pipeline Cache** : Les shaders et pipelines sont compilés une fois puis réutilisés via `Arc`. Aucun readback (`map_async`) n'est effectué sauf débug critique.
## 2. Organisation des Modules (`lib/src/`)
@@ -38,18 +41,23 @@ La façade `App` orchestre la boucle de jeu. Elle encapsule :
### Le trait `AppHandler`
L'utilisateur implémente ce trait pour définir la logique métier :
L'utilisateur implémente ce trait pour définir la logique métier. Les deux méthodes sont appelées **dans l'ordre strict** à chaque frame :
```rust
pub trait AppHandler {
// Appelé avant la préparation de la frame
// Phase Update — écriture des Transform bruts (CPU → GPU via buffer mappé).
// Seules les données logiques changent ici (position, rotation, échelle).
fn update(&mut self, _app: &mut App) {}
// Appelé au moment de la présentation
// Phase Compute + Render — déclenche un Compute Pass puis un Render Pass.
// La Scene est en lecture seule : aucun état métier ne doit être modifié.
fn render(&mut self, app: &mut App);
}
```
- **`update()`** : appelé en premier. L'utilisateur peut modifier librement la scène (transformations, ajout/suppression d'entités). Ces modifications sont synchronisées vers le GPU via un Double Buffering avant la passe de calcul.
- **`render()`** : appelé après. Il ne sert qu'à injecter du rendu personnalisé (debug, HUD, etc.). La Scene reste immuable : aucune mutation d'état métier.
## 4. Workflow et Cycle de Vie
### A. Initialisation (Configuration)
@@ -59,21 +67,44 @@ pub trait AppHandler {
- **Matériaux** : Créés avec un shader associé. Un mesh sans matériau explicite utilise `basic_shader` par défaut.
- **Scene** : Assemblage des objets. L'utilisateur peuple la scène via `app.scene`.
### B. Boucle de Rendu (Automatisée)
### B. Boucle de Rendu — Pipeline GPU-Driven
Le moteur gère la renderloop interne :
Le moteur gère la renderloop interne via un pipeline à **deux passes séquentielles** :
1. **Update** : Appel à `AppHandler::update`.
2. **Acquisition** : Gestion interne de `wgpu::SurfaceTexture`.
3. **Render** : Appel à `AppHandler::render` où l'utilisateur exécute `app.render(scene)`.
4. **Présentation** : Gestion interne de `present()`.
```
[ CPU : Envoi des Transforms bruts ]
[ Pass 1 : Compute (World Matrices + Frustum Culling + Indirect Draw Buffer) ]
↓ (Barrière de mémoire automatique par le driver)
[ Pass 2 : Render (Draw Indexed Indirect basé sur les objets visibles) ]
```
Étape par étape :
1. **Update** (`AppHandler::update`) — L'utilisateur modifie la scène (transformations, entités). Ces changements sont synchronisés vers le GPU via Double Buffering avant la passe de calcul.
2. **Compute Pass** — Un compute shader lit les Transform bruts, calcule les World Matrices finales, effectue le Frustum Culling par AABB, et remplit l'Indirect Draw Buffer avec les identifiants des objets visibles.
3. **Render Pass** — Le CPU émet une unique commande `draw_indexed_indirect`. Le GPU pioche dans l'Indirect Draw Buffer et dessine uniquement les objets visibles, sans intervention du CPU.
4. **Présentation** — La surface est présentée à l'écran.
L'ordre d'appel des méthodes sur le `CommandEncoder` (`begin_compute_pass` puis `begin_render_pass`) garantit l'exécution séquentielle. Les barrières de mémoire entre passes sont insérées automatiquement par le pilote.
#### Ressources VRAM persistantes (d'une frame à l'autre)
| Buffer | Rôle | Type wGPU | Direction du flux |
|--------|------|-----------|-------------------|
| Transform Buffer | Positions/rotations/échelles brutes | Storage Buffer | CPU → GPU |
| Matrix Buffer | World Matrices finales calculées | Storage Buffer | GPU (Calculé) → GPU (Lu par Render) |
| Bounding Box Buffer | AABB de chaque mesh pour culling | Storage Buffer | CPU → GPU (Statique) |
| Indirect Draw Buffer | Liste dynamique des objets à dessiner | Indirect + Storage | GPU (Rempli par Compute) → GPU (Lu par Render) |
Toutes ces données vivent en VRAM — aucun readback (`map_async`) n'est effectué sauf débug critique. Le CPU fait confiance à sa propre structure de données initiale pour la logique métier.
## 5. Accès Avancé
Les utilisateurs souhaitant ignorer l'abstraction `App` peuvent accéder directement à :
- `wsg_lib::core::Context` et `Renderer` pour gérer manuellement les RenderPass.
- `wsg_lib::pipeline::PipelineCache` pour des besoins de shaders personnalisés.
- `wsg_lib::core::Context` et `Renderer` pour gérer manuellement les Compute Pass et RenderPass.
- `wsg_lib::pipeline::PipelineCache` pour des besoins de shaders personnalisés (compute + render).
- `winit` pour la gestion précise des événements système.
## 6. Structure des données (pour LLM)
@@ -81,11 +112,19 @@ Les utilisateurs souhaitant ignorer l'abstraction `App` peuvent accéder directe
```
App (Facade) -> Scene (Conteneur) -> Entities -> Mesh + Material (Shader)
|
+-> Renderer (WGPU) <-> PipelineCache (Shaders)
+-> Renderer (WGPU)
├── Compute Pass : World Matrices + Frustum Culling → Indirect Draw Buffer
└── Render Pass : draw_indexed_indirect (objets visibles uniquement)
VRAM persistante : Transform Buffer → Matrix Buffer → BoundingBox Buffer → Indirect Draw Buffer
Double buffering : Update écrit dans le buffer "back", Compute lit depuis "front"
```
## Notes pour l'implémentation future
- `app.render(scene)` : Cette méthode doit devenir l'API principale pour le rendu de la scène complète.
- Trait `AppHandler` : Il est recommandé de faire passer la `Scene` ou une référence à celle-ci comme argument ou de permettre à `AppHandler` d'être le lieu où la `Scene` est manipulée (ex : `MyGame { scene: Scene, ... }`).
- `PipelineCache` : Son utilisation doit être invisible pour l'utilisateur standard lors de la création d'un `Material`.
- **`render()` ne prend pas de scène en argument** — elle déclenche automatiquement le Compute Pass puis le Render Pass sur la scène actuelle. Pour injecter du rendu personnalisé, utiliser ce point d'extension sans modifier l'état métier.
- **Trait `AppHandler`** : Le `update()` est le seul endroit où muter la scène. La `Scene` peut être stockée directement dans l'implémentation (`MyGame { scene: Scene, ... }`) ou passée via argument selon les besoins ergonomiques.
- **`PipelineCache`** : Invisible pour l'utilisateur standard lors de la création d'un `Material`, mais accessible publiquement pour shaders compute personnalisés et pipelines avancés.
- **Compute shader par défaut** : Un compute shader intégré gère le calcul des World Matrices et le Frustum Culling. Les utilisateurs avancés peuvent le remplacer entièrement via `PipelineCache`.
- **Synchronisation** : Toujours appeler `begin_compute_pass` avant `begin_render_pass` sur le même `CommandEncoder`. Les barrières entre passes sont automatiques — ne jamais insérer de barrière manuelle sauf besoin critique.
- **Double Buffering** : Ne jamais lire un buffer GPU pendant que l'autre écrit dedans. Utiliser un mécanisme de double buffering (buffers "front" / "back") pour éviter les conflits lecture/écriture entre frames.