docs(étape8): valider et documenter le refactor stockage CPU Arc<Geometry> (8.6)

- DRAFT.md : cases 8.1-8.6 cochées, état 'terminée et vérifiée 2026-09-18', bilan final.
- README.md : workflow manuel et déclaratif (extraits Mesh::new -> Geometry/from_geometry),
  note API create_mesh(Geometry), section architecture + table quick reference, roadmap +1 (CPU storage).
- ROADMAP.md (1.2) : colors + refactor Mesh/Scene cochés 'fait', déviation D3 'implémenté'.
- PLAN.md : statut réel à jour 2026-09-18 (Étape 8 effectuée).
- resources/README.md : Mesh::new() -> from_geometry() + rétention CPU.
This commit is contained in:
Jérôme Bousquié
2026-09-18 10:22:43 +02:00
parent 4a94ad4ac1
commit 9ad47e8790
5 changed files with 92 additions and 47 deletions
+28 -15
View File
@@ -2,7 +2,7 @@
WSG is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://crates.io/crates/winit) for simple GPU drawing. It groups the five core wgpu objects (Instance, Surface, Adapter, Device, Queue) behind a single `Context`, adds small building blocks (`Mesh`, `Material`, `PipelineCache`, `Frame`), and exposes the low-level primitives for advanced users. WSG is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://crates.io/crates/winit) for simple GPU drawing. It groups the five core wgpu objects (Instance, Surface, Adapter, Device, Queue) behind a single `Context`, adds small building blocks (`Mesh`, `Material`, `PipelineCache`, `Frame`), and exposes the low-level primitives for advanced users.
> **Status: unstable development version.** The manual workflow below is fully working, the high-level "declarative" workflow (automatic `App` scene rendering) works for flat/NDC drawing, and the **3D MVP is reached** : the `cube` example (Étape 5) renders a rotating, Phong-lit cube through `App::render_scene`. The GPU-driven two-pass pipeline described in the architecture docs is **not implemented yet** — see [Status](#status) and [Roadmap](#roadmap). > **Status: unstable development version.** The manual workflow below is fully working, the high-level "declarative" workflow (automatic `App` scene rendering) works for flat/NDC drawing, and the **3D MVP is reached** : the `cube` example (Étape 5) renders a rotating, Phong-lit cube through `App::render_scene`. Since Étape 8, meshes are declared from a CPU `Geometry` (retained as `Arc<Geometry>` on the Mesh) instead of raw vertex arrays. The GPU-driven two-pass pipeline described in the architecture docs is **not implemented yet** — see [Status](#status) and [Roadmap](#roadmap).
## Status ## Status
@@ -28,7 +28,7 @@ use winit::event_loop::EventLoop;
use winit::window::WindowBuilder; use winit::window::WindowBuilder;
use wsg_lib::core::{Context, Frame, Renderer}; use wsg_lib::core::{Context, Frame, Renderer};
use wsg_lib::pipeline::PipelineCache; use wsg_lib::pipeline::PipelineCache;
use wsg_lib::resources::{Material, Mesh, Vertex}; use wsg_lib::resources::{Geometry, Material, Mesh};
use wsg_lib::utils; use wsg_lib::utils;
fn main() { fn main() {
@@ -45,16 +45,23 @@ fn main() {
let mut cache = PipelineCache::new(Arc::new(context.device.clone())); let mut cache = PipelineCache::new(Arc::new(context.device.clone()));
cache.register_shader("standard", utils::STANDARD_SHADER_PATH).unwrap(); cache.register_shader("standard", utils::STANDARD_SHADER_PATH).unwrap();
// Material + mesh // Material + mesh (Étape 8 : le mesh est construit depuis une `Geometry` — positions,
// attributs optionnels en builder, défauts blancs via `to_vertices`).
let material = Material::new(renderer.format(), "standard", &mut cache); let material = Material::new(renderer.format(), "standard", &mut cache);
let vertices: [Vertex; 4] = [ let geometry = Geometry::new(vec![
Vertex { position: [-0.5, 0.5, 0.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 0.0], color: [1.0, 0.0, 0.0, 1.0] }, [-0.5, 0.5, 0.0], // Haut-Gauche
Vertex { position: [ 0.5, 0.5, 0.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 0.0], color: [0.0, 1.0, 0.0, 1.0] }, [ 0.5, 0.5, 0.0], // Haut-Droite
Vertex { position: [ 0.5, -0.5, 0.0], normal: [0.0, 0.0, 1.0], uv: [1.0, 1.0], color: [0.0, 0.0, 1.0, 1.0] }, [ 0.5, -0.5, 0.0], // Bas-Droite
Vertex { position: [-0.5, -0.5, 0.0], normal: [0.0, 0.0, 1.0], uv: [0.0, 1.0], color: [1.0, 1.0, 0.0, 1.0] }, [-0.5, -0.5, 0.0], // Bas-Gauche
]; ])
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3]; .with_colors(vec![
let mesh = Mesh::new(renderer.device(), &vertices, Some(&indices)); [1.0, 0.0, 0.0, 1.0], // Rouge
[0.0, 1.0, 0.0, 1.0], // Vert
[0.0, 0.0, 1.0, 1.0], // Bleu
[1.0, 1.0, 0.0, 1.0], // Jaune
])
.with_indices(vec![0, 1, 2, 0, 2, 3]);
let mesh = Mesh::from_geometry(renderer.device(), Arc::new(geometry), None);
// Render loop // Render loop
event_loop.run(|event, elwt| { event_loop.run(|event, elwt| {
@@ -96,10 +103,13 @@ async fn main() -> Result<(), wsg_lib::utils::WsgError> {
// Register your scene once (string IDs), then App renders it automatically each frame. // Register your scene once (string IDs), then App renders it automatically each frame.
// Since Étape 7 the Scene owns the pipeline cache: build materials/meshes through it and // Since Étape 7 the Scene owns the pipeline cache: build materials/meshes through it and
// link the material to the mesh (no material_id on the entity anymore). // link the material to the mesh (no material_id on the entity anymore).
// Since Étape 8 meshes are declared from a `Geometry` (positions + optional attributes).
// app.renderer_mut().set_unlit(true); // select flat 2D rendering (optional) // app.renderer_mut().set_unlit(true); // select flat 2D rendering (optional)
// app.scene.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)?; // app.scene.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)?;
// app.scene.add_material_shader("mat", "standard")?; // build via the Scene's cache // app.scene.add_material_shader("mat", "standard")?; // build via the Scene's cache
// app.scene.create_mesh("quad", &vertices, Some(&indices), Some("mat"))?; // mesh links its Material // let geometry = wsg_lib::resources::Geometry::new(vec![[-0.5,0.5,0.0],[0.5,0.5,0.0]])
// .with_colors(vec![[1.0,0.0,0.0,1.0],[0.0,1.0,0.0,1.0]]);
// app.scene.create_mesh("quad", geometry, Some("mat"))?; // mesh links its Material
// app.scene.add_entity("my_quad", "quad")?; // app.scene.add_entity("my_quad", "quad")?;
app.run(MyGame) app.run(MyGame)
@@ -108,13 +118,14 @@ async fn main() -> Result<(), wsg_lib::utils::WsgError> {
> API note: `Scene::register_shader` / `add_material_shader` / `create_mesh` / `add_entity` and > API note: `Scene::register_shader` / `add_material_shader` / `create_mesh` / `add_entity` and
> `PipelineCache::register_shader` currently return `Result<_, String>` — typed error unification > `PipelineCache::register_shader` currently return `Result<_, String>` — typed error unification
> is on the roadmap. > is on the roadmap. Since Étape 8, `Scene::create_mesh(id, geometry, material)` takes a CPU
> `Geometry` (source of truth, retained on the Mesh) instead of raw `&[Vertex]`.
## Architecture overview ## Architecture overview
- **Manager layer (`Context`)** — owns the GPU hardware lifecycle (Instance → Surface → Adapter → Device → Queue). Created once at startup; `configure()` sets up the swapchain, `Frame` wraps each frame's surface texture + view. - **Manager layer (`Context`)** — owns the GPU hardware lifecycle (Instance → Surface → Adapter → Device → Queue). Created once at startup; `configure()` sets up the swapchain, `Frame` wraps each frame's surface texture + view.
- **Executor layer (`Renderer`)** — binds a `Material` pipeline + `Mesh` buffers into a RenderPass and submits the commands. Rendering a whole `Scene` (`render_scene`) batches all entities into **one encoder + one submit per frame**; the low-level `render` still allocates one per object. - **Executor layer (`Renderer`)** — binds a `Material` pipeline + `Mesh` buffers into a RenderPass and submits the commands. Rendering a whole `Scene` (`render_scene`) batches all entities into **one encoder + one submit per frame**; the low-level `render` still allocates one per object.
- **Supporting pieces** — `PipelineCache` (shader → compiled RenderPipeline, `Arc`-shared), `Material`, `Mesh`/`Vertex`, `Scene` (string-ID registry), `Camera`/`Transform` (active camera wired to the frame uniforms, Étape 4.3). - **Supporting pieces** — `PipelineCache` (shader → compiled RenderPipeline, `Arc`-shared), `Material`, `Geometry`/`Mesh`/`Vertex`, `Scene` (string-ID registry), `Camera`/`Transform` (active camera wired to the frame uniforms, Étape 4.3). `Geometry` is the CPU source of truth (positions/normals/UVs/colors), `Mesh` uploads it to GPU buffers and retains the `Arc<Geometry>`, `Vertex` is the interleaved upload contract (Étape 8).
The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass: world matrices + frustum culling → Indirect Draw Buffer, then a single `draw_indexed_indirect` per frame) — is specified in [docs/tech/ARCHI_APP.md](docs/tech/ARCHI_APP.md) and [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) but is **not implemented yet**. The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass: world matrices + frustum culling → Indirect Draw Buffer, then a single `draw_indexed_indirect` per frame) — is specified in [docs/tech/ARCHI_APP.md](docs/tech/ARCHI_APP.md) and [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) but is **not implemented yet**.
@@ -129,7 +140,8 @@ The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass
| Renderer | Struct | Binds Material + Mesh into a RenderPass, submits | ✅ (`render_scene` batches one pass/frame) | | Renderer | Struct | Binds Material + Mesh into a RenderPass, submits | ✅ (`render_scene` batches one pass/frame) |
| PipelineCache | Struct | Shader → compiled RenderPipeline cache | ✅ | | PipelineCache | Struct | Shader → compiled RenderPipeline cache | ✅ |
| Material | Struct | Shader ID → RenderPipeline | ✅ | | Material | Struct | Shader ID → RenderPipeline | ✅ |
| Mesh / Vertex | Struct | GPU geometry container / CPU-side vertex tuple | ✅ | | Geometry | Struct | CPU-side scattered vertex data (positions/normals/UVs/colors/indices), source of truth | ✅ (Étape 8 — retained `Arc<Geometry>` on Mesh) |
| Mesh / Vertex | Struct | GPU geometry container / CPU-side interleaved upload tuple | ✅ |
| Frame | Struct | Per-frame RAII wrapper (surface texture + view) | ✅ | | Frame | Struct | Per-frame RAII wrapper (surface texture + view) | ✅ |
| Camera / Transform | Struct | Camera & transform math | ✅ Active camera + transform wired to per-frame uniforms (Étape 4.3) | | Camera / Transform | Struct | Camera & transform math | ✅ Active camera + transform wired to per-frame uniforms (Étape 4.3) |
@@ -170,3 +182,4 @@ The architecture docs live in `docs/tech/` and are written in **French**. Each d
4. ✅ **Real 3D pipeline (MVP atteint)** — MVP uniforms + camera support in the vertex shader. *(Engine plumbing done 2026-09-16 ; Étape 5, 2026-09-17 : `standard` branché sur l'exemple `cube` — un cube unitaire éclairé (Phong) qui tourne, rendu automatiquement par `App::render_scene`. Retrait de `basic` : le 2D plat = variante unlit de `standard` via `Renderer::set_unlit`.)* 4. ✅ **Real 3D pipeline (MVP atteint)** — MVP uniforms + camera support in the vertex shader. *(Engine plumbing done 2026-09-16 ; Étape 5, 2026-09-17 : `standard` branché sur l'exemple `cube` — un cube unitaire éclairé (Phong) qui tourne, rendu automatiquement par `App::render_scene`. Retrait de `basic` : le 2D plat = variante unlit de `standard` via `Renderer::set_unlit`.)*
5. **Typed resource handles** — keep String IDs for the MVP (current design, source of truth in `Scene`); slotmap-based generational handles (`ARCHI_ARENES.md`) are deferred to a later performance pass. 5. **Typed resource handles** — keep String IDs for the MVP (current design, source of truth in `Scene`); slotmap-based generational handles (`ARCHI_ARENES.md`) are deferred to a later performance pass.
6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors. 6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors.
7. ✅ **CPU geometry storage (Étape 8)** — `Mesh` retains a shared `Arc<Geometry>` (CPU source of truth with colors) alongside its GPU buffers; meshes are declared from a `Geometry` via `Mesh::from_geometry`/`Scene::create_mesh(id, geometry, material)` instead of raw `&[Vertex]` arrays. (Done 2026-09-18; `transform` stays on `Entity` — deviation D3.)
+42 -19
View File
@@ -4,8 +4,8 @@
> **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est > **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est
> le code + README.md ; les autres docs `docs/*` restent stables. > le code + README.md ; les autres docs `docs/*` restent stables.
> **État.** Étape 8 en préparation — refactor du stockage CPU des données géométriques : > **État.** Étape 8 **terminée et vérifiée le 2026-09-18** : `Mesh.geometry: Arc<Geometry>` + buffers
> `Mesh` contient `geometry: Arc<Geometry>`. Étape 7 terminée et vérifiée le 2026-09-17. > dérivés, API `create_mesh` basée `Geometry`, exemples réécrits, validation verte, docs à jour.
--- ---
@@ -54,7 +54,7 @@ Valeurs apportées :
## Étape 8.1 — Étendre `Geometry` (couleur + validation) — **proposition validée 2026-09-18** ## Étape 8.1 — Étendre `Geometry` (couleur + validation) — **proposition validée 2026-09-18**
- [ ] Dans `lib/src/math/geometry.rs` : - [x] Dans `lib/src/math/geometry.rs` :
- [ ] ajouter `colors: Option<Vec<[f32; 4]>>` en champ optionnel (parallèle à `normals`/`uvs`). - [ ] ajouter `colors: Option<Vec<[f32; 4]>>` en champ optionnel (parallèle à `normals`/`uvs`).
- [ ] documenter les invariants : `positions` obligatoire ; `normals`, `uvs`, `colors`, - [ ] documenter les invariants : `positions` obligatoire ; `normals`, `uvs`, `colors`,
`indices` optionnels mais doivent avoir la même longueur que `positions` quand présents. `indices` optionnels mais doivent avoir la même longueur que `positions` quand présents.
@@ -67,7 +67,7 @@ Valeurs apportées :
## Étape 8.2 — Convertisseur `Geometry` → `Vec<Vertex>` ## Étape 8.2 — Convertisseur `Geometry` → `Vec<Vertex>`
- [ ] Dans `lib/src/math/geometry.rs` (ou un petit trait dédié), implémenter : - [x] Dans `lib/src/math/geometry.rs` (ou un petit trait dédié), implémenter :
- [ ] `Geometry::to_vertices() -> Vec<Vertex>` qui zip `positions`/`normals`/`uvs`/`colors` - [ ] `Geometry::to_vertices() -> Vec<Vertex>` qui zip `positions`/`normals`/`uvs`/`colors`
avec des valeurs par défaut : normale `[0,0,1]`, uv `[0,0]`, couleur blanche `[1,1,1,1]`. avec des valeurs par défaut : normale `[0,0,1]`, uv `[0,0]`, couleur blanche `[1,1,1,1]`.
- [ ] documenter clairement ces défauts (i.e. une géométrie sans normales via Phong sera plate). - [ ] documenter clairement ces défauts (i.e. une géométrie sans normales via Phong sera plate).
@@ -75,7 +75,7 @@ Valeurs apportées :
## Étape 8.3 — `Mesh` contient `Arc<Geometry>` et construit ses buffers ## Étape 8.3 — `Mesh` contient `Arc<Geometry>` et construit ses buffers
- [ ] Dans `lib/src/resources/mesh.rs` : - [x] Dans `lib/src/resources/mesh.rs` :
- [ ] ajouter le champ `geometry: Arc<Geometry>`. - [ ] ajouter le champ `geometry: Arc<Geometry>`.
- [ ] remplacer/ajouter `Mesh::from_geometry(device: &wgpu::Device, geometry: Arc<Geometry>, - [ ] remplacer/ajouter `Mesh::from_geometry(device: &wgpu::Device, geometry: Arc<Geometry>,
material: Option<Arc<Material>>) -> Mesh` : material: Option<Arc<Material>>) -> Mesh` :
@@ -90,7 +90,7 @@ Valeurs apportées :
## Étape 8.4 — Adapter `Scene` / l'API déclarative ## Étape 8.4 — Adapter `Scene` / l'API déclarative
- [ ] Dans `lib/src/scene/scene.rs` : - [x] Dans `lib/src/scene/scene.rs` :
- [ ] changer `create_mesh(id, vertices: &[Vertex], indices, material)` → - [ ] changer `create_mesh(id, vertices: &[Vertex], indices, material)` →
`create_mesh(id, geometry: Geometry, material: Option<&str>) -> Result<...>` : `create_mesh(id, geometry: Geometry, material: Option<&str>) -> Result<...>` :
- [ ] il construit `Arc<Geometry>`, appelle `Mesh::from_geometry(self.device(), arc, mat)`. - [ ] il construit `Arc<Geometry>`, appelle `Mesh::from_geometry(self.device(), arc, mat)`.
@@ -100,38 +100,61 @@ Valeurs apportées :
## Étape 8.5 — Réécrire les exemples (non contraignants) sur `Geometry` ## Étape 8.5 — Réécrire les exemples (non contraignants) sur `Geometry`
- [ ] `lib/examples/cube.rs` : - [x] `lib/examples/cube.rs` :
- [ ] remplacer `cube_vertices() -> Vec<Vertex>` / `cube_indices()` par un builder de - [ ] remplacer `cube_vertices() -> Vec<Vertex>` / `cube_indices()` par un builder de
`Geometry` (ou `Geometry::new(...).with_normals(...).with_indices(...)`) ; couleur blanche `Geometry` (ou `Geometry::new(...).with_normals(...).with_indices(...)`) ; couleur blanche
par défaut → vérifier le rendu Phong inchangé. par défaut → vérifier le rendu Phong inchangé.
- [ ] appeler `scene.create_mesh("cube_mesh", geometry, Some("cube_material"))`. - [ ] appeler `scene.create_mesh("cube_mesh", geometry, Some("cube_material"))`.
- [ ] `lib/examples/simple.rs` : - [x] `lib/examples/simple.rs` :
- [ ] construire une `Geometry` (positions ± couleurs par sommet pour le quad unlit) ; - [ ] construire une `Geometry` (positions ± couleurs par sommet pour le quad unlit) ;
- [ ] `scene.create_mesh("quad", geometry, None)` (matériau par défaut). - [ ] `scene.create_mesh("quad", geometry, None)` (matériau par défaut).
- [ ] `lib/examples/manual.rs` (exemple bas-niveau, utilise `Mesh::new(renderer.device(), - [x] `lib/examples/manual.rs` (exemple bas-niveau, utilise `Mesh::new(renderer.device(),
&vertices, ...)`) : &vertices, ...)`) :
- [ ] réécrire sur `Mesh::from_geometry(device, Arc<Geometry>, None)`. - [ ] réécrire sur `Mesh::from_geometry(device, Arc<Geometry>, None)`.
- [ ] mettre à jour les en-têtes / commentaires des exemples (références à `Vertex` en public). - [x] mettre à jour les en-têtes / commentaires des exemples (références à `Vertex` en public).
## Étape 8.6 — Validation ## Étape 8.6 — Validation
- [ ] `cargo fmt --all` (aucun diff résiduel). - [x] `cargo fmt --all` (aucun diff résiduel).
- [ ] `cargo check --workspace` puis `cargo build --workspace` **sans warning** (veiller à la - [x] `cargo check --workspace` puis `cargo build --workspace` **sans warning** (veiller à la
régularité des tableaux dans les exemples). régularité des tableaux dans les exemples).
- [ ] `cargo test --workspace` (zéro test à ce stade, mais compilation clean). - [x] `cargo test --workspace` (zéro test à ce stade, mais compilation clean).
- [ ] `cargo doc --no-deps` **sans warning `missing_docs`** (la crate est en `#![warn(missing_docs)]`). - [x] `cargo doc --no-deps` **sans warning `missing_docs`** (la crate est en `#![warn(missing_docs)]`).
- [ ] Lancer les 3 exemples (simple, cube, manual) et constater l'absence de panic / rendu visé. - [x] Lancer les 3 exemples (simple, cube, manual) et constater l'absence de panic / rendu visé.
- [ ] Mettre à jour `README.md` (extraits de code `Mesh::new` → `Geometry`, section architecture) - [x] Mettre à jour `README.md` (extraits de code `Mesh::new` → `Geometry`, section architecture)
et les statuts `docs/PLAN.md` + `docs/ROADMAP.md` (cocher le refactor 1.2 « stockage CPU » ; et les statuts `docs/PLAN.md` + `docs/ROADMAP.md` (cocher le refactor 1.2 « stockage CPU » ;
laisser `transform` documenté comme déviation en D3). laisser `transform` documenté comme déviation en D3).
## Point d'étape ## Point d'étape
- [ ] Caser le refactor : `Mesh.geometry: Arc<Geometry>` + buffers dérivés, API `create_mesh` - [x] Caser le refactor : `Mesh.geometry: Arc<Geometry>` + buffers dérivés, API `create_mesh`
basée `Geometry`, exemples réécrits, validation verte, docs à jour. basée `Geometry`, exemples réécrits, validation verte, docs à jour.
- [ ] Deux commits séparés comme d'habitude : un `refactor(...)` (8.1–8.5) puis un `docs(...)` - [x] Deux commits séparés comme d'habitude : un `refactor(...)` (8.1–8.5) puis un `docs(...)`
(8.6). Rédiger un court bilan et ouvrir la question du prochain chantier. (8.6). Rédiger un court bilan et ouvrir la question du prochain chantier.
## Bilan (2026-09-18)
L'Étape 8 est **terminée et vérifiée** :
- `Geometry` (math) : champ `colors`, constructeur `Geometry::new(positions)` + builder
fluent (`.with_normals/.with_uvs/.with_colors/.with_indices`), `validate()` + `GeometryError`,
`indices()` et `to_vertices()`/`try_into_vertices()` (zip positions/normals/uvs/colors avec défauts
normale `[0,0,1]`, uv `[0,0]`, blanc opaque).
- `Mesh` (resources) : retient `geometry: Arc<Geometry>` (CPU, D5) + buffers GPU pré-uploadés ;
constructeur **unique** `Mesh::from_geometry(device, Arc<Geometry>, Option<Arc<Material>>)`
(D4) — `Mesh::new`/`with_material` (`&[Vertex]`) supprimés ; accesseurs `geometry()`/`material()`/`set_material()`.
- `Scene` : `create_mesh(id, Geometry, Option<&str>)` (D4) ; `Geometry` exposé via `math` (D2)
et ré-exporté en convenance depuis `resources`.
- Exemples cube/simple/manual réécrits sur `Geometry` (8.5). `simple` passe par `None` pour
vérifier le matériau par défaut (`Scene::default_material`).
- Validation : `cargo fmt` (aucun diff), `build`/`check` sans warning, `test` vert (1 unitaire
shader + 1 doc-test `Geometry::new`), `doc` sans `missing_docs`, 3 exemples lancés (rendu sans panic).
- Commits : `refactor(...)` (8.1–8.5) + `docs(...)` (8.6).
**Prochain chantier possible** : la suite du ROADMAP — gestion des matériaux/textures (Phase 2/4,
ex. `Texture` + `uniform` diffuse), ou remonter vers les **handles typés** (Phase 2/5) / pipeline
**GPU-driven** (Phase 3) avec la bounding box dans `Geometry` (le champ `colors` et la rétention CPU
`Arc<Geometry>` posent déjà la base du culling).
--- ---
_Fin du DRAFT Étape 8 — à valider avant implémentation._ _Fin du DRAFT Étape 8 — implémentée et vérifiée le 2026-09-18._
+6 -2
View File
@@ -11,7 +11,7 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé. Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé.
> **Statut réel (à jour au 2026-09-17).** Ce plan couvre la phase de *consolidation* passée ; la source > **Statut réel (à jour au 2026-09-18).** Ce plan couvre la phase de *consolidation* passée ; la source
> de vérité sur l'état actuel est **README.md** et le code. Depuis la révision du 2026-09-14, l'étape > de vérité sur l'état actuel est **README.md** et le code. Depuis la révision du 2026-09-14, l'étape
> **« Scene auto-render »** a été réalisée : le rendu de la `Scene` est **automatisé** en une seule > **« Scene auto-render »** a été réalisée : le rendu de la `Scene` est **automatisé** en une seule
> passe groupée via `App::render_scene(frame.view())` (appelée par défaut dans `AppHandler::render`), > passe groupée via `App::render_scene(frame.view())` (appelée par défaut dans `AppHandler::render`),
@@ -21,7 +21,11 @@ Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle
> le buffer frame chaque frame, matrices monde par entité. **Étape 5 (2026-09-17) : MVP 3D atteint** — > le buffer frame chaque frame, matrices monde par entité. **Étape 5 (2026-09-17) : MVP 3D atteint** —
> l'exemple `cube` rend un cube unitaire éclairé (Phong) en rotation via `App::render_scene` ; le > l'exemple `cube` rend un cube unitaire éclairé (Phong) en rotation via `App::render_scene` ; le
> shader `basic` est supprimé, le 2D plat devient la **variante unlit** de `standard` > shader `basic` est supprimé, le 2D plat devient la **variante unlit** de `standard`
> (`Renderer::set_unlit` / `app.renderer_mut().set_unlit(true)`). > (`Renderer::set_unlit` / `app.renderer_mut().set_unlit(true)`). L'**Étape 7 (2026-09-17)** a rattaché
> le `PipelineCache` à la `Scene` et fait référencer son `Material` par chaque `Mesh`. L'**Étape 8
> (2026-09-18)** a donné à `Mesh` une source de vérité **CPU partagée** (`geometry: Arc<Geometry>`) :
> `Scene::create_mesh(id, Geometry, material)` déclare les meshes depuis une `Geometry` (avec couleurs),
> `Mesh::from_geometry` dérive ses buffers GPU, et les exemples cube/simple/manual utilisent `Geometry`.
## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue) ## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue)
+15 -10
View File
@@ -53,24 +53,29 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
- [x] `positions: Vec<[f32; 3]>` (obligatoire) - [x] `positions: Vec<[f32; 3]>` (obligatoire)
- [x] `indices: Option<Vec<u16>>` (optionnel) - [x] `indices: Option<Vec<u16>>` (optionnel)
- [x] `normals: Option<Vec<[f32; 3]>>` (pour Phong) — plus `uvs: Option<Vec<[f32; 2]>>` - [x] `normals: Option<Vec<[f32; 3]>>` (pour Phong) — plus `uvs: Option<Vec<[f32; 2]>>`
- [ ] `colors: Option<Vec<[f32; 4]>>` — **décidé en DRAFT Étape 8 (D1)** : le shader lit la couleur - [x] `colors: Option<Vec<[f32; 4]>>` — **fait (Étape 8, 8.1, 2026-09-18)** : décidé en DRAFT Étape 8 (D1) ;
unlit, il faut la porter dans `Geometry` ; conversion `Geometry -> Vec<Vertex>` pour l'upload. le shader lit la couleur unlit, elle est donc portée dans `Geometry`. Conversion
- [ ] Refactorer `Mesh` pour contenir : `Geometry -> Vec<Vertex>` via `Geometry::to_vertices()` (D6) pour l'upload.
- [ ] `geometry: Arc<Geometry>` - [x] Refactorer `Mesh` pour contenir — **fait (Étape 8, 8.3, 2026-09-18)** :
- [ ] `vertex_buffer: wgpu::Buffer` - [x] `geometry: Arc<Geometry>` (rétention CPU, D5) + accesseur `geometry()`
- [ ] `index_buffer: Option<wgpu::Buffer>` - [x] `vertex_buffer: wgpu::Buffer`
- [x] `index_buffer: Option<wgpu::Buffer>`
- Construction via `Mesh::from_geometry(device, Arc<Geometry>, material)` (D4) ; les anciennes
voies `Mesh::new`/`with_material` (`&[Vertex]`) sont supprimées.
- `Scene::create_mesh(id, Geometry, Option<&str>)` (8.4) ; exemples cube/simple/manual réécrits
sur `Geometry` (8.5).
- [x] Ajouter un mesh de test (cube unitaire) en exemple — **fait** (helper `cube_geometry` dans l'exemple `cube`, Étape 5, 2026-09-17) - [x] Ajouter un mesh de test (cube unitaire) en exemple — **fait** (helper `cube_geometry` dans l'exemple `cube`, Étape 5, 2026-09-17)
> **Note (2026-09-17, DRAFT Étape 7)** : le refactor « Mesh contient `Arc<Geometry>` » ci-dessus reste > **Note (2026-09-17, DRAFT Étape 7)** : le volet *matériau* de `Mesh` a été fait en Étape 7 :
> **reporté** (il porte sur le *stockage CPU* des données géométriques). En revanche le volet *matériau* > `Mesh.material: Option<Arc<Material>>` (cf. PLAN Phase 2, gestion des matériaux), indépendant de
> de `Mesh` a été fait en Étape 7 : `Mesh.material: Option<Arc<Material>>` (cf. PLAN Phase 2, gestion des > la structure `Geometry`.
> matériaux), indépendant de la structure `Geometry`.
> >
> **Décision **D3** (2026-09-18, DRAFT Étape 8)** : le ROADMAP listait `transform: Transform` sur `Mesh`. > **Décision **D3** (2026-09-18, DRAFT Étape 8)** : le ROADMAP listait `transform: Transform` sur `Mesh`.
> **Déviation validée : `transform` reste sur `Entity` et n'est PAS ajouté à `Mesh`.** Un mesh est > **Déviation validée : `transform` reste sur `Entity` et n'est PAS ajouté à `Mesh`.** Un mesh est
> **partagé** par plusieurs entités à des transforms différents (modèle instancé, Étape 4/7) : un > **partagé** par plusieurs entités à des transforms différents (modèle instancé, Étape 4/7) : un
> `transform` unique sur `Mesh` casserait ce modèle. L'Étape 8 met donc en œuvre le refactor de > `transform` unique sur `Mesh` casserait ce modèle. L'Étape 8 met donc en œuvre le refactor de
> *stockage CPU* (`Mesh.geometry: Arc<Geometry>` + buffers dérivés) **sans** le champ `transform`. > *stockage CPU* (`Mesh.geometry: Arc<Geometry>` + buffers dérivés) **sans** le champ `transform`.
> *(Implémenté 2026-09-18.)*
### 1.3 Shader Phong Minimal ### 1.3 Shader Phong Minimal
- [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) : - [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) :
+1 -1
View File
@@ -7,7 +7,7 @@ The `resources` module defines three immutable data types that flow through the
| File | Responsibility | | File | Responsibility |
|------|---------------| |------|---------------|
| **vertex** | Vertex struct — CPU-side per-attribute tuple (position [f32;3], normal [f32;3], uv [f32;2], color [f32;4]). Must match PipelineCache::build_pipeline() vertex buffer layout byte-for-byte. | | **vertex** | Vertex struct — CPU-side per-attribute tuple (position [f32;3], normal [f32;3], uv [f32;2], color [f32;4]). Must match PipelineCache::build_pipeline() vertex buffer layout byte-for-byte. |
| **mesh** | Mesh struct — persistent GPU geometry container with vertex_buffer (wgpu::Buffer), optional index_buffer, and draw call counters. Created via Mesh::new() which uploads data from CPU to GPU buffers. | | **mesh** | Mesh struct — persistent GPU geometry container with retained CPU `geometry: Arc<Geometry>` (Étape 8), vertex_buffer (wgpu::Buffer), optional index_buffer, and draw call counters. Created via Mesh::from_geometry() which derives Vertex arrays from the Geometry and uploads them to GPU buffers. |
| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. | | **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. |
## Interaction with Other Modules ## Interaction with Other Modules