docs: reconcile README/ROADMAP/PLAN state and settle slotmap vs String IDs

- README stays the source of truth for current state; drive/item-5 now
  reflects the settled decision: String IDs for the MVP, slotmap deferred.
- ROADMAP gains a 'point de depart' (present-state) block, marks glam done,
  removes the slotmap Phase-1 mandates, and documents the String-ID decision
  plus the deferred 'typed handles' step.
- PLAN corrects stale [X] checkmarks (App render() cannot draw, Scene
  rendering not automated, simple example does not compile) and notes its
  verification checklist against current reality.
- Remove the unused slotmap direct dependency from wsg-lib (package remains
  in Cargo.lock only as a transitive dep of glow).
This commit is contained in:
Jérôme Bousquié
2026-09-14 15:28:41 +02:00
parent cfb4c6f212
commit f6c0851d4f
5 changed files with 160 additions and 71 deletions
Generated
-1
View File
@@ -2331,7 +2331,6 @@ dependencies = [
"bytemuck", "bytemuck",
"glam", "glam",
"pollster", "pollster",
"slotmap",
"thiserror 2.0.18", "thiserror 2.0.18",
"wgpu", "wgpu",
"winit", "winit",
+132 -53
View File
@@ -1,88 +1,167 @@
# WSG - WGPU Simple Graphics Library # WSG - WGPU Simple Graphics Library
WSG is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) to provide a simple, declarative API for 3D graphics. It abstracts away the complexity of managing GPU resources while exposing low-level primitives for advanced users. WSG is a Rust library that wraps [wgpu](https://github.com/gfx-rs/wgpu) and [winit](https://crates.io/crates/winit) for simple GPU drawing. It groups the five core wgpu objects (Instance, Surface, Adapter, Device, Queue) behind a single `Context`, adds small building blocks (`Mesh`, `Material`, `PipelineCache`, `Frame`), and exposes the low-level primitives for advanced users.
> **Status: unstable development version.** The manual workflow below is fully working. The high-level "declarative" workflow and the GPU-driven two-pass pipeline described in the architecture docs are **not implemented yet** — see [Status](#status) and [Roadmap](#roadmap).
NOTE : the development version is currently unstable and the examples described below may not work as expected. ## Status
| Area | State |
|------|-------|
| Manual workflow (`Context` + `Renderer` + `PipelineCache`) | ✅ Working |
| `App` / `AppBuilder` / `AppHandler` event-loop facade | 🚧 Scaffold — window, events and frame presentation work, but the `render()` callback cannot draw yet (the per-frame view is not exposed to it) |
| `Scene` resource/entity registry | 🚧 Registration API works; the engine does not render the scene yet |
| GPU-driven two-pass pipeline (Compute → indirect draw) | 📋 Roadmap — spec in [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) |
| 3D transforms (MVP uniforms, camera in the pipeline) | 📋 Roadmap — the bundled shader draws positions straight to NDC today |
Note: the bundled `basic_shader.wgsl` treats vertex positions as already in NDC space, so what you can see today is flat, untransformed drawing (e.g. a colored quad) — not a 3D scene.
## What it does ## What it does
WSG provides two complementary workflows: ### Manual workflow (working — recommended today)
### Declarative workflow (recommended) Bypass the `App` facade and drive `Context`, `Renderer` and `PipelineCache` yourself. This is the only workflow that renders pixels today (same code as the `manual` example):
Register your scene's resources and entities once at startup, then let WSG handle rendering each frame via a GPU-Driven pipeline (Compute Pass → Render Pass):
```rust ```rust
use wsg_lib::{App, AppHandler}; use std::sync::Arc;
use wsg_lib::resources::{Mesh, Material, Vertex}; use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;
use wsg_lib::core::{Context, Frame, Renderer};
use wsg_lib::pipeline::PipelineCache;
use wsg_lib::resources::{Material, Mesh, Vertex};
use wsg_lib::utils;
struct MyGame { /* ... */ } fn main() {
// Window + async GPU init
let event_loop = EventLoop::new().unwrap();
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
let context = pollster::block_on(Context::new(window.clone())).expect("GPU init failed");
let format = context.configure(&context.adapter, 800, 600).expect("surface config failed");
// Renderer + shader cache (falls back to the embedded shader if the file is missing)
let renderer = Renderer::new(&context, format);
let mut cache = PipelineCache::new(Arc::new(context.device.clone()));
cache.register_shader("basic", utils::BASIC_SHADER_PATH).unwrap();
// Material + mesh
let material = Material::new(renderer.format(), "basic", &mut cache);
let vertices: [Vertex; 4] = [
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] },
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] },
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] },
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] },
];
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
let mesh = Mesh::new(renderer.device(), &vertices, Some(&indices));
// Render loop
event_loop.run(|event, elwt| {
match event {
winit::event::Event::AboutToWait => window.request_redraw(),
winit::event::Event::WindowEvent { event: winit::event::WindowEvent::RedrawRequested, .. } => {
if let Some(frame) = Frame::try_new(&context.surface) {
renderer.render(frame.view(), &mesh, &material);
renderer.present(frame);
}
}
winit::event::Event::WindowEvent { event: winit::event::WindowEvent::CloseRequested, .. } => elwt.exit(),
_ => {}
}
}).unwrap();
}
```
### Declarative workflow (work in progress)
The intended API: register your scene's resources and entities once, then let `App` handle the window lifecycle, event processing and frame presentation. Users implement the `AppHandler` trait to inject per-frame logic:
```rust
use wsg_lib::app::AppBuilder;
use wsg_lib::{App, AppHandler};
struct MyGame;
impl AppHandler for MyGame { impl AppHandler for MyGame {
fn update(&mut self, _app: &mut App) { // update() has an empty default — implement it to mutate scene state each frame.
// Modify scene state (transformations, entities) — only place allowed for mutations fn render(&mut self, _app: &mut App) {
} // The engine acquires and presents the frame around this callback,
// but scene rendering is not automated yet — see Roadmap.
fn render(&mut self, app: &mut App) {
// WSG automatically runs Compute Pass → Render Pass on the current scene.
// No manual iteration needed — draw_indexed_indirect handles everything.
} }
} }
#[pollster::main] #[pollster::main]
async fn main() -> Result<(), wsg_lib::utils::WsgError> { async fn main() -> Result<(), wsg_lib::utils::WsgError> {
let mut app = AppBuilder::new().build().await?; let app = AppBuilder::new().build().await?;
// Declare resources (once, before the render loop) // Scene registration is available (string IDs):
app.cache.register_shader("basic", "assets/shaders/basic_shader.wgsl")?; // app.scene.add_mesh("quad", Arc::new(mesh))?;
let vertices: [Vertex; 4] = [/* ... */]; // app.scene.add_material("mat", Arc::new(material))?;
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3]; // app.scene.add_entity("my_quad", "quad", "mat")?;
let mesh = Mesh::new(app.context.device(), &vertices, Some(&indices)); // ...but the engine will not draw them until the declarative pipeline lands.
let material = Material::new(app.renderer.format(), "basic", &mut app.cache);
app.scene.add_mesh("quad", Arc::new(mesh))?; app.run(MyGame)
app.scene.add_material("mat", Arc::new(material))?;
app.scene.add_entity("my_quad", "quad", "mat")?;
// Run the render loop — WSG handles Compute Pass + Render Pass each frame
app.run(MyGame {})
} }
``` ```
### Manual workflow > API note: `Scene::add_mesh` / `add_material` / `add_entity` and `PipelineCache::register_shader`
> currently return `Result<_, String>` — typed error unification is on the roadmap.
For fine-grained control, bypass the Scene facade entirely and manipulate Context, Renderer, and PipelineCache directly through their public APIs.
## Architecture overview ## Architecture overview
WSG follows a GPU-Driven two-layer architecture: - **Manager layer (`Context`)** — owns the GPU hardware lifecycle (Instance → Surface → Adapter → Device → Queue). Created once at startup; `configure()` sets up the swapchain, `Frame` wraps each frame's surface texture + view.
- **Executor layer (`Renderer`)** — binds a `Material` pipeline + `Mesh` buffers into a RenderPass and submits the commands. Today this is one encoder + one submit **per object**.
- **Supporting pieces** — `PipelineCache` (shader → compiled RenderPipeline, `Arc`-shared), `Material`, `Mesh`/`Vertex`, `Scene` (string-ID registry), `Camera`/`Transform` (types only, not yet used by the pipeline).
- **Manager layer (Context)** — owns GPU hardware lifecycle (Instance → Surface → Adapter → Device → Queue). Created once at startup. The planned target architecture — a GPU-driven two-pass pipeline (Compute Pass: world matrices + frustum culling → Indirect Draw Buffer, then a single `draw_indexed_indirect` per frame) — is specified in [docs/tech/ARCHI_APP.md](docs/tech/ARCHI_APP.md) and [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) but is **not implemented yet**.
- **Executor layer (Renderer)** — orchestrates a two-pass pipeline per frame: Compute Pass (World Matrix calculation + Frustum Culling → Indirect Draw Buffer) then Render Pass (`draw_indexed_indirect`). Dynamic, changes each frame.
The high-level `App` facade ties everything together, automating window lifecycle, event processing, and frame presentation. Users implement the `AppHandler` trait to inject game logic.
## Quick reference ## Quick reference
| Concept | Type | Responsibility | | Concept | Type | Responsibility | Status |
|---------|------|---------------| |---------|------|---------------|--------|
| App | Facade | Window lifecycle + event loop + render automation | | App / AppBuilder | Facade | Window lifecycle + winit event loop + frame presentation | 🚧 Scaffold (no scene rendering) |
| AppHandler | Trait | User-defined update/render callbacks | | AppHandler | Trait | User-defined `update()` / `render()` callbacks | ✅ (render() has no frame access yet) |
| Scene | Struct | Resource depot + entity graph (declarative) | | Scene | Struct | String-ID registry: meshes, materials, entities | 🚧 Registration only |
| Context | Struct | GPU hardware lifecycle (Manager) | | Context | Struct | GPU hardware lifecycle (Instance, Surface, Adapter, Device, Queue) | ✅ |
| Renderer | Struct | Two-pass pipeline: Compute + Render | | Renderer | Struct | Binds Material + Mesh into a RenderPass, submits | ✅ (one submit per object) |
| Material | Struct | Shader ID → compiled RenderPipeline | | PipelineCache | Struct | Shader → compiled RenderPipeline cache | ✅ |
| Mesh | Struct | Persistent GPU geometry container | | Material | Struct | Shader ID → RenderPipeline | ✅ |
| Vertex | Struct | CPU-side vertex attribute tuple | | Mesh / Vertex | Struct | GPU geometry container / CPU-side vertex tuple | ✅ |
| PipelineCache | Struct | Shader compilation cache | | Frame | Struct | Per-frame RAII wrapper (surface texture + view) | ✅ |
| Frame | Struct | Per-frame RAII wrapper for surface texture + view | | Camera / Transform | Struct | Camera & transform math | 📋 Types only, not in the pipeline |
## Getting started ## Getting started
```bash WSG is **not published on crates.io** — depend on it by path:
cargo add wsg-lib # Add the dependency
# Then build your app following the declarative example above ```toml
[dependencies]
wsg-lib = { path = "/path/to/wsg/lib" }
pollster = "0.4" # only if you use the async AppBuilder
``` ```
For details on the architecture and internal modules, see [ARCHI_APP](docs/ARCHI_APP.md). | Action | Command |
|--------|---------|
| Build everything | `cargo build --workspace` |
| Run the working example | `cargo run -p wsg-lib --example manual` |
| Check everything (incl. examples) | `cargo check --all-targets` |
The `manual` example is the reference for the working workflow. The `simple` example (App facade) is work in progress and currently does not compile.
## Documentation
The architecture docs live in `docs/tech/` and are written in **French**:
- [ARCHI_APP](docs/tech/ARCHI_APP.md) — engine architecture. ⚠️ Describes the *target* architecture; the GPU-driven pipeline parts are not implemented yet.
- [ARCHI_CPU_GPU](docs/tech/ARCHI_CPU_GPU.md) — CPU/GPU workload split specification.
- [ARCHI_RENDU](docs/tech/ARCHI_RENDU.md) — update/render mutability model.
- [ARCHI_ARENES](docs/tech/ARCHI_ARENES.md) — planned slotmap-based generational resource handles.
- [FRAME_LOOP](docs/tech/FRAME_LOOP.md) — frame lifetime and resource persistence.
## Roadmap
1. **Scene auto-rendering** — `App`/`Renderer` iterate registered entities and draw them in one encoder/submit per frame; expose the frame view to `AppHandler::render` for custom draws.
2. **GPU-driven two-pass pipeline** — Compute Pass (world matrices + frustum culling) filling an indirect draw buffer, single `draw_indexed_indirect` (see ARCHI_CPU_GPU).
3. **CPU→GPU transform sync** — persistent transform buffers with ring (triple) buffering.
4. **Real 3D pipeline** — MVP uniforms + camera support in the vertex shader.
5. **Typed resource handles** — keep String IDs for the MVP (current design, source of truth in `Scene`); slotmap-based generational handles (`ARCHI_ARENES.md`) are deferred to a later performance pass.
6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors.
+13 -7
View File
@@ -11,6 +11,12 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé. Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé.
> **Statut réel (à jour au 2026-09-14).** Ce plan couvre la phase de *consolidation* passée ; la source
> de vérité sur l'état actuel est **README.md** et le code. Plusieurs cases `[X]` ci-dessous ont été
> re-corrigées car elles ne reflétaient plus la réalité : notamment l'exemple `simple` **ne compile pas**
> actuellement (`App::new()` obsolète, à remplacer par `AppBuilder`) et le rendu de la `Scene` n'est
> **pas automatisé** (items Phase 2 et Check-list concernés).
## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue) ## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue)
Cette phase vise à supprimer la dette technique et à unifier les accès. Cette phase vise à supprimer la dette technique et à unifier les accès.
@@ -39,19 +45,19 @@ Une fois la plomberie encapsulée, nous devons rendre l'assemblage des objets co
### Intégration de la Scene ### Intégration de la Scene
- [X] Formaliser la structure `Scene` : un conteneur qui liste les Entities. - [X] Formaliser la structure `Scene` : un conteneur qui liste les Entities.
- [X] Associer le `PipelineCache` à la Scene pour que le rendu des matériaux soit automatique. - [ ] Associer le `PipelineCache` à la Scene pour que le rendu des matériaux soit automatique (actuellement le cache est porté par `App`, indépendant de la Scene ; le rendu n'est pas automatisé).
- [X] Implémenter la logique `app.render(scene)` : cette méthode doit parcourir la scène, récupérer les matériaux, gérer les pipelines via le cache, et soumettre les draw calls. - [ ] Implémenter la logique `app.render(scene)` : cette méthode doit parcourir la scène, récupérer les matériaux, gérer les pipelines via le cache, et soumettre les draw calls (non implémenté — cf. README, étape 1 du Roadmap : scene auto-rendering).
### Gestion des Matériaux et Shaders ### Gestion des Matériaux et Shaders
- [X] S'assurer que chaque Mesh possède une référence vers un Material. - [ ] S'assurer que chaque Mesh possède une référence vers un Material (à l'heure actuelle le lien est porté par l'entité `(mesh_id, material_id)` de la Scene, pas par le Mesh lui-même).
- [X] Implémenter le comportement par défaut : si aucun matériau n'est assigné, le moteur injecte automatiquement le `basic_shader`. - [ ] Implémenter le comportement par défaut : si aucun matériau n'est assigné, le moteur injecte automatiquement le `basic_shader` (non implémenté).
## Phase 3 : Documentation et Interface (API "User-Friendly") ## Phase 3 : Documentation et Interface (API "User-Friendly")
### Refonte des Exemples ### Refonte des Exemples
- [X] `simple.rs` doit devenir le modèle : **15 lignes** de code, pas de manipulation WGPU explicite. - [ ] `simple.rs` doit devenir le modèle : **15 lignes** de code, pas de manipulation WGPU explicite (actuellement ne compile pas — API obsolète `App::new()` → `AppBuilder`).
- [X] `manual.rs` doit rester disponible en tant que tutoriel pour ceux qui veulent contourner l'abstraction App. - [X] `manual.rs` doit rester disponible en tant que tutoriel pour ceux qui veulent contourner l'abstraction App.
### Nettoyage du Code Interne ### Nettoyage du Code Interne
@@ -68,8 +74,8 @@ Une fois les phases 1 à 3 validées, nous pourrons introduire :
## Check-list de Vérification pour le LLM d'Assistance ## Check-list de Vérification pour le LLM d'Assistance
- [X] Est-ce que `simple.rs` compile sans importer `winit` ou `wgpu` ? - [ ] Est-ce que `simple.rs` compile sans importer `winit` ou `wgpu` ? (ne compile pas actuellement ; `App::new()` → `AppBuilder`)
- [X] Est-ce que `App::run` gère bien le cycle update → render → present ? - [ ] Est-ce que `App::run` gère bien le cycle update → render → present ? (boucle + présentation OK, mais `render()` ne peut pas encore dessiner — vue de frame non exposée)
- [X] Les modules sont-ils bien exposés via `lib.rs` ? - [X] Les modules sont-ils bien exposés via `lib.rs` ?
- [X] `pollster` est-il uniquement en dev-dependencies ? - [X] `pollster` est-il uniquement en dev-dependencies ?
+15 -9
View File
@@ -11,6 +11,14 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
> Basé sur l'architecture existante (ARCHI_APP, ARCHI_ARENES, ARCHI_CPU_GPU, ARCHI_RENDU). > Basé sur l'architecture existante (ARCHI_APP, ARCHI_ARENES, ARCHI_CPU_GPU, ARCHI_RENDU).
> Objectif : prototype fonctionnel d'abord, enrichissement progressif ensuite. > Objectif : prototype fonctionnel d'abord, enrichissement progressif ensuite.
>
> **Point de départ (état réel au 2026-09-14 — la source de vérité est README.md).**
> Les fondations suivantes existent et fonctionnent déjà ; cette roadmap décrit la **trajectoire à
> venir** à partir de cet état (elle reprend les étapes 1-4 du README avant la montée GPU-driven) :
> - Workflow manuel (`Context` + `Renderer` + `PipelineCache`) : ✅ fonctionnel (exemple `manual`).
> - Façade `App` / `AppBuilder` / `AppHandler` : 🚧 scaffold — boucle et présentation OK, mais `render()` ne peut pas encore dessiner (vue de frame non exposée) et le rendu de la scène n'est pas automatisé.
> - `Scene` avec identifiants **String** (décision prise — voir tableau Notes de Décision) : 🚧 enregistrement seul.
> - `Camera` / `Transform` et `glam` : types et mathématiques présents (`math/`, `resources/camera.rs`), non branchés au pipeline.
--- ---
@@ -19,8 +27,8 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
**Objectif** : Afficher un cube (ou autre mesh) 3D avec un éclairage Phong basique. **Objectif** : Afficher un cube (ou autre mesh) 3D avec un éclairage Phong basique.
### 1.1 Dépendances & Mathématiques ### 1.1 Dépendances & Mathématiques
- [ ] Ajouter `glam = "0.33"` en dépendance (`lib/Cargo.toml`) - [x] `glam = "0.33"` ajouté (`lib/Cargo.toml`) — déjà présent, utilisé par `math/transform.rs` et `resources/camera.rs`
- [ ] Ajouter `slotmap = "1.0"` en dépendance - [x] `slotmap` **retiré** — décision prise : **String IDs pour le MVP** ; slotmap reporté à l'étape "handles typés" (voir Notes de Décision)
- [ ] Créer module `math/` (ou `transform.rs`) : - [ ] Créer module `math/` (ou `transform.rs`) :
- [ ] Struct `Transform { translation: Vec3, rotation: Quat, scale: Vec3 }` - [ ] Struct `Transform { translation: Vec3, rotation: Quat, scale: Vec3 }`
- [ ] Méthode `to_matrix() -> Mat4` pour calculer la matrice locale - [ ] Méthode `to_matrix() -> Mat4` pour calculer la matrice locale
@@ -46,12 +54,10 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
- [ ] Uniforms : `view_matrix`, `proj_matrix`, `world_matrix`, `light_dir`, `light_color` - [ ] Uniforms : `view_matrix`, `proj_matrix`, `world_matrix`, `light_dir`, `light_color`
- [ ] Mettre à jour `Material` pour supporter les uniforms du shader Phong - [ ] Mettre à jour `Material` pour supporter les uniforms du shader Phong
### 1.4 Scene avec Arènes (slotmap) ### 1.4 Scene avec identifiants (MVP : String IDs)
- [ ] Implémenter `Scene` avec arènes générationalles : - [x] `Scene` implémentée avec **String IDs** (`HashMap<String, Arc<Mesh>>`, `...Material`, entités) — état actuel validé ; décision : rester en String IDs pour le MVP
- [ ] `SlotMap<MeshId, Mesh>` - [x] Méthodes : `add_mesh()`, `get_mesh()`, `add_material()`, `add_entity()`, `iter_entities()`, `remove_entity()`
- [ ] `SlotMap<MaterialId, Material>` (préparation future) - [ ] **Reporté (étape "Handles typés")** : migrer vers `slotmap` générationnel (`MeshId`/`MaterialId`) quand l'éviction/les performances le justifieront
- [ ] Méthodes : `add_mesh()`, `get_mesh()`, `iter_meshes()`
- [ ] Les entités stockent des `MeshId` (handles typés), pas des références
### 1.5 Rendu du Prototype ### 1.5 Rendu du Prototype
- [ ] Uniform buffer pour la frame : `view_matrix`, `proj_matrix`, `light_dir` - [ ] Uniform buffer pour la frame : `view_matrix`, `proj_matrix`, `light_dir`
@@ -143,4 +149,4 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
| **UVs en Phase 4** | Inutiles avant les textures ; on garde `Geometry` simple au départ | | **UVs en Phase 4** | Inutiles avant les textures ; on garde `Geometry` simple au départ |
| **BBox en Phase 3** | Utile uniquement pour le frustum culling GPU | | **BBox en Phase 3** | Utile uniquement pour le frustum culling GPU |
| **World Matrix CPU → MVP, GPU → Phase 3** | Le MVP est plus simple avec un uniform par mesh ; la migration GPU-driven est progressive | | **World Matrix CPU → MVP, GPU → Phase 3** | Le MVP est plus simple avec un uniform par mesh ; la migration GPU-driven est progressive |
| **slotmap dès Phase 1** | Architecture décidée (`ARCHI_ARENES.md`) ; mieux de l'adopter tôt que de refactorer | | **String IDs pour le MVP, slotmap reporté** | Le code et le README utilisent des String IDs (simples, sûrs, figés avant la boucle de rendu) ; `ARCHI_ARENES.md` reste la cible "handles typés" pour plus tard. La dépendance `slotmap` a été retirée tant qu'elle est inutilisée |
-1
View File
@@ -12,7 +12,6 @@ winit = "0.29" # For window management — pinned to match examples
thiserror = "2" thiserror = "2"
bytemuck = { version = "1.25.0", features = ["derive"] } bytemuck = { version = "1.25.0", features = ["derive"] }
glam = "0.33" glam = "0.33"
slotmap = "1.0"
[dev-dependencies] [dev-dependencies]
pollster = { version="0.4.0", features = ["macro"] } pollster = { version="0.4.0", features = ["macro"] }