re-org en App

This commit is contained in:
Jérôme Bousquié
2026-07-07 16:42:37 +02:00
parent cfd8b421a2
commit 82ea16d118
28 changed files with 518 additions and 67 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
path = "src/lib.rs"
[dependencies]
wgpu = "30.0.0" # Vérifiez la version la plus récente
-28
View File
@@ -1,28 +0,0 @@
//! # WSG Library — Public API Surface
//!
//! Re-exports all submodules so consumers can access types via `wsg::mesh::Mesh`, etc.
//! The library's core responsibility is to abstract five wgpu objects (Instance, Surface, Adapter, Device, Queue)
//! into a single Context for simpler user interaction.
//!
//! ## Module Interactions
//! - **context** owns hardware resources (Device, Queue, Surface) across the frame lifecycle.
//! - **pipeline_cache** compiles WGSL shaders into RenderPipelines once, caching them via HashMap + Arc.
//! - **material** requests pipelines from PipelineCache to define per-object appearance.
//! - **mesh** holds vertex/index buffers sent to the GPU once at creation time.
//! - **renderer** orchestrates draw calls using Material + Mesh references passed in at render time.
//! - **vertex** defines the CPU-side vertex layout matching GPU shader input attributes.
//! - **error** provides application-level error types mapping each failure mode to a message.
//! - **conf** centralizes shared constants like shader paths and embedded fallback sources.
pub mod conf;
pub mod context;
pub mod error;
pub mod frame;
pub mod material;
pub mod mesh;
pub mod pipeline_cache;
pub mod renderer;
pub mod vertex;
/// Re-export of the application-level error type for direct use by consumers.
pub use error::WsgError; // For convenient import without path prefix
+31
View File
@@ -0,0 +1,31 @@
# wsg-lib Source Directory
## Overview
This is the source tree for `wsg-lib`, a Rust library wrapping [wgpu](https://github.com/gfx-rs/wgpu) for simple 3D drawing operations. The crate follows a layered architecture organized into five modules:
| Module | Responsibility |
|--------|---------------|
| **core** | Manager (Context) + Executor (Renderer) layers — GPU lifecycle and draw call orchestration |
| **resources** | Data types: Vertex (CPU-side), Mesh (GPU geometry), Material (appearance descriptor) |
| **pipeline** | PipelineCache — WGSL shader loading and RenderPipeline compilation cache |
| **scene** | Scene — resource depot and entity graph for declarative rendering setup |
| **utils** | Configuration constants and WsgError type |
## Architecture Pattern
The library supports two workflows:
- **Declarative (recommended)**: Use `Scene` to register resources and associate entities before the render loop begins. This is the "App facade" pattern described in [ARCHI_APP_FACADE](../../docs/ARCHI_APP_FACADE.md).
- **Manual**: Manipulate Context, Renderer, and PipelineCache directly for fine-grained control.
## Dependency Flow
```
scene → core → pipeline → utils
↘→ resources → utils (via Vertex offsets)
scene (consumes)
```
Each submodule's `mod.rs` re-exports its public types so consumers import through the module level rather than deep paths.
+18
View File
@@ -0,0 +1,18 @@
# Core Module — Manager and Executor Layers
## Overview
The `core` module contains two architectural layers that drive rendering:
| File | Responsibility |
|------|---------------|
| **context** | **Manager layer** — owns GPU hardware resource lifecycle (Instance, Surface, Adapter, Device, Queue). Initializes GPU at startup; orchestrates frame-by-frame rendering via begin_frame() / end_frame(). Does not own rendering logic. |
| **renderer** | **Executor layer** — owns Device/Queue references after initialization from Context. Orchestrates draw calls by binding Material pipelines and Mesh vertex data into a RenderPass. Does not own raw hardware resources externally or RenderPipelines/shaders. |
| **frame** | Per-frame RAII wrapper around the surface texture and its TextureView. Exists only for the duration of a single rendering pass. |
## Interaction with Other Modules
- **utils**: Context returns WsgError from all fallible methods; Renderer does not use errors directly.
- **pipeline**: Context requires Device reference during pipeline creation in new(); Renderer uses PipelineCache indirectly through Material.
- **resources**: Renderer consumes Mesh and Material instances for draw calls.
- **scene**: Renderer queries Scene for Material/Mesh pairs to render.
+12 -5
View File
@@ -1,20 +1,27 @@
//! # Context Module
//! # Context Module — Manager Layer (Hardware Lifecycle)
//!
//! The **Manager** layer of the architecture — owns hardware resource lifecycle (Device, Queue, Surface).
//! Initializes the GPU at startup; orchestrates frame-by-frame rendering via begin_frame() / end_frame().
//! Initializes the GPU at startup via async Builder pattern; orchestrates frame-by-frame rendering via begin_frame() / end_frame().
//! Does not own rendering logic (that belongs to Renderer) or shader compilation (PipelineCache).
//!
//! ## Interaction with Other Modules
//! - **renderer**: receives Device/Queue references to write rendered output into the TextureView.
//! - **pipeline_cache**: requires Device reference during pipeline creation in Context::new().
//! - **error**: returns WsgError variants from all fallible methods.
//!
//! ## Architecture Notes (per ARCHI_APP.md)
//! - **Phase de Déclaration**: Context is created once at application startup before the render loop begins.
//! This follows the declarative workflow where all GPU state is configured upfront.
//! - **Injection Async**: Context::new() is async because the runtime must be injected at creation time.
//! - **Accès Bas-Niveau**: Advanced users can bypass the Scene facade and manipulate Context directly
//! through App.renderer(), App.context(), etc., for fine-grained control over wgpu handles.
use std::sync::Arc;
use wgpu::{Adapter, Device, Instance, Queue, Surface};
use winit::window::Window;
use crate::error::WsgError;
use crate::frame::Frame;
use crate::utils::WsgError;
use crate::core::Frame;
/// Represents the GPU context. Holds all WGPU objects needed for rendering.
/// Created once at startup and shared across frames via Arc.
@@ -106,7 +113,7 @@ impl Context {
};
self.surface.configure(&self.device, &config);
// On retourne le format choisi pour que le Renderer puisse le stocker
// Return the chosen format so the Renderer can store it for pipeline creation
Ok(format)
}
+5 -1
View File
@@ -1,4 +1,4 @@
//! # Frame Module
//! # Frame Module — Per-Frame RAII Wrapper (Surface Texture + View)
//!
//! Defines `Frame`, a per-frame RAII wrapper around the surface texture and its TextureView.
//! A Frame exists only for the duration of a single rendering pass — it is acquired at the start
@@ -10,6 +10,10 @@
//! - **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.
//!
//! ## 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.
pub struct Frame {
/// The GPU surface texture representing the current display buffer to be presented.
+19
View File
@@ -0,0 +1,19 @@
//! # Core Module — Manager and Executor Layers
//!
//! Defines the two architectural layers that drive rendering: **Context** (Manager) owns GPU hardware
//! resource lifecycle (Device, Queue, Surface), and **Renderer** (Executor) orchestrates draw calls by binding
//! Material pipelines and Mesh vertex data into a RenderPass.
//!
//! ## Interaction with Other Modules
//! - `context` consumes errors from `utils`, holds Frame references during frame loops.
//! - `renderer` receives Device/Queue references from Context, uses Materials from `resources`.
//! - `frame` is consumed by both Context (begin_frame → end_frame) and Renderer (render → present).
pub mod context;
pub mod frame;
pub mod renderer;
// Re-exports
pub use context::Context;
pub use frame::Frame;
pub use renderer::Renderer;
+23 -12
View File
@@ -1,6 +1,6 @@
//! # Renderer Module
//! # Renderer Module — Executor Layer (WGPU Command Execution)
//!
//! The **Specialist** (Executor) layer of the architecture. Owns rendering logic — orchestrates draw calls by binding
//! The **Executor** layer of the architecture. Executes WGPU rendering commands — orchestrates draw calls by binding
//! Material pipelines and Mesh vertex buffers into a RenderPass, then submits commands to the GPU queue.
//! Does not own hardware resources (Device, Queue); receives references when called by the orchestrator (main.rs).
//! Does not own RenderPipelines or shaders — those are managed by PipelineCache and accessed through Material.
@@ -11,23 +11,34 @@
//! - **pipeline_cache**: indirectly via Material — Renderer uses pipelines that PipelineCache compiled.
//! - **mesh**: passes vertex/index buffers into set_vertex_buffer/set_index_buffer during draw.
//! - **material**: provides the RenderPipeline reference via set_pipeline during draw.
//!
//! ## Architecture Notes (per ARCHI_APP.md)
//! - **Phase d'Exécution**: Renderer executes per-frame render loops. During this phase it iterates Scene entities
//! and draws each one by binding the appropriate Material+Mesh pair.
//! - **Performance**: Entity sorting within the render loop minimizes pipeline switches (batching par matériau).
//! - **Accès Bas-Niveau**: Advanced users can bypass Scene and call Renderer directly for custom rendering paths.
use crate::context::Context;
use crate::core::Context;
use crate::resources::{Mesh, Material};
use crate::core::Frame;
/// The Executor layer of the architecture. Owns Device, Queue, and Format after initialization from Context.
/// Orchestrates all GPU draw calls without owning raw hardware resources externally.
/// 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
/// into RenderPasses during each frame. Does not own raw hardware resources (they are Arc-cloned from Context).
pub struct Renderer {
/// GPU command submission queue — owned by the Renderer after initialization from Context.
/// GPU command submission queue — holds an Arc clone from Context; shared with other Context users.
queue: wgpu::Queue,
/// GPU device — creates buffers, textures, pipelines; owned by the Renderer after initialization.
/// GPU device — creates buffers, textures, pipelines; holds an Arc clone from Context.
device: wgpu::Device,
/// Surface texture output format — stored here so it can be passed to PipelineCache on Material creation.
format: wgpu::TextureFormat,
}
impl Renderer {
/// Creates a Renderer by taking ownership of Device, Queue, and Format from the Context.
/// Called once at application startup during scene setup. The Renderer becomes the sole owner of these resources.
/// Creates a Renderer by cloning Device and Queue Arc references from the Context, plus capturing the surface format.
/// Called once at application startup during scene setup. The Renderer shares these resources via Arc;
/// Context retains ownership and can continue using them after this call.
pub fn new(context: &Context, format: wgpu::TextureFormat) -> Self {
Self {
queue: context.queue.clone(),
@@ -45,8 +56,8 @@ impl Renderer {
pub fn render(
&self,
view: &wgpu::TextureView,
mesh: &crate::mesh::Mesh,
material: &crate::material::Material,
mesh: &Mesh,
material: &Material,
) {
// Create per-frame command encoder; its lifetime is scoped to this function only.
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
@@ -90,7 +101,7 @@ impl Renderer {
/// Presents the rendered frame by submitting the acquired surface texture to the GPU queue.
/// The frame must have been obtained via Context::begin_frame() or Frame::try_new(); calling present()
/// twice on the same texture is undefined behavior. Called by the orchestrator after render().
pub fn present(&self, frame: crate::frame::Frame) {
pub fn present(&self, frame: Frame) {
self.queue.present(frame.surface_texture);
}
+23
View File
@@ -0,0 +1,23 @@
//! # WSG Library Crate Root
//!
//! The top-level entry point for the wsg-lib crate. Exposes five public modules organized by architectural responsibility:
//! **core** (Manager + Executor layers), **resources** (data types), **pipeline** (shader compilation cache),
//! **scene** (resource graph and entity management), and **utils** (configuration and error handling).
//!
//! ## Module Interaction Map
//! - `core` consumes resources from `resources`, pipelines from `pipeline`, and errors from `utils`.
//! - `scene` aggregates Resources, Materials, and Pipelines into an entity graph.
//! - `utils` is a leaf module — no internal dependencies on other library modules.
//!
//! ## Usage
//! Consumers import through the re-exports defined in each submodule's `mod.rs`:
//! ```ignore
//! use wsg_lib::core::{Context, Renderer};
//! use wsg_lib::resources::{Mesh, Material, Vertex};
//! use wsg_lib::utils::BASIC_SHADER;
//! ```
pub mod core;
pub mod pipeline;
pub mod resources;
pub mod scene;
pub mod utils;
+20
View File
@@ -0,0 +1,20 @@
# Pipeline Module — Shader Compilation Cache
## Overview
The `pipeline` module contains the shader compilation cache that avoids duplicate GPU work by reusing compiled RenderPipelines.
| File | Responsibility |
|------|---------------|
| **pipeline_cache** | PipelineCache — maps (shader_id, format) keys to compiled RenderPipelines. Loads WGSL from disk or falls back to embedded BASIC_SHADER constant. Creates pipelines on-demand via build_pipeline(). |
## Interaction with Other Modules
- **utils::conf**: Provides BASIC_SHADER_PATH (disk path) and BASIC_SHADER (embedded fallback).
- **resources::vertex**: Vertex struct field offsets define the CPU-side layout that build_pipeline() uses as the vertex buffer contract.
- **resources::material**: Material::new() calls get_or_create() during construction to obtain a shared RenderPipeline.
## Technical Points
- Pipelines are stored behind Arc so multiple Materials share the same compiled object without copying.
- wgpu 30 requires `compilation_options` in VertexState/FragmentState and `depth_slice` in color attachments.
+14
View File
@@ -0,0 +1,14 @@
//! # Pipeline Module — Shader Compilation Cache
//!
//! Defines the **PipelineCache**, which owns WGSL shader loading and RenderPipeline creation. It caches compiled
//! pipelines keyed by (shader_id, format) to avoid duplicate GPU work — multiple Materials sharing the same ID
//! share one Arc-wrapped pipeline without copying.
//!
//! ## Interaction with Other Modules
//! - `material` calls `get_or_create()` during its own construction to obtain a shared RenderPipeline.
//! - `conf::BASIC_SHADER` provides fallback WGSL source when an external file is not found.
//! - `vertex::Vertex` defines the CPU-side layout that `build_pipeline()` uses as the vertex buffer contract.
pub mod pipeline_cache;
// Re-exports
pub use pipeline_cache::PipelineCache;
@@ -1,7 +1,7 @@
//! # PipelineCache Module
//! # PipelineCache Module — Translation of resources/ Data Toward GPU Pipelines
//!
//! Defines `PipelineCache`, the library's shader compilation cache. It owns WGSL shader loading and RenderPipeline
//! creation, storing compiled pipelines in a HashMap keyed by shader_id + texture format to avoid duplicate GPU work.
//! Defines `PipelineCache`, the library's shader compilation cache. It translates WGSL shader source and resources/ data types
//! into compiled RenderPipelines, storing them in a HashMap keyed by shader_id + texture format to avoid duplicate GPU work.
//! Materials request pipelines through this cache; if a pipeline for the given key exists, it is returned directly
//! via Arc cloning. Otherwise the cache compiles one on-the-fly, caches it, then returns it.
//!
@@ -13,9 +13,11 @@
//! ## Technical Points
//! - Pipelines are stored behind `Arc` so multiple Materials share the same compiled object without copying.
//! - wgpu 30 requires `compilation_options` in VertexState/FragmentState and `depth_slice` in color attachments.
//! - **Batching**: Multiple Materials with the same shader_id share one pipeline, enabling material-level batching in Renderer.
use crate::utils::BASIC_SHADER;
use crate::resources::Vertex;
use crate::conf::BASIC_SHADER;
use crate::vertex::Vertex;
use std::collections::HashMap;
use std::sync::Arc;
+17
View File
@@ -0,0 +1,17 @@
# Resources Module — Data Types
## Overview
The `resources` module defines three immutable data types that flow through the rendering pipeline. These are created once during scene initialization and consumed by Renderer for draw calls every frame.
| File | Responsibility |
|------|---------------|
| **vertex** | Vertex struct — CPU-side per-attribute tuple (position [f32;3], normal [f32;3], uv [f32;2], color [f32;4]). Must match PipelineCache::build_pipeline() vertex buffer layout byte-for-byte. |
| **mesh** | Mesh struct — persistent GPU geometry container with vertex_buffer (wgpu::Buffer), optional index_buffer, and draw call counters. Created via Mesh::new() which uploads data from CPU to GPU buffers. |
| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. |
## Interaction with Other Modules
- **pipeline**: build_pipeline() reads Vertex field offsets to construct VertexBufferLayout attributes array.
- **scene**: Scene stores Arc<Mesh> and Arc<Material> instances keyed by identifier strings.
- **utils**: Mesh creation uses BASIC_SHADER fallback when external shader files are missing.
@@ -1,10 +1,15 @@
//! # Material Module
//! # Material Module — Appearance Descriptor (shader_id → RenderPipeline)
//!
//! Defines `Material`, a lightweight appearance descriptor that pairs a shader identifier with
//! a shared RenderPipeline. Materials are created via PipelineCache to ensure pipeline reuse—
//! multiple materials referencing the same shader_id point to the identical compiled GPU pipeline.
//!
//! ## 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.
//! - **Phase de Déclaration**: Materials are instantiated once in the declarative phase before the render loop begins.
use crate::pipeline_cache::PipelineCache;
use crate::pipeline::PipelineCache;
use std::sync::Arc;
/// Lightweight appearance descriptor: links a shader ID to a shared RenderPipeline.
+8 -2
View File
@@ -1,9 +1,15 @@
//! # Mesh Module
//! # Mesh Module — Persistent GPU Geometry Container
//!
//! Defines `Mesh`, a persistent GPU geometry container. Mesh data is uploaded to the GPU once at creation time
//! and remains valid across all frames until dropped. It holds no rendering knowledge—only raw geometric data.
//!
//! ## 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.
//! - **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.
use crate::vertex::Vertex;
use crate::resources::vertex::Vertex;
use wgpu::util::DeviceExt;
/// Persistent GPU geometry: vertex positions, optional indices, and draw call counters.
+20
View File
@@ -0,0 +1,20 @@
//! # Resources Module — Data Types
//!
//! Defines the three core data types that flow through the rendering pipeline: **Vertex** (CPU-side per-attribute
//! tuple), **Mesh** (GPU geometry container with vertex/index buffers), and **Material** (appearance descriptor
//! pairing shader ID with a compiled RenderPipeline). These are immutable after creation and consumed by Renderer
//! for draw calls.
//!
//! ## Interaction with Other Modules
//! - `pipeline_cache::build_pipeline()` reads Vertex field offsets to construct the vertex buffer layout.
//! - `mesh::new()` uploads Vertex arrays from CPU memory into GPU vertex buffers via DeviceExt::create_buffer_init().
//! - `material::new()` requests RenderPipelines from PipelineCache during scene initialization.
pub mod material;
pub mod mesh;
pub mod vertex;
// Re-exports
pub use material::Material;
pub use mesh::Mesh;
pub use vertex::Vertex;
@@ -1,4 +1,4 @@
//! # Vertex Module
//! # Vertex Module — CPU-Side Per-Attribute Tuple (GPU Contract)
//!
//! Defines `Vertex`, the CPU-side data layout that is sent to the GPU as vertex attribute buffers.
//! The struct's field order and offsets must exactly match the shader input attributes defined in
@@ -7,6 +7,8 @@
//! ## Technical Points
//! - `#[repr(C)]` ensures fields are laid out contiguously without Rust padding reordering, matching C ABI.
//! - `bytemuck::Pod + bytemuck::Zeroable` enables safe `cast_slice()` conversion for GPU buffer uploads.
//! - **Performance**: The 56-byte stride per vertex is the contract between CPU data and GPU shader inputs;
//! PipelineCache::build_pipeline() reads this layout to construct VertexBufferLayout attributes array.
/// Per-vertex attribute tuple: position (3D), normal (3D), texture coordinate (2D), color (RGBA).
/// Must match the vertex buffer layout in PipelineCache::build_pipeline() byte-for-byte.
+20
View File
@@ -0,0 +1,20 @@
# Scene Module — Resource Depot and Entity Graph
## Overview
The `scene` module defines Scene, the declarative layer of the WSG architecture. Users register resources (Meshes, Materials) by identifier before the render loop starts, then associate entities via labels. At runtime, Scene provides immutable access to these resources without exposing raw wgpu handles.
| File | Responsibility |
|------|---------------|
| **scene** | Scene struct — resource depot storing Meshes and Materials keyed by string identifiers, plus entity graph mapping labels to (mesh_id, material_id) pairs for rendering iteration. |
## Interaction with Other Modules
- **core::renderer**: Renderer queries Scene for Material/Mesh pairs during frame rendering; Context does not interact directly.
- **pipeline**: PipelineCache creates Materials keyed by shader_id; Scene stores references to those Materials.
- **resources**: Scene owns Arc<Mesh> and Arc<Material> instances; Vertex is only used at Mesh creation time.
- **utils**: Scene uses WsgError if resource registration fails.
## Architecture Note
Per ARCHI_APP_FACADE.md, Scene is one half of the "App" facade pattern. It enables a declarative workflow where all resources are declared before the render loop begins, while keeping the freedom to build the engine "brick by brick" through direct Context/PipelineCache/Renderer manipulation.
+22
View File
@@ -0,0 +1,22 @@
//! # Scene Module — Resource Depot and Entity Graph
//!
//! Defines `Scene`, the repository for resources (Mesh, Material) and the graph of entity associations between them.
//! Scene is the declarative layer of the architecture: users register Meshes and Materials by identifier before
//! the render loop starts, then associate entities via labels during initialization. At runtime, Scene provides
//! immutable access to these resources without exposing raw wgpu handles.
//!
//! ## Interaction with Other Modules
//! - **core**: Renderer queries Scene for Material/Mesh pairs to draw; Context does not interact directly.
//! - **pipeline**: PipelineCache creates Materials keyed by shader_id; Scene stores references to those Materials.
//! - **resources**: Scene owns Mesh and Material instances; Vertex is only used at Mesh creation time.
//! - **utils**: Scene uses WsgError if resource registration fails.
//!
//! ## Architecture Note
//! Per ARCHI_APP_FACADE.md, Scene is one half of the "App" facade pattern. It enables a declarative workflow where
//! all resources are declared before the render loop begins, while keeping the freedom to build the engine
//! "brick by brick" through direct Context/PipelineCache/Renderer manipulation.
pub mod scene;
// Re-export
pub use scene::Scene;
+118
View File
@@ -0,0 +1,118 @@
//! # Scene Module — Resource Depot and Entity Graph (per ARCHI_APP.md)
//!
//! Defines `Scene`, the declarative layer of the WSG architecture. Users register resources (Meshes, Materials) by identifier before
//! the render loop starts, then associate entities via labels. At runtime, Scene provides immutable access to these resources without exposing raw wgpu handles.
//!
//! ## 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
//! 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.
use crate::resources::{Material, Mesh};
use std::collections::HashMap;
use std::sync::Arc;
/// Resource depot and entity graph. Stores Meshes and Materials keyed by identifier strings,
/// 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 Mesh instances. Populated via `add_mesh()`.
meshes: HashMap<String, Arc<Mesh>>,
/// Map of material identifiers to owned 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)>,
}
impl Scene {
/// Creates an empty scene with no registered resources or entities.
/// Called at application startup before any resource registration.
pub fn new() -> Self {
Self {
meshes: HashMap::new(),
materials: HashMap::new(),
entities: HashMap::new(),
}
}
/// Registers a Mesh in the scene under a unique identifier.
/// Inputs: id (unique key), mesh (Mesh instance). Returns Ok(id) on success or Err if already exists.
/// Called during scene initialization when building the resource depot.
pub fn add_mesh(&mut self, id: &str, mesh: Arc<Mesh>) -> Result<String, String> {
if self.meshes.contains_key(id) {
return Err(format!("Mesh ID '{}' already exists.", id));
}
self.meshes.insert(id.to_string(), mesh);
Ok(id.to_string())
}
/// Registers a Material in the scene under a unique identifier.
/// Inputs: id (unique key), material (Material instance). Returns Ok(id) on success or Err if already exists.
/// Called during scene initialization when building the resource depot.
pub fn add_material(&mut self, id: &str, material: Arc<Material>) -> Result<String, String> {
if self.materials.contains_key(id) {
return Err(format!("Material ID '{}' already exists.", id));
}
self.materials.insert(id.to_string(), material);
Ok(id.to_string())
}
/// Associates an entity label with a mesh and material pair for rendering iteration.
/// Inputs: label (entity identifier), mesh_id (key into meshes map), material_id (key into materials map).
/// Returns Ok(label) on success or Err if either referenced resource does not exist.
/// Called during scene initialization to build the renderable entity graph.
pub fn add_entity(
&mut self,
label: &str,
mesh_id: &str,
material_id: &str,
) -> Result<String, String> {
if !self.meshes.contains_key(mesh_id) {
return Err(format!("Mesh '{}' does not exist.", mesh_id));
}
if !self.materials.contains_key(material_id) {
return Err(format!("Material '{}' does not exist.", material_id));
}
self.entities.insert(label.to_string(), (mesh_id.to_string(), material_id.to_string()));
Ok(label.to_string())
}
/// Retrieves a Mesh by its registered identifier.
/// Called by Renderer during frame rendering to obtain vertex data for draw calls.
pub fn get_mesh(&self, id: &str) -> Option<&Arc<Mesh>> {
self.meshes.get(id)
}
/// Retrieves a Material by its registered identifier.
/// Called by Renderer during frame rendering to obtain pipeline reference for draw calls.
pub fn get_material(&self, id: &str) -> Option<&Arc<Material>> {
self.materials.get(id)
}
/// Iterates all entity associations, yielding (label, mesh_ref, material_ref) triples.
/// Called by the orchestrator during each render pass to draw every entity in order.
pub fn iter_entities(
&self,
) -> impl Iterator<Item = (&str, &Arc<Mesh>, &Arc<Material>)> + '_ {
self.entities.iter().map(|(label, (mesh_id, mat_id))| {
let mesh = self.meshes.get(mesh_id).unwrap(); // safe: add_entity validates existence
let mat = self.materials.get(mat_id).unwrap(); // same invariant
(label.as_str(), mesh, mat)
})
}
/// Removes an entity from the graph without freeing its underlying resources.
/// The referenced Mesh and Material remain registered; only the association is dropped.
/// Called during dynamic updates when an entity should be hidden or removed temporarily.
pub fn remove_entity(&mut self, label: &str) -> bool {
self.entities.remove(label).is_some()
}
/// Returns the number of registered entities in this scene.
/// Called for diagnostic logging or culling decisions (e.g., skip rendering empty scenes).
pub fn entity_count(&self) -> usize {
self.entities.len()
}
}
+21
View File
@@ -0,0 +1,21 @@
# Shaders Directory
## Overview
Contains WGSL shader source files used by the PipelineCache module. These are loaded at runtime from disk when referenced by their registered ID in PipelineCache.register_shader(). If a file is missing, PipelineCache falls back to the embedded BASIC_SHADER constant defined in utils::conf.
| File | Purpose |
|------|---------|
| **basic_shader.wgsl** | Default vertex/fragment shader pair (vs_main / fs_main entry points) with position, normal, UV, and color attributes matching the Vertex struct layout. |
## Shader Contract
The WGSL shader must define:
- `@vertex fn vs_main() -> @builtin(position) vec4<f32>` — vertex entry point
- `@fragment fn fs_main() -> @location(0) vec4<f32>` — fragment entry point writing RGBA output
- Vertex input attributes matching the 56-byte stride of resources::Vertex:
- `@location(0)` → position `[f32; 3]` (offset 0)
- `@location(1)` → normal `[f32; 3]` (offset 12)
- `@location(2)` → uv `[f32; 2]` (offset 24)
- `@location(3)` → color `[f32; 4]` (offset 32)
+24
View File
@@ -0,0 +1,24 @@
//! # Basic Shader Module
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) uv: vec2<f32>,
@location(2) color: vec3<f32>,
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) color: vec3<f32>,
};
@vertex
fn vs_main(model: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.clip_position = vec4<f32>(model.position, 1.0);
out.color = model.color; // On transmet la couleur au fragment shader
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(in.color, 1.0);
}
+16
View File
@@ -0,0 +1,16 @@
# Utils Module — Configuration and Error Handling
## Overview
The `utils` module defines two leaf concepts that other modules consume but have no internal dependencies on. As a leaf module, it does not import from any other library submodule.
| File | Responsibility |
|------|---------------|
| **conf** | Shared constants for shader paths (BASIC_SHADER_PATH) and embedded WGSL source code (BASIC_SHADER). Centralized here so all submodules import from one place instead of duplicating literal strings. Enables PipelineCache to fall back to an embedded default shader when the file-based one is missing. |
| **error** | WsgError enum — application-level error type mapping specific wgpu failure modes to user-friendly messages via thiserror. Every variant maps a GPU initialization or rendering failure to a recoverable or fatal outcome. |
## Interaction with Other Modules
- **pipeline::pipeline_cache**: load_shader() reads BASIC_SHADER_PATH from disk; falls back to BASIC_SHADER if unreadable.
- **core::context**: Returns WsgError variants from all fallible methods (new, configure, begin_frame).
- **core::renderer**: Does not use errors directly — panics on invalid state rather than returning Result.
+1 -1
View File
@@ -13,4 +13,4 @@ pub const BASIC_SHADER_PATH: &str = "assets/shaders/basic_shader.wgsl";
/// The basic WGSL shader source code, embedded at compile time via `include_str!`.
/// Serves as a fallback when `BASIC_SHADER_PATH` cannot be read at runtime.
pub const BASIC_SHADER: &str = include_str!("../assets/shaders/basic_shader.wgsl");
pub const BASIC_SHADER: &str = include_str!("../shaders/basic_shader.wgsl");
+18
View File
@@ -0,0 +1,18 @@
//! # Utils Module — Configuration and Error Handling
//!
//! Defines two leaf concepts: **conf** (shared constants for shader paths and embedded WGSL source) and
//! **error** (WsgError, the application-level error type mapping wgpu failure modes to user-friendly messages).
//! Both are consumed by other modules but have no internal dependencies on them.
//!
//! ## Interaction with Other Modules
//! - `pipeline_cache` loads shaders from disk using conf::BASIC_SHADER_PATH; falls back to BASIC_SHADER.
//! - `context` returns WsgError variants from all fallible methods (new, configure, begin_frame).
//! - `renderer` does not use errors directly (panics on invalid state rather than returning Result).
pub mod conf;
pub mod error;
// Re-exports
pub use conf::BASIC_SHADER;
pub use conf::BASIC_SHADER_PATH;
pub use error::WsgError;