From 1258668e4fe0070265a1b6b804d7679d842ce513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Bousqui=C3=A9?= Date: Fri, 18 Sep 2026 17:37:22 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20vid=C3=A9=20DRAFT=20(=C3=89tape=2011=20?= =?UTF-8?q?resize=20confirm=C3=A9e=20au=20runtime)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/DRAFT.md | 158 +++++--------------------------------------------- 1 file changed, 13 insertions(+), 145 deletions(-) diff --git a/docs/DRAFT.md b/docs/DRAFT.md index e748346..1e13d5b 100644 --- a/docs/DRAFT.md +++ b/docs/DRAFT.md @@ -1,147 +1,15 @@ -# DRAFT — Étape 11 : Gestion du Resize (cycle de vie Surface + Depth) +# DRAFT — Étape suivante -> 📅 **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). +> 📅 **Document vidé le 2026-09-18** (fin de l'Étape 11, Resize — Phase 4.4, bilan archivé +> dans l'historique git). Ce fichier accueillera le plan de l'étape suivante. > -> ✅ **FAIT (2026-09-18).** `App::resize(w,h)` (`lib/src/app.rs`) reconfigure la surface via -> `Context::configure` et recrée la depth texture via `Renderer::resize_depth` (+ `set_format`), -> avec re-synchronisation de la Scene si le format change (D4). `AppRunner::window_event` -> branche `WindowEvent::Resized` (garde 0×0, D3) et `RedrawRequested` (garde taille nulle, D6). -> `cargo build --workspace`, `cargo test --workspace` et la compilation des exemples passent. -> La vérification visuelle (redimensionner `cube`) reste à faire au runtime. - ---- - -## 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. +> **Étape 11 (2026-09-18) : Gestion du Resize (Surface + Depth) — FAIT & vérifié.** +> `App::resize(w,h)` (`lib/src/app.rs`) reconfigure la surface via `Context::configure` et +> recrée la depth texture via `Renderer::resize_depth` (+ `set_format`), avec re-synchronisation +> de la Scene si le format change (D4). `AppRunner::window_event` branche `WindowEvent::Resized` +> (garde 0×0, D3) et `RedrawRequested` (garde taille nulle, D6). +> Vérifié : `cargo build`/`test` workspace + compilation des exemples OK, et **au runtime** +> (exemple `cube`) le resize (agrandir + rétrécir) ne produit ni crash, ni artefact — rendu +> correct à la nouvelle taille, aspect non déformé. +> +> Source de vérité = code + README.md. Ce document est vidé à la complétion de chaque étape.