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.
7.7 KiB
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 docsdocs/*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(): acquiertFrameviacontext.get_next_frame(), appellehandler.render(&mut self), puisrenderer.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é pardocs/PLAN.md(§ Phase 2 intégra Scene, check-list) etdocs/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 dansmanual.rs) : il ouvre 1 encoder + 1 render pass par objet, dessine, soumet.Scenea déjà :add_mesh,add_material,add_entity,iter_entities() -> (label, mesh, mat),get_mesh,get_material,remove_entity.Materialporte déjà saArc<RenderPipeline>(compilée viaPipelineCache). La scène stocke desArc<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
- Que
AppHandler::render()reçoive la vue/frame courante. - Que la scène se rende automatiquement (
app.render(scene)), sans quesimple.rstouche à wgpu. - Que
simple.rsaffiche le quad (4 sommets, 6 indices, matériaubasic), 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 :
fn render(&mut self, app: &mut App, frame: &Frame);
Frameest un type de bibliothèque (core::Frame) qui exposeframe.view()→&wgpu::TextureView. C'est plus riche et plus stable que de passer leTextureViewbrut : on garde une API bibliothèque.- Adapter
handler.rsdocs (consignesdocs/DOCUMENTATION.md: backticks, description ≤3 lignes, ce que/qui/quand).
Fichier : lib/src/app.rs, dans App::run, branche RedrawRequested :
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 :
frameest une valeur owned détachée deself.contextune fois acquise, on peut donc la passer par référence en même temps que&mut selfsans 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 :
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 parmanual.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 :
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) :
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 viaScene+AppBuilder). - Enregistrer le shader :
app.cache.register_shader("basic", utils::BASIC_SHADER_PATH)(le shader_id"basic"fonctionne déjà en fallback surBASIC_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 dansrun()avantapp.run(...)— à voir selon oùappest constructible ; le plus simple : dansupdate(&mut self, app)une fois).
⚠️ Les vertex passent par
Mesh::new(device, ...)qui exigewgpu::Device. Décision prise : pour l'étape 1, utiliserapp.renderer.device()/app.renderer.format()(accès bibliothèque — l'utilisateur n'importe pas wgpu). Un helper haut niveauScene::add_quad_entitypourra être ajouté plus tard.
Étape 3.5 — Validation
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 == 0dans le cas non indexé : bien gérer le branchement indexé/non indexé (copié depuisRenderer::renderactuel).- Après cette étape, l'ajout de lumières/textures/caméras = simple ajout de données à la Scene
(voir
docs/ROADMAP.mdPhases 2-4 etdocs/PLAN.mdPhase 4).