Compare commits

..

5 Commits

Author SHA1 Message Date
Jérôme Bousquié 4acf1d821d feat(core): expose frame view and auto-render the scene
- AppHandler::render now receives the current &Frame; its default
  implementation renders the whole scene automatically via
  app.render_scene(frame.view()) (Option A). Users can simply not
  implement render for full auto-rendering.
- Add Renderer::render_scene: batch-renders every scene entity in a
  single render pass. Factored per-mesh draw logic into a private
  draw_entity helper shared with Renderer::render.
- Add App::render_scene(view) delegating to the Renderer.
- Fill simple.rs with a real quad (mesh/material/entity) without
  importing wgpu; the scene now auto-renders via the trait default.
2026-09-16 10:36:35 +02:00
Jérôme Bousquié 628c125925 docs: move DRAFT.md into docs/ and confirm step decisions
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.
2026-09-16 10:23:42 +02:00
Jérôme Bousquié 3b0db5fa12 docs: add DRAFT.md with detailed implementation plan for scene auto-rendering 2026-09-16 10:08:35 +02:00
Jérôme Bousquié e6f190e035 docs: add API documentation guidelines (rustdoc) in docs/DOCUMENTATION.md
Codifies the systematic documentation of public items with the API docs
in mind, based on the incidents from the last session: backticks around
every type (Option<Self>, Arc<Mesh>, [f32; 3]) to avoid unresolved-link
and unclosed-HTML-tag warnings, one doc comment per public item, the
#![warn(missing_docs)] policy, verifying '0 warnings' via cargo doc
before committing, and doc-test conventions (rust vs ignore blocks).

Applies to wsg-lib development from now on.
2026-09-16 09:45:33 +02:00
Jérôme Bousquié d81743481b docs: fix rustdoc warnings and enforce full API doc coverage
- Wrap in backticks every bare type in rustdoc comments (vertex.rs,
  frame.rs, pipeline_cache.rs, material.rs, mesh.rs, scene.rs, error.rs)
  so rustdoc no longer misreads them as intra-doc links or HTML tags.
- Add a missing doc comment on the public Frame struct.
- Add #![warn(missing_docs)] to the crate root so unevidenced public
  items are surfaced going forward.

cargo doc --no-deps now generates with zero warnings.
2026-09-16 09:32:25 +02:00
17 changed files with 389 additions and 46 deletions
+80
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+5 -1
View File
@@ -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,
+54 -12
View File
@@ -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;
}
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);
draw_entity(&mut render_pass, mesh, material);
}
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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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)]
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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],
}
+3 -3
View File
@@ -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)>,
+1 -1
View File
@@ -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;