628c125925
DRAFT.md now lives in docs/. Confirmed the pending design decisions: render() auto-renders the scene by default (Option A, alternative B removed), and simple.rs uses app.renderer.device()/format() via the library API without importing wgpu.
155 lines
7.7 KiB
Markdown
155 lines
7.7 KiB
Markdown
# DRAFT — Brouillon d'implémentation
|
|
|
|
> **Usage.** Ce fichier (dans `docs/`) sert de brouillon pour noter les idées et le plan détaillé de l'étape
|
|
> en cours. **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.
|
|
|
|
---
|
|
|
|
# Étape : Rendu automatique de la scène + vue de frame exposée
|
|
|
|
## 1. Contexte (état réel au 2026-09-16)
|
|
|
|
- `App::run()` : acquiert `Frame` via `context.get_next_frame()`, appelle `handler.render(&mut self)`,
|
|
puis `renderer.present(frame)`.
|
|
- ⚠️ `handler.render()` ne reçoit **pas** la frame : le callback ne peut rien dessiner. C'est précisément
|
|
le point bloquant signalé par `docs/PLAN.md` (§ Phase 2 intégra Scene, check-list) et `docs/ROADMAP.md`
|
|
(point de départ : « render() ne peut pas encore dessiner — vue de frame non exposée »).
|
|
- `Renderer::render(view, mesh, material)` existe et fonctionne (usage bas-niveau dans `manual.rs`) :
|
|
il ouvre 1 encoder + 1 render pass par objet, dessine, soumet.
|
|
- `Scene` a déjà : `add_mesh`, `add_material`, `add_entity`, `iter_entities() -> (label, mesh, mat)`,
|
|
`get_mesh`, `get_material`, `remove_entity`.
|
|
- `Material` porte déjà sa `Arc<RenderPipeline>` (compilée via `PipelineCache`). La scène stocke des
|
|
`Arc<Material>`. Donc pour dessiner une scène, le code n'a **pas** besoin de consulter le cache :
|
|
chaque matériau détient sa pipeline. Le « lien PipelineCache → Scene » du PLAN est donc **conceptuel**,
|
|
pas indispensable côté rendu pour cette étape.
|
|
|
|
## 2. Objectif
|
|
|
|
1. Que `AppHandler::render()` reçoive la vue/frame courante.
|
|
2. Que la scène se rende automatiquement (`app.render(scene)`), sans que `simple.rs` touche à wgpu.
|
|
3. Que `simple.rs` affiche le quad (4 sommets, 6 indices, matériau `basic`), en gardant ~15 lignes.
|
|
|
|
## 3. Plan d'implémentation (détail, dans l'ordre)
|
|
|
|
### Étape 3.1 — Exposer la vue de frame au callback
|
|
|
|
**Fichier** : `lib/src/handler.rs` (+ `app.rs`).
|
|
|
|
Changer la signature :
|
|
```rust
|
|
fn render(&mut self, app: &mut App, frame: &Frame);
|
|
```
|
|
- `Frame` est un type de bibliothèque (`core::Frame`) qui expose `frame.view()` → `&wgpu::TextureView`.
|
|
C'est plus riche et plus stable que de passer le `TextureView` brut : on garde une API bibliothèque.
|
|
- Adapter `handler.rs` docs (consignes `docs/DOCUMENTATION.md` : backticks, description ≤3 lignes,
|
|
ce que/qui/quand).
|
|
|
|
**Fichier** : `lib/src/app.rs`, dans `App::run`, branche `RedrawRequested` :
|
|
```rust
|
|
let frame = self.context.get_next_frame();
|
|
handler.render(&mut self, &frame); // frame est owned (valeur locale) → pas de conflit de borrow avec &mut self
|
|
self.renderer.present(frame);
|
|
```
|
|
> Point d'attention borrow : `frame` est une valeur *owned* détachée de `self.context` une fois acquise,
|
|
> on peut donc la passer par référence en même temps que `&mut self` sans erreur du borrow checker.
|
|
|
|
### Étape 3.2 — Méthode de rendu de scène groupé
|
|
|
|
**Fichier** : `lib/src/core/renderer.rs`.
|
|
|
|
Le `Renderer::render(view, mesh, material)` actuel ouvre un encoder+pass **par objet** (N submits par frame
|
|
si appelé en boucle). Pour rendre une scène entière proprement, ajouter un rendu **batch** :
|
|
|
|
```rust
|
|
pub fn render_scene(&self, view: &wgpu::TextureView, scene: &Scene) {
|
|
let mut encoder = self.device.create_command_encoder(...);
|
|
{
|
|
let mut pass = encoder.begin_render_pass(/* color attachment: view */);
|
|
for (_label, mesh, material) in scene.iter_entities() {
|
|
pass.set_pipeline(&material.pipeline);
|
|
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
|
if let Some(ib) = &mesh.index_buffer {
|
|
pass.set_index_buffer(ib.slice(..), wgpu::IndexFormat::Uint16);
|
|
pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
|
|
} else {
|
|
pass.draw(0..mesh.num_vertices, 0..1);
|
|
}
|
|
}
|
|
}
|
|
self.queue.submit(once(encoder.finish()));
|
|
}
|
|
```
|
|
- **Batching** : un seul pass pour toutes les entités (aligné sur le principe « batching par matériau »
|
|
évoqué dans renderer.rs / README Phase 4.3). On évite N submits/encoder alloués à la volée.
|
|
- Conserver `render(view, mesh, material)` (API bas-niveau utilisée par `manual.rs`). Le battle placer du code commun (layout pass / draw d'un mesh) dans un petit helper privé pour éviter la duplication.
|
|
- Import `crate::scene::Scene`.
|
|
- Documenter selon `docs/DOCUMENTATION.md`.
|
|
|
|
### Étape 3.3 — Automatisation côté App
|
|
|
|
**Fichier** : `lib/src/app.rs`.
|
|
|
|
Ajouter sur `App` :
|
|
```rust
|
|
pub fn render_scene(&self, view: &wgpu::TextureView) {
|
|
self.renderer.render_scene(view, &self.scene);
|
|
}
|
|
```
|
|
Le rendu automatique est branché par **défaut** dans le trait (**Option A, décidée**) :
|
|
```rust
|
|
fn render(&mut self, app: &mut App, frame: &Frame) {
|
|
app.render_scene(frame.view());
|
|
}
|
|
```
|
|
→ `simple.rs` **n'implémente même pas `render`** : la scène se rend toute seule, exactement l'esprit
|
|
« scene auto-render ». L'utilisateur avancé peut surcharger `render` pour contrôler le dessin.
|
|
|
|
### Étape 3.4 — Remplir `simple.rs`
|
|
|
|
**Fichier** : `lib/examples/simple.rs`.
|
|
|
|
- Créer le quad (mêmes 4 sommets + 6 indices que dans `manual.rs`, mais sans toucher à wgpu : tout se fait
|
|
via `Scene` + `AppBuilder`).
|
|
- Enregistrer le shader : `app.cache.register_shader("basic", utils::BASIC_SHADER_PATH)`
|
|
(le shader_id `"basic"` fonctionne déjà en fallback sur `BASIC_SHADER`, cf. `pipeline_cache.rs`).
|
|
- Créer le matériau avec `Material::new(app.renderer.format(), "basic", &mut app.cache)`.
|
|
- Créer le mesh avec `Mesh::new(app.renderer.device(), &vertices, Some(&indices))`.
|
|
- Enregistrer dans `app.scene` : `add_mesh`, `add_material`, `add_entity`.
|
|
- Tout ce remplissage se fait dans `AppHandler::update()` (ou dans `run()` avant `app.run(...)` — à voir
|
|
selon où `app` est constructible ; le plus simple : dans `update(&mut self, app)` une fois).
|
|
|
|
> ⚠️ Les vertex passent par `Mesh::new(device, ...)` qui exige `wgpu::Device`. **Décision prise** : pour
|
|
> l'étape 1, utiliser `app.renderer.device()`/`app.renderer.format()` (accès bibliothèque — l'utilisateur
|
|
> n'importe pas wgpu). Un helper haut niveau `Scene::add_quad_entity` pourra être ajouté plus tard.
|
|
|
|
### Étape 3.5 — Validation
|
|
|
|
```bash
|
|
cargo check --workspace
|
|
cargo doc -p wsg-lib --no-deps # exigence : "generated 0 warnings"
|
|
cargo run -p wsg-lib --example simple # le quad doit s'afficher
|
|
cargo run -p wsg-lib --example manual # le workflow manuel doit rester fonctionnel
|
|
cargo fmt --all
|
|
```
|
|
- Vérifier docs (`docs/DOCUMENTATION.md`) : zéro warning, backticks, chaque item public documenté.
|
|
- Committer proprement (conventional commits, ex. `feat(app): expose frame view and auto-render scene`).
|
|
|
|
## 4. Décisions (actées)
|
|
|
|
| Sujet | Décision |
|
|
|-------|----------|
|
|
| Signature de `render` | Ajouter `&Frame` en paramètre (Option A : default → auto-render) |
|
|
| Où dessiner la scène | `Renderer::render_scene(view, &Scene)` en **batch** (1 pass unique) |
|
|
| PipelineCache dans Scene | **Non bougé pour cette étape** : les `Material` portent déjà leur pipeline ; le lien conceptuel cache↔scene est reporté |
|
|
| wgpu dans `simple.rs` | Via `app.renderer.device()`/`format()` : l'utilisateur n'importe pas wgpu |
|
|
| Helper quad haut niveau | Reporté (éventuel `Scene::add_quad_entity`) |
|
|
|
|
## 5. Notes ouvertes / idées
|
|
|
|
- Le "label" d'entité n'est pour l'instant pas utilisé au rendu (juste itéré). OK pour le MVP.
|
|
- `num_indices == 0` dans le cas non indexé : bien gérer le branchement indexé/non indexé (copié depuis
|
|
`Renderer::render` actuel).
|
|
- Après cette étape, l'ajout de lumières/textures/caméras = simple ajout de données à la Scene
|
|
(voir `docs/ROADMAP.md` Phases 2-4 et `docs/PLAN.md` Phase 4).
|