Files
wsg/docs/DRAFT.md
T

141 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DRAFT — Étape 11 : Gestion du Resize (cycle de vie Surface + Depth)
> 📅 **2026-09-18** — Plan de l'étape suivante.
> **Source de vérité** = code + README.md. Ce document est vidé à la complétion de l'étape.
> **Références** : ROADMAP Phase 4.4 · PLAN Phase 4 · décision D3 (2026-09-18).
---
## Contexte
L'application ne gère **aucun** resize aujourd'hui : la surface est configurée une seule fois,
au démarrage, dans `AppRunner::resumed` (`context.configure(&adapter, width, height)`), et la
**depth texture** est allouée une seule fois, à la taille initiale, dans `Renderer::new`
(Étape 9). Quand l'utilisateur redimensionne la fenêtre :
- la surface reste configurée à l'ancienne taille → `get_current_texture()` retourne
`Suboptimal`/`Outdated`, le rendu est cassé ou artefacté ;
- la depth texture reste à l'ancienne taille → les attachments color/depth n'ont plus la
même taille → **erreur de validation wgpu** (tailles des attachments divergentes).
Le helper isolé `create_depth_texture(device, width, height)` (Étape 9, D3) rend le recreate
de la depth texture trivial. C'est le chantier dédié planifié en **Phase 4.4**, hors périmètre
de l'Étape 9.
## Objectif
Rendre le moteur robuste au redimensionnement de la fenêtre : à chaque `WindowEvent::Resized`,
reconfigurer la surface et recréer la depth texture à la nouvelle taille, en gardant la
scène et les pipelines valides.
**Critère d'acceptation** : redimensionner la fenêtre (agrandir, rétrécir) ne produit ni
crash, ni artefact, ni erreur de validation wgpu ; le rendu se met à jour à la nouvelle
taille ; le rapport hauteur/largeur (aspect) de la projection suit la fenêtre en continu.
## Périmètre
**Inclus**
- Handler `WindowEvent::Resized` dans `AppRunner::window_event` (`lib/src/app.rs`).
- Reconfiguration de la surface (`Context::configure`) à la nouvelle taille.
- Recréation de la depth texture (`Renderer::resize_depth`).
- Garde contre les tailles invalides (0).
- Mise à jour du format de surface et re-synchronisation Renderer ↔ Scene si nécessaire.
**Hors périmètre**
- Le present mode (FIFO figé — voir Notes de Décision ROADMAP).
- Le culling/rendu GPU-driven (Phase 3).
- La LOD / batching / HDR (Phase 4.3).
- Toute modification du shader ou des bind groups.
## Décisions
| Id | Décision | Raison |
|----|----------|--------|
| D1 | Le resize est encapsulé dans une méthode `App::resize(&mut self, w, h)` appelée par `AppRunner::window_event` | La logique reste testable et réutilisable, distincte de la couche winit ; `window_event` reste fine (dispatch uniquement). |
| D2 | La depth texture est recréée via `Renderer::resize_depth(w, h)`, qui réutilise le helper `create_depth_texture` existant | Cohérent avec l'Étape 9 (D3) ; le helper isolé existe déjà et est prêt. |
| D3 | Garde `if w == 0 \|\| h == 0 { return; }` : on ne reconfigure jamais à une taille nulle (fenêtre minimisée) | `Resized(0x0)` arrive au minimize ; configurer une surface 0×0 est une erreur wgpu. |
| D4 | Le format de surface est comparé avant/après `configure` : s'il change, on re-synchronise Renderer et Scene ; dans le cas normal (stable sRGB) on se contente du swap surface+depth | Le format est déterministe pour une même fenêtre (premier sRGB) ; le cas « format changé » est rare mais documenté et structuré. |
| D5 | L'aspect de projection reste dérivé en continu de `window().inner_size()` dans `App::render_scene` (déjà le cas) — pas de champ de taille « stale » à maintenir | Évite un état redondant : la source de vérité du rendu est la taille live de la fenêtre. Les champs `App.width/height` ne servent qu'à l'init. |
| D6 | Pendant une taille invalide (0), on ne rend pas la frame (garde dans `RedrawRequested`) | Évite un `get_next_frame()`/rendu sur une surface de taille nulle. |
## Tâches détaillées
### 11.1 `App::resize` (orchestration du resize)
- [ ] Ajouter une méthode `resize(&mut self, width: u32, height: u32) -> Result<(), WsgError>` sur `App` (`lib/src/app.rs`) :
- [ ] Récupérer `self.context` (Option) → ok/err si absent.
- [ ] `let new_format = self.context().configure(&self.context().adapter, width, height)?;`
(reconfigure la surface à la nouvelle taille ; renvoie le format choisi).
- [ ] `self.renderer_mut().resize_depth(width, height);` (recrée depth à la nouvelle taille).
- [ ] Synchroniser le format : mettre à jour `Renderer::format` (via un setter, cf. 11.2)
et, si `new_format != format_anterieur`, re-synchroniser la Scene (cf. D4).
- [ ] Retourner `Ok(())`.
- [ ] Gérer l'erreur : si `configure` échoue, propager `WsgError` sans crash silencieux.
### 11.2 `Renderer::resize_depth` (+ synchronisation du format)
- [ ] Ajouter `resize_depth(&mut self, width: u32, height: u32)` sur `Renderer` (`lib/src/core/renderer.rs`) :
- [ ] `let (t, v) = create_depth_texture(&self.device, width, height);`
- [ ] Remplacer `self._depth_texture = t; self.depth_view = v;` (l'ancienne texture est
dropée par le remplacement — pas de fuite, pas de double allocation).
- [ ] (Optionnel, D4) Ajouter un accès pour mettre à jour `Renderer::format` si le format change ;
le champ `format` est actuellement `wgpu::TextureFormat` non-mutable depuis l'extérieur.
- [ ] Vérifier que `create_depth_texture` reste privé au module (pas d'exposition publique).
### 11.3 Handler `WindowEvent::Resized` dans `AppRunner::window_event`
- [ ] Dans le `match event` de `window_event` (`lib/src/app.rs`), ajouter :
- [ ] `WindowEvent::Resized(size)`:
- [ ] `let w = size.width as u32; let h = size.height as u32;`
- [ ] `if w == 0 || h == 0 { return; }` (D3).
- [ ] `if let Err(e) = app.resize(w, h) { /* log/propager ; ne pas paniquer */ }`
- [ ] `app.window().request_redraw();` pour rendre immédiatement la nouvelle taille.
- [ ] (D6) Dans `WindowEvent::RedrawRequested`, gardez la garde : si la taille courante de la
fenêtre est 0, sauter `get_next_frame()`/`render`/`present` pour cette frame.
### 11.4 Synchronisation du format Renderer ↔ Scene (D4)
- [ ] Après `configure`, comparer `new_format` avec le format en cours :
- [ ] Cas normal (identique) : rien à faire, le swap surface+depth suffit.
- [ ] Cas format changé : re-initialiser le GPU de la Scene (`Scene::init_gpu(device, queue,
new_format)`) et documenter que les pipelines doivent être revalids — pour le MVP,
lever une erreur explicite (le format est stable pour une même fenêtre ; ce chemin est
structuré mais pas exercé couramment).
- [ ] S'assurer que `Renderer::format` et `Scene` (via `SceneGpu`) portent le même format.
### 11.5 Exemple / vérification manuelle
- [ ] Aucun exemple ne doit changer de code (le resize est transparent).
- [ ] Vérification manuelle : lancer `cube` (ou `simple`), redimensionner la fenêtre
(agrandir + rétrécir), confirmer : pas de crash, pas d'artefact, rendu correct à la
nouvelle taille, aspect correct (pas de distorsion de la projection).
## Fichiers touchés
| Fichier | Changement |
|---------|-----------|
| `lib/src/app.rs` | `App::resize` (nouveau) ; `window_event` : branchement `Resized` + garde 0 dans `RedrawRequested`. |
| `lib/src/core/renderer.rs` | `Renderer::resize_depth` (nouveau) ; éventuellement setter du format (D4). |
| `lib/src/core/context.rs` | **Aucun** — `configure` existe déjà et reconfigure à une taille donnée. |
| `lib/src/scene/scene.rs` | **Aucun** en cas normal ; seul le chemin format-changé (D4) toucherait `init_gpu`. |
> `create_depth_texture` (helper) et `Context::configure` existent déjà — réutilisés, pas créés.
## Check-list de vérification
- [ ] Redimensionner la fenêtre n'émet aucune erreur de validation wgpu (attachments color/depth
de même taille).
- [ ] La surface est reconfigurée à la taille réelle de la fenêtre (`Context::configure`).
- [ ] La depth texture est recréée à la taille réelle (`Renderer::resize_depth`).
- [ ] Pas de crash au minimize (garde `w==0 || h==0`, D3) ni au restore.
- [ ] L'aspect de la projection suit la fenêtre (pas de distorsion au redimensionnement).
- [ ] Le format Renderer == format Scene (synchronisation D4).
- [ ] `cargo build --workspace` et `cargo test --workspace` passent ; exemples `cube`/`simple`
compiles sans changement.
- [ ] Pas de fuite : l'ancienne depth texture est bien dropée au remplacement.
## Risques & notes
- **Minimize/restore** : `Resized(0x0)` puis `Resized(n,m)` — la garde D3 + la reconfiguration au
restore couvrent le cycle complet.
- **Format changé** : pathologique pour une même fenêtre (le choix est déterministe). Structuré
mais non exercé couramment (D4) ; si jamais un backend change de format, le MVP lève une
erreur claire plutôt que de produire un rendu corrompu.
- **Présence du `adapter`** : `configure` nécessite `&adapter` — `Context` porte déjà `adapter`
(utilisé dans `resumed`), donc accessible sans nouvelle API.