# WSG - WGPU Simple Graphics Library ## Project Type Rust workspace (2024 edition) wrapping [wgpu](https://github.com/gfx-rs/wgpu) for simple 3D drawing operations. ## Workspace Structure ``` Cargo.toml # workspace root (members = ["lib"]) — no dependencies here lib/Cargo.toml # wsg-lib crate: wgpu 30.0.0, winit 0.30 + explicit [[example]] entries lib/src/ # library source (app, core/, mesh/, pipeline/, resources/, scene/, utils/) lib/examples/ # examples, one subfolder per category (each folder has a README.md): │ ├── meshes/ # simple, cube, pbr, import, manual │ ├── lights/ # shadow, shadow_test, spot_test, emissive │ ├── cameras/ # culling │ └── effects/ # demo, bloom, hdr, msaa, fog, dof ``` **Key convention**: examples live in `lib/examples//` subfolders. Cargo only auto-discovers top-level `examples/*.rs`, so **every example is declared explicitly in `lib/Cargo.toml`** (`[[example]] name = … path = "examples//….rs"`). Names are stable: `cargo run -p wsg-lib --example ` works as before. Do not publish this to crates.io as-is — it uses local path conventions. ## Essential Commands | Action | Command | |--------|---------| | Build everything | `cargo build --workspace` | | Run an example | `cargo run -p wsg-lib --example ` | | Run a feature-gated example | `cargo run -p wsg-lib --example import --features import-obj` | | Test | `cargo test --workspace` | | Check | `cargo check --workspace` | | Format | `cargo fmt --all` | No custom scripts or linting tooling beyond standard Cargo conventions. ## Architecture Overview The library's purpose is to abstract the five core wgpu objects into a single **Context**: - **Instance** — GPU backend selection (Vulkan/Metal/DX12) - **Surface** — window rendering surface (via winit) - **Adapter** — physical/logical GPU device - **Device** — buffer/texture/pipeline creation - **Queue** — command submission WGPU doesn't have a native "Context" object — this type groups them together for a simpler user API. See README.md for the French documentation of each component. ## Gotchas - Rust 2024 edition is used. Ensure your Rust toolchain supports it (`rustup update`). - wgpu 30.0.0 is pinned in `lib/Cargo.toml`. The comment says "check the latest version" — verify compatibility before upgrading. - Cargo features gate primitives (`prim-*`, `all-prims` is default) and importers (`import-obj`, `import-gltf`); the `import` example is `required-features = ["import-obj"]`. 138 tests exist (`cargo test --workspace`, incl. particle layout + billboard WGSL validation). - The workspace has no `[workspace.dependencies]` section. Dependencies are declared per-crate rather than centrally. - **WGSL `select` argument order** (cost us a day): `select(reject, accept, cond)` returns the **second** arg when `cond` is true — the reverse of HLSL's `select(trueVal, falseVal, cond)`. In `shaders/gpu_driven.wgsl` the cull pass must stay `select(0u, u32(flags.z), visible)` (visible ⇒ full count, culled ⇒ 0). Swapped args silently zero the counts of every visible entity → black window. See the GOTCHA comment at the top of that shader. - **wgpu 30 API drift** (verified this session): `BufferInitDescriptor` has a `contents: &[u8]` field (not `data`) and `create_buffer_init` comes from the `wgpu::util::DeviceExt` trait (import it, as in `mesh.rs`). `BlendState::ALPHA_BLENDING` is the alpha-blend constant (there is no `ALPHA`); `DepthStencilState` has **no** `Default` impl — write `stencil`/`bias` fields explicitly. `min_binding_size` is `Option>`. bytemuck 1.25: `Zeroable::zeroed()` is not `const` (const traits unstable) — use a const literal for `ZERO`-style constants. For layout-offset tests prefer `std::mem::offset_of!` (stable 1.77, no unsafe). - **LOD UV blending: never fold integer-tile jumps, freeze seam twins instead** (cost us a day, 2026-09-23): a UV *seam* is two copies of the same 3-D point on integer-apart UVs (u=0/u=1 columns) — it is NOT a mesh edge, so the decimation must record the weld's refused pairs and **freeze** those twins (any edge touching one is excluded from the PQ). A co-facial edge spanning a whole tile (cone apex v=1 ↔ base v=0) is a *legit* chart span — the chart is bilinear, so the UVs **blend linearly** (fold the integer jump to zero and the apex UV smears down the cone side). And the attribute-aware weld refuses a Δ of *exactly* 0.5 (ambiguous: seam at its widest vs legit half-tile jump — the cone's u=1 column vs the cap-disc chart sits exactly there). See the comments in `geometry.rs` (`welded`, `Collapse::collapse_edge`) and the cone/seam regression tests. ## lean-ctx Prefer lean-ctx MCP tools over native equivalents for token savings. Full rules: @LEAN-CTX.md