# WSG — Detailed Documentation > Technical content from the main README: status, architecture, API reference, workflows, roadmap. ## Status | Area | State | |------|-------| | Manual workflow (`Context` + `Renderer` + `PipelineCache`) | ✅ Working (advanced — fine-grained control) | | `App` / `AppBuilder` / `AppHandler` event-loop facade | ✅ Working — window, events, frame presentation, automatic scene rendering | | `Scene` resource/entity registry | ✅ Working — auto-rendered in one batched pass (`App::render_scene`) | | GPU-driven two-pass pipeline (Compute → indirect draw) | ✅ Working (Phase 3) — `render_scene` + shadow pass 100% indirect; opt-in frustum culling | | 3D infrastructure (uniform bind groups, MVP + camera) | ✅ Working — per-frame camera + per-entity world matrices in shared uniforms | | Shadows (shadow mapping) | ✅ Working — directional/spot, slope-scaled bias, PCF 3×3 | | HDR + Tone Mapping | ✅ Working — offscreen Rgba16Float, ACES/Reinhard, opt-in | | LOD (Level of Detail) | ✅ Working — quadric decimation, hysteresis, multi-level buffer | | Mesh module (primitives + import) | ✅ Working — feature-gated primitives, OBJ parser | | Bloom | ✅ Working — threshold + separable blur + composite, HDR required | | Fog | ✅ Working — 3 modes (linear, exp, exp²), runtime switchable | | MSAA | ✅ Working — 4× multisample, resolve pass | | DoF | ✅ Working — CoC + disc blur, focus presets | | PBR (metallic/roughness + normal maps) | ✅ Working — Cook-Torrance, GGX, IBL, derivative tangent | Note: `standard_shader.wgsl` (Phong + PBR, with an explicit **unlit** mode) is the **single** shader the library ships. Flat 2D drawing is its unlit variant (`Renderer::set_unlit(true)`). ## Architecture ### Layer model - **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. `render_scene` batches all entities into one encoder + one submit per frame. - **Supporting pieces** — `PipelineCache` (shader → compiled RenderPipeline, `Arc`-shared), `Material`, `Geometry`/`Mesh`/`Vertex`, `Scene` (string-ID registry), `Camera`/`Transform`. ### GPU-driven pipeline (Phase 3) A Compute Pass derives each entity's world matrix and fills per-entity indirect draw arguments (with opt-in frustum culling), then the main and shadow render passes issue one indirect draw per active slot. Spec: [docs/tech/ARCHI_CPU_GPU.md](docs/tech/ARCHI_CPU_GPU.md) · User guide: [docs/user/gpu-driven.md](docs/user/gpu-driven.md) ### Module layout ``` lib/src/ ├── lib.rs # crate root, re-exports ├── prelude.rs # glob re-exports ├── app.rs # App + AppBuilder ├── handler.rs # AppHandler trait ├── camera.rs # Camera, CameraController ├── input.rs # InputState ├── lights.rs # Lights, Light, LightType, directional_light, … ├── core/ │ ├── context.rs # GPU lifecycle (Instance/Surface/Adapter/Device/Queue) │ ├── renderer.rs # RenderPass execution, shadow pass, HDR/TM pass │ ├── frame.rs # Per-frame RAII (surface texture + view) │ ├── geometry.rs # Geometry (positions/normals/UVs/indices) + BBox │ ├── transform.rs # Transform (translation/rotation/scale) │ ├── frustum.rs # Frustum (6 planes, sphere/box culling) │ ├── lod.rs # LOD decimation (quadric edge collapse) │ ├── shadow.rs # ShadowConfig (map size, bias, PCF) │ ├── hdr.rs # ToneMapper enum (Aces/Reinhard) │ ├── bloom.rs # BloomConfig + BloomPipeline │ ├── msaa.rs # MsaaConfig │ ├── fog.rs # FogConfig + FogMode │ └── dof.rs # DoFConfig + DoFPipeline ├── mesh/ │ ├── mod.rs # Re-exports flat │ ├── primitives/ # 6 feature-gated generators │ └── import/ # OBJ parser + glTF stub ├── pipeline/ # PipelineCache (shader → RenderPipeline) ├── resources/ # Mesh, Material, Texture, Uniform, Vertex ├── scene/ # Scene (registry), Entity └── utils/ # Conf constants, WsgError ``` ## Quick reference (types) | Concept | Type | Responsibility | |---------|------|---------------| | App / AppBuilder | Facade | Window + event loop + frame + auto scene render | | AppHandler | Trait | `setup()` / `update()` / `render()` callbacks | | Scene | Struct | Registry: shaders, materials, meshes, entities, lights, camera | | Context | Struct | GPU hardware (Instance, Surface, Adapter, Device, Queue) | | Renderer | Struct | RenderPass execution (scene, shadow, HDR/TM, bloom, DoF) | | PipelineCache | Struct | Shader → compiled RenderPipeline cache | | Material | Struct | Shader ID + texture + pipeline + PBR params | | Geometry | Struct | CPU vertex data (positions/normals/UVs/colors/indices) | | Mesh / Vertex | Struct | GPU geometry / interleaved upload tuple | | Frame | Struct | Per-frame RAII (surface texture + view) | | Camera / Transform | Struct | Camera math + per-entity transform | | CameraController | Struct | Orbital camera (orbit/zoom/reset/apply_to) | | InputState | Struct | Unified keyboard/mouse (pressed/held/released, delta, scroll) | | Texture | Struct | GPU image (Rgba8UnormSrgb) + sampler | | Lights / Light | Struct | Light list (directional/point/spot, MAX=8) + ambient | | ShadowConfig | Struct | Shadow map size, bias, PCF taps, scene radius | | ToneMapper | Enum | ACES Filmic / Reinhard | | BloomConfig | Struct | Threshold, intensity, H/V passes | | MsaaConfig | Struct | Sample count (1 = disabled) | | FogConfig | Struct | Mode, near/far, density, color | | DoFConfig | Struct | Focus distance, aperture, max blur | | BBox | Struct | Axis-aligned bounding box (min/max) | | Frustum | Struct | 6 planes, sphere/box culling | ## Declarative workflow (recommended) ```rust use wsg_lib::prelude::*; use wsg_lib::app::AppBuilder; use wsg_lib::utils::WsgError; struct MyScene; impl AppHandler for MyScene { fn setup(&mut self, app: &mut wsg_lib::App) { app.scene .register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH) .unwrap(); app.scene.add_material_shader("mat", "standard").unwrap(); app.scene.create_mesh("cube", cube(1.0), Some("mat")).unwrap(); app.scene.add_entity("my_cube", "cube").unwrap(); } fn update(&mut self, app: &mut wsg_lib::App) { // your per-frame logic } // render() default: app.render_scene(frame.view()) — auto-draws everything } #[pollster::main] async fn main() -> Result<(), WsgError> { let app = AppBuilder::new().title("WSG").build().await?; app.run(MyScene); Ok(()) } ``` > `Scene` methods return `Result<_, String>` — typed-error unification is on the roadmap. ## Manual workflow (advanced) Bypass the `App` facade and drive `Context`, `Renderer` and `PipelineCache` yourself: ```rust use std::sync::Arc; 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::{Geometry, Material, Mesh}; use wsg_lib::utils; fn main() { 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"); let format = context.configure(&context.adapter, 800, 600).expect("surface config"); let mut renderer = Renderer::new(&context, format, 800, 600); let mut cache = PipelineCache::new(Arc::new(context.device.clone())); cache.register_shader("standard", utils::STANDARD_SHADER_PATH).unwrap(); let material = Material::new(renderer.format(), "standard", &mut cache); let geometry = Geometry::new(vec![-0.5f32, 0.5, 0.0, 0.5, 0.5, 0.0, 0.5, -0.5, 0.0, -0.5, -0.5, 0.0]) .with_indices(vec![0, 1, 2, 0, 2, 3]); let mesh = Mesh::from_geometry(renderer.device(), Arc::new(geometry), None); 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(); } ``` ## Features | Feature | Default | Provides | |---------|---------|----------| | `prim-cube` | ✅ | `cube(size)` | | `prim-plane` | ✅ | `plane(w, d, seg_x, seg_z)` | | `prim-sphere` | ✅ | `uv_sphere(…)`, `icosphere(…)` | | `prim-cylinder` | ✅ | `cylinder(…)` | | `prim-cone` | ✅ | `cone(…)` | | `prim-torus` | ✅ | `torus(…)` | | `all-prims` | ✅ (default) | All 6 primitives | | `import-obj` | ⬜ | `load_obj(path)`, `parse_obj(str)` | | `import-gltf` | ⬜ | `load_gltf(path)` (stub) | ## Design principle: opt-in = zero cost | Feature | How to enable | If NOT enabled | |---------|--------------|----------------| | Shadows | `scene.set_shadow_caster(Some(idx))` | No shadow map, no depth pass, no PCF | | HDR + TM | `AppBuilder::with_hdr(ToneMapper::Aces)` | No offscreen texture, no TM pass | | Bloom | `AppBuilder::with_bloom(BloomConfig::…)` | No bloom textures, no passes | | MSAA | `AppBuilder::with_msaa(MsaaConfig { sample_count: 4 })` | Single sample, no resolve | | Fog | `AppBuilder::with_fog(FogConfig::…)` | No fog uniforms | | DoF | `AppBuilder::with_dof(DoFConfig::…)` | No CoC/blur textures | | GPU-driven culling | `AppBuilder::with_gpu_driven(true)` | No compute pipeline, no indirect buffers | | LOD | `scene.create_mesh_with_lod(…, levels)` | Single-level mesh | | Primitives | Cargo feature `prim-*` | Not compiled | | File import | Cargo feature `import-*` | Not compiled | ## Roadmap | Phase | Status | |-------|--------| | 1 — Foundations (window, render loop, Context) | ✅ | | 2 — 3D infrastructure (Geometry, Mesh, Material, Pipeline) | ✅ | | 3 — GPU-driven (compute pass, indirect draws, culling) | ✅ | | 4 — Advanced rendering (shadows, HDR/TM, lights) | ✅ | | 5 — Polish (LOD, camera controller, input, demo) | ✅ | | 6 — Post-MVP (bloom, PBR, fog, DoF, MSAA, cascaded shadows, SSAO) | 🔄 | ## Documentation | Where | What | |-------|------| | [docs/user/](docs/user/README.md) | User guide | | [docs/tech/](docs/tech/ARCHI_APP.md) | Internal architecture | | [docs/ROADMAP.md](docs/ROADMAP.md) | Roadmap | | [docs/PLAN.md](docs/PLAN.md) | Recipe book (step history) | | `cargo doc -p wsg-lib --no-deps` | API reference (rustdoc) |