Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4acf1d821d | |||
| 628c125925 | |||
| 3b0db5fa12 | |||
| e6f190e035 | |||
| d81743481b |
@@ -0,0 +1,80 @@
|
||||
---
|
||||
type: Rule
|
||||
title: Guidelines for API Documentation (rustdoc)
|
||||
description: Consignes pour documenter systématiquement le code en pensant à la génération de documentation d'API (rustdoc) dans le projet WSG
|
||||
tags: [documentation, rustdoc, api, guidelines, wsg-lib]
|
||||
status: active
|
||||
stale_after: 2027-01-31T00:00:00Z
|
||||
related: [docs/rules/DOCUMENTATION.md]
|
||||
generated: { by: "human:jerome", at: 2026-09-16T00:00:00Z }
|
||||
---
|
||||
|
||||
# Consignes de Documentation d'API (rustdoc)
|
||||
|
||||
Ce document complète `docs/rules/DOCUMENTATION.md` (règles générales, en anglais). Il définit la
|
||||
manière **concrète** d'écrire les commentaires pour que `cargo doc` produise une documentation d'API
|
||||
de qualité, **sans aucun avertissement**. Ces consignes s'appliquent à toute modification de code du
|
||||
projet, y compris la doc elle-même.
|
||||
|
||||
## Principe
|
||||
|
||||
Chaque item public (`pub struct`, `pub enum`, `pub trait`, `pub fn`, `pub const`, module, crate)
|
||||
est documenté **au moment où il est écrit**, pas après coup. On ne documente pas seulement *ce que*
|
||||
fait le code, mais *pourquoi* il existe et *quand/par qui* il est appelé (cf. règles générales :
|
||||
description ≤ 3 lignes, étapes internes ≤ 3 lignes si corps > 15 lignes, points techniques ≤ 3 lignes).
|
||||
|
||||
## Règles techniques (issues d'incidents réels)
|
||||
|
||||
### 1. Types et code entre backticks
|
||||
|
||||
Tout nom de type, fonction, variable ou fragment de code dans un commentaire est enveloppé de
|
||||
backticks (`` ` ``). Sans cela, rustdoc croit :
|
||||
|
||||
- à un **lien intra-doc** pour tout ce qui est entre crochets → avertissement `unresolved link`
|
||||
(ex. `[f32; 3]` au lieu de `` `[f32; 3]` ``) ;
|
||||
- à une **balise HTML** pour tout `<X>` → avertissement `unclosed HTML tag`
|
||||
(ex. `Option<Self>`, `Arc<Mesh>`, `Handle<T>` au lieu de `` `Option<Self>` ``…).
|
||||
|
||||
À proscrire : `Option<Self>`, `Vec<Mesh>`, `[f32; 3]`, `Arc<Material>`.
|
||||
À écrire : `` `Option<Self>` ``, `` `Vec<Mesh>` ``, `` `[f32; 3]` ``, `` `Arc<Material>` ``.
|
||||
|
||||
### 2. Un commentaire par item public
|
||||
|
||||
- Crate / module : `//!` en tête de fichier.
|
||||
- Item (struct, enum, trait, fn, const, champs) : `///` juste au-dessus.
|
||||
- Toute structure publique dont seuls les champs sont commentés déclenche `missing_docs` :
|
||||
commenter **aussi** la structure elle-même.
|
||||
|
||||
### 3. Faire remonter les trous de couverture
|
||||
|
||||
`#![warn(missing_docs)]` est actif en tête de `lib.rs`. Tout nouvelle item public sans doc remonte
|
||||
en **warning** au build de la doc : c'est voulu, il faut le corriger avant de committer.
|
||||
Un rendu de doc doit toujours terminer par « generated 0 warnings ».
|
||||
|
||||
### 4. Vérifier le rendu avant de committer
|
||||
|
||||
Après toute modification de doc :
|
||||
|
||||
```bash
|
||||
cargo doc -p wsg-lib --no-deps # doit afficher « generated 0 warnings »
|
||||
cargo doc --no-deps --open # ouvre la doc dans le navigateur
|
||||
cargo check --workspace # compile sans erreur
|
||||
```
|
||||
|
||||
### 5. Tests de documentation
|
||||
|
||||
- Un bloc de code ```rust``` dans un commentaire est compilé et exécuté par `cargo test` (doc-test) :
|
||||
il doit compiler **et** tourner.
|
||||
- Pour un extrait non autonome (dépend de winit/wgpu, etc.), utiliser ` ```ignore ```` ```` au lieu de
|
||||
` ```rust ``` ` afin de ne pas casser `cargo test`.
|
||||
|
||||
## Récapitulatif
|
||||
|
||||
| Situation | À faire | À éviter |
|
||||
|---|---|---|
|
||||
| Type dans une doc | `` `Arc<Mesh>` `` | `Arc<Mesh>` |
|
||||
| Tableau dans une doc | `` `[f32; 3]` `` | `[f32; 3]` |
|
||||
| Item public sans description | ajouter `///` | laisser vide |
|
||||
| Struct publique | doc sur la struct + les champs | doc sur les champs seuls |
|
||||
| Extraits exécutables | ` ```rust ``` ` | ` ```ignore ``` ` |
|
||||
| Extraits non autonomes | ` ```ignore ``` ` | ` ```rust ``` ` |
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
# 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).
|
||||
+55
-8
@@ -1,19 +1,66 @@
|
||||
//! Workflow déclaratif minimal (~15 lignes), sans manipulation WGPU explicite.
|
||||
//! Workflow déclaratif minimal, sans manipulation WGPU explicite dans ce fichier.
|
||||
//! `AppBuilder` ouvre la fenêtre, construit le `Context`/`Renderer` et fait tourner la boucle
|
||||
//! update → render → present. Le rendu automatisé de la scène n'est pas encore en place
|
||||
//! (README, Roadmap étape 1) : `render()` est donc vide pour l'instant.
|
||||
//! update → render → present. La scène se rend automatiquement : la méthode `render()` par défaut
|
||||
//! du trait `AppHandler` appelle `app.render_scene(frame.view())`, donc l'utilisateur n'implémente
|
||||
//! même pas `render` ici — il ne fait que remplir `app.scene` avec un mesh, un matériau et une entité.
|
||||
use std::sync::Arc;
|
||||
use wsg_lib::AppHandler;
|
||||
use wsg_lib::app::AppBuilder;
|
||||
use wsg_lib::resources::{Material, Mesh, Vertex};
|
||||
use wsg_lib::utils::WsgError;
|
||||
use wsg_lib::{App, AppHandler};
|
||||
|
||||
struct MonQuad;
|
||||
|
||||
impl AppHandler for MonQuad {
|
||||
fn render(&mut self, _app: &mut App) {}
|
||||
}
|
||||
impl AppHandler for MonQuad {}
|
||||
|
||||
#[pollster::main]
|
||||
async fn main() -> Result<(), WsgError> {
|
||||
let app = AppBuilder::new().title("WSG Simple").build().await?;
|
||||
let mut app = AppBuilder::new().title("WSG Simple").build().await?;
|
||||
|
||||
// Enregistrement du shader, création du matériau et du mesh du quad (sans importer wgpu).
|
||||
app.cache
|
||||
.register_shader("basic", wsg_lib::utils::BASIC_SHADER_PATH)
|
||||
.unwrap();
|
||||
let vertices = [
|
||||
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],
|
||||
}, // Haut-Gauche (Rouge)
|
||||
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],
|
||||
}, // Haut-Droite (Vert)
|
||||
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],
|
||||
}, // Bas-Droite (Bleu)
|
||||
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],
|
||||
}, // Bas-Gauche (Jaune)
|
||||
];
|
||||
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||
|
||||
let mesh = Arc::new(Mesh::new(app.renderer.device(), &vertices, Some(&indices)));
|
||||
let material = Arc::new(Material::new(
|
||||
app.renderer.format(),
|
||||
"basic",
|
||||
&mut app.cache,
|
||||
));
|
||||
|
||||
app.scene.add_mesh("quad_mesh", mesh).unwrap();
|
||||
app.scene.add_material("basic_material", material).unwrap();
|
||||
app.scene
|
||||
.add_entity("quad", "quad_mesh", "basic_material")
|
||||
.unwrap();
|
||||
|
||||
app.run(MonQuad)
|
||||
}
|
||||
|
||||
+10
-2
@@ -68,8 +68,8 @@ impl App {
|
||||
// Rendering logic
|
||||
let frame = self.context.get_next_frame();
|
||||
|
||||
// On appelle le render() de l'utilisateur
|
||||
handler.render(&mut self);
|
||||
// On appelle le render() de l'utilisateur (reçoit la frame courante)
|
||||
handler.render(&mut self, &frame);
|
||||
// On présente automatiquement
|
||||
self.renderer.present(frame);
|
||||
}
|
||||
@@ -84,6 +84,14 @@ impl App {
|
||||
})
|
||||
.map_err(|_| WsgError::WindowSystem)
|
||||
}
|
||||
|
||||
/// Renders every entity in `self.scene` into the given color view in a single batched render pass.
|
||||
/// Called automatically each frame by the default `AppHandler::render`, or manually by users
|
||||
/// who override `render` to control drawing themselves.
|
||||
/// Inputs: view — the frame's texture view acting as the color attachment target.
|
||||
pub fn render_scene(&self, view: &wgpu::TextureView) {
|
||||
self.renderer.render_scene(view, &self.scene);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for constructing a configured `App` instance with custom title and dimensions.
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
//! - **context**: provides the Surface from which Frame acquires the current texture.
|
||||
//! - **renderer**: passes Frame's TextureView to render() as the color attachment target.
|
||||
//! - **error**: does not use errors directly; Frame::new() panics on acquisition failure while
|
||||
//! Frame::try_new() returns Option<Self> for graceful recovery.
|
||||
//! Frame::try_new() returns `Option<Self>` for graceful recovery.
|
||||
//!
|
||||
//! ## Architecture Notes (per ARCHI_APP.md)
|
||||
//! - **Phase d'Exécution**: Frame is acquired at the start of each render loop iteration and released after rendering.
|
||||
//! - **Ergonomie**: Users interact with frames through Renderer::render() + Renderer::present(), not directly with wgpu handles.
|
||||
|
||||
/// A per-frame RAII wrapper around the surface texture and its `TextureView`.
|
||||
/// Owned for the duration of a single render pass: acquired via `Frame::new()`/`try_new()` at the
|
||||
/// start of each frame loop iteration, used by `Renderer` as the color attachment target, then
|
||||
/// dropped after `present()` submits it to the GPU queue.
|
||||
pub struct Frame {
|
||||
/// The GPU surface texture representing the current display buffer to be presented.
|
||||
pub surface_texture: wgpu::SurfaceTexture,
|
||||
|
||||
+53
-11
@@ -21,6 +21,7 @@
|
||||
use crate::core::Context;
|
||||
use crate::core::Frame;
|
||||
use crate::resources::{Material, Mesh};
|
||||
use crate::scene::Scene;
|
||||
|
||||
/// The Executor layer of the architecture. Holds shared references to Device and Queue from Context,
|
||||
/// plus the surface texture format. Executes WGPU rendering commands by binding Materials and Meshes
|
||||
@@ -80,18 +81,40 @@ impl Renderer {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&material.pipeline);
|
||||
if mesh.num_vertices > 0 {
|
||||
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||
} else {
|
||||
// If no vertices, skip drawing entirely (nothing to render)
|
||||
return;
|
||||
draw_entity(&mut render_pass, mesh, material);
|
||||
}
|
||||
if let Some(index_buffer) = &mesh.index_buffer {
|
||||
render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||
render_pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
|
||||
} else {
|
||||
render_pass.draw(0..mesh.num_vertices, 0..1);
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
|
||||
/// Renders every entity in `scene` into the given color view within a single batched render pass.
|
||||
/// This avoids allocating a separate encoder and render pass per entity (which the low-level
|
||||
/// `render` does), minimizing GPU submissions. Called automatically each frame by the default
|
||||
/// `AppHandler::render` through `App::render_scene`.
|
||||
/// Inputs: view — the frame's texture view color attachment; scene — the scene whose entities are drawn.
|
||||
pub fn render_scene(&self, view: &wgpu::TextureView, scene: &Scene) {
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("scene encoder"),
|
||||
});
|
||||
|
||||
{
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("scene render pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view,
|
||||
resolve_target: None,
|
||||
depth_slice: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for (_label, mesh, material) in scene.iter_entities() {
|
||||
draw_entity(&mut render_pass, mesh, material);
|
||||
}
|
||||
}
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
@@ -116,3 +139,22 @@ impl Renderer {
|
||||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a Material pipeline and Mesh buffers into an active render pass and issues the draw call.
|
||||
/// Shared by `Renderer::render` and `Renderer::render_scene` to avoid duplicated draw logic.
|
||||
/// Draws indexed geometry when an index buffer exists, otherwise falls back to a non-indexed draw.
|
||||
/// Inputs: pass (active render pass), mesh (geometry to draw), material (pipeline to bind).
|
||||
fn draw_entity(pass: &mut wgpu::RenderPass<'_>, mesh: &Mesh, material: &Material) {
|
||||
if mesh.num_vertices == 0 {
|
||||
// No vertices — nothing to render.
|
||||
return;
|
||||
}
|
||||
pass.set_pipeline(&material.pipeline);
|
||||
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||
if let Some(index_buffer) = &mesh.index_buffer {
|
||||
pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||
pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
|
||||
} else {
|
||||
pass.draw(0..mesh.num_vertices, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -17,18 +17,23 @@
|
||||
//! the engine "brick by brick" through direct Context/PipelineCache/Renderer manipulation if needed.
|
||||
|
||||
use crate::app::App;
|
||||
use crate::core::Frame;
|
||||
|
||||
/// Trait defining user-provided game logic injected into the render loop at two callback points.
|
||||
/// Users implement this trait to define what happens per-frame: update (pre-render logic) and
|
||||
/// render (draw call execution). Default implementations provide empty update for convenience.
|
||||
/// render (draw call execution). Default implementations provide empty update and automatic
|
||||
/// scene rendering for convenience.
|
||||
pub trait AppHandler {
|
||||
/// Called once per frame before rendering begins. Used for physics updates, input processing,
|
||||
/// entity management, and any other pre-render logic. Default implementation does nothing.
|
||||
/// Inputs: _app — mutable reference to the App facade providing access to all subsystems.
|
||||
fn update(&mut self, _app: &mut App) {}
|
||||
/// Called during each RedrawRequested event after frame acquisition. Used for executing draw calls
|
||||
/// by iterating Scene entities and calling app.renderer.render(view, mesh, material) per entity.
|
||||
/// Must be implemented — called every frame that needs rendering.
|
||||
/// Inputs: app — mutable reference to the App facade providing access to all subsystems.
|
||||
fn render(&mut self, app: &mut App);
|
||||
/// Called during each RedrawRequested event after frame acquisition, receiving the current frame.
|
||||
/// Used for custom draw call execution. Default implementation renders the whole scene
|
||||
/// automatically (`app.render_scene(frame.view())`), so most users don't need to override it.
|
||||
/// Advanced users override this method to control drawing manually.
|
||||
/// Inputs: app — mutable reference to the App facade; frame — the acquired frame exposing its view.
|
||||
fn render(&mut self, app: &mut App, frame: &Frame) {
|
||||
app.render_scene(frame.view());
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -25,14 +25,17 @@
|
||||
//! use wsg_lib::utils::BASIC_SHADER;
|
||||
//! ```
|
||||
|
||||
// Warn if a public API item has no rustdoc comment, keeping API coverage at 100%.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod app;
|
||||
pub mod core;
|
||||
pub mod handler;
|
||||
pub mod math;
|
||||
pub mod pipeline;
|
||||
pub mod resources;
|
||||
pub mod scene;
|
||||
pub mod utils;
|
||||
pub mod math;
|
||||
|
||||
/// Re-export of the high-level application facade for convenient top-level access.
|
||||
/// Users create App instances via `AppBuilder`, then call `.run(handler)` to start the application.
|
||||
|
||||
+2
-2
@@ -15,9 +15,9 @@
|
||||
//! - `geometry.rs`: Defines the `Geometry` struct for mesh data storage
|
||||
//! - `camera.rs`: Defines the `Camera` struct and view/projection matrix calculations
|
||||
|
||||
pub mod transform;
|
||||
pub mod geometry;
|
||||
pub mod transform;
|
||||
|
||||
// Re-exports
|
||||
pub use transform::Transform;
|
||||
pub use geometry::Geometry;
|
||||
pub use transform::Transform;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! - `Transform`: Core struct for position/rotation/scale
|
||||
//! - `to_matrix()`: Converts transform to a 4x4 matrix
|
||||
|
||||
use glam::{Vec3, Quat, Mat4};
|
||||
use glam::{Mat4, Quat, Vec3};
|
||||
|
||||
/// Represents a 3D transformation with translation, rotation, and scale.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
||||
@@ -198,7 +198,7 @@ impl PipelineCache {
|
||||
|
||||
/// Retrieves a cached RenderPipeline by shader_id without creating one.
|
||||
/// Inputs: shader_id (unique key into the cache).
|
||||
/// Returns Some(Arc<RenderPipeline>) if found, None otherwise. Called by renderer code for pipeline inspection.
|
||||
/// Returns Some(`Arc<RenderPipeline>`) if found, None otherwise. Called by renderer code for pipeline inspection.
|
||||
pub fn get(&self, shader_id: &str) -> Option<&Arc<wgpu::RenderPipeline>> {
|
||||
self.pipelines.get(shader_id)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! ## Architecture Notes (per ARCHI_APP.md)
|
||||
//! - **Identifiants**: Each Material is registered in Scene by string identifier, enabling dynamic access
|
||||
//! during the render loop without borrow checker issues. The shader_id serves as the Handle<T> key.
|
||||
//! during the render loop without borrow checker issues. The shader_id serves as the `Handle<T>` key.
|
||||
//! - **Phase de Déclaration**: Materials are instantiated once in the declarative phase before the render loop begins.
|
||||
|
||||
use crate::pipeline::PipelineCache;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//!
|
||||
//! ## Architecture Notes (per ARCHI_APP.md)
|
||||
//! - **Identifiants**: Each Mesh is registered in Scene by string identifier, enabling dynamic access
|
||||
//! during the render loop without borrow checker issues. The identifier serves as the Handle<T> key.
|
||||
//! during the render loop without borrow checker issues. The identifier serves as the `Handle<T>` key.
|
||||
//! - **Phase de Déclaration**: Meshes are instantiated once in the declarative phase before the render loop begins.
|
||||
//! - **Performance**: Multiple entities can reference the same Mesh, reducing memory footprint for repeated geometry.
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vertex {
|
||||
/// XYZ coordinates of this vertex in world space. Offset: 0 bytes (12 bytes total as [f32;3]).
|
||||
/// XYZ coordinates of this vertex in world space. Offset: 0 bytes (12 bytes total as `[f32; 3]`).
|
||||
pub position: [f32; 3],
|
||||
/// XYZ coordinates of the vertex normal. Offset: 12 bytes (12 bytes total as [f32;3]).
|
||||
/// XYZ coordinates of the vertex normal. Offset: 12 bytes (12 bytes total as `[f32; 3]`).
|
||||
pub normal: [f32; 3],
|
||||
/// UV texture coordinates. Offset: 24 bytes (8 bytes total as [f32;2]).
|
||||
/// UV texture coordinates. Offset: 24 bytes (8 bytes total as `[f32; 2]`).
|
||||
pub uv: [f32; 2],
|
||||
/// RGBA color values. Offset: 32 bytes (16 bytes total as [f32;4]).
|
||||
/// RGBA color values. Offset: 32 bytes (16 bytes total as `[f32; 4]`).
|
||||
pub color: [f32; 4],
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//! ## Architecture Notes (per ARCHI_APP.md)
|
||||
//! - **La Recette**: Scene is central to the "App" facade workflow. In the Phase de Déclaration, users call add_mesh(), add_material(), and add_entity()
|
||||
//! to build the resource depot. During Phase d'Exécution, Renderer iterates Scene entities for rendering.
|
||||
//! - **Identifiants**: All resource registration uses string identifiers (Handle<T>/String pattern), guaranteeing memory safety
|
||||
//! - **Identifiants**: All resource registration uses string identifiers (`Handle<T>`/String pattern), guaranteeing memory safety
|
||||
//! and avoiding borrow checker issues during dynamic updates.
|
||||
//! - **Ergonomie**: Users interact only with entity-level operations (add/remove/get) rather than wgpu buffers/pipelines directly.
|
||||
|
||||
@@ -18,9 +18,9 @@ use std::sync::Arc;
|
||||
/// and maps entity labels to their associated mesh+material pairs for rendering iteration.
|
||||
/// Created once during application setup; entities are added before the render loop starts.
|
||||
pub struct Scene {
|
||||
/// Map of mesh identifiers to owned Arc<Mesh> instances. Populated via `add_mesh()`.
|
||||
/// Map of mesh identifiers to owned `Arc<Mesh>` instances. Populated via `add_mesh()`.
|
||||
meshes: HashMap<String, Arc<Mesh>>,
|
||||
/// Map of material identifiers to owned Arc<Material> instances. Populated via `add_material()`.
|
||||
/// Map of material identifiers to owned `Arc<Material>` instances. Populated via `add_material()`.
|
||||
materials: HashMap<String, Arc<Material>>,
|
||||
/// Map of entity labels to (mesh_id, material_id) associations. Populated via `add_entity()`.
|
||||
entities: HashMap<String, (String, String)>,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! ## Interaction with Other Modules
|
||||
//! - **context** uses WsgError as return types for `new()`, `configure()`, and `begin_frame()`.
|
||||
//! - **renderer** does not use errors directly (render panics on invalid state rather than returning Result).
|
||||
//! - **frame** does not use errors — Frame::new() panics while Frame::try_new() returns Option<Self>.
|
||||
//! - **frame** does not use errors — Frame::new() panics while Frame::try_new() returns `Option<Self>`.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user