# MSAA (Anti-aliasing) Multi-Sample Anti-Aliasing (MSAA) smooths jagged edges by rendering the scene at a higher sample count (e.g. 4 samples per pixel), then averaging the samples into the final image. ## Activation MSAA is opt-in via the builder. When disabled (default), the renderer uses single-sample rendering with zero overhead: ```rust let app = AppBuilder::new() .title("My App") .with_msaa(4) // 4× MSAA (also: 2 or 8) .build() .await?; ``` ## How it works MSAA is a **rasterizer feature** — no new shader is needed. The pipeline: ```text ┌─────────────────────────────────────────────────────────────────────┐ │ Without MSAA (default) │ │ │ │ Main pass ──→ HDR texture (1 sample) ──→ [Bloom] ──→ TM ──→ Surface │ └─────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────┐ │ With MSAA 4× (and HDR) │ │ │ │ Main pass ──→ MSAA texture (4 samples, Rgba16Float) │ │ ↓ resolve (hardware average) │ │ HDR texture (1 sample) │ │ ↓ │ │ [Bloom] ──→ TM ──→ Surface │ └─────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────┐ │ With MSAA 4× (no HDR) │ │ │ │ Main pass ──→ MSAA texture (4 samples, surface format) │ │ ↓ resolve │ │ Swapchain (surface) │ └─────────────────────────────────────────────────────────────────────┘ ``` Key points: - The **main scene pass** renders into the MSAA texture (N samples/pixel). - The **resolve** (hardware average) produces the single-sample output. - **Post-processes** (bloom, tone mapping) operate on the **resolved** single-sample texture — they are completely unaffected by MSAA. - The **shadow map** is always single-sample (depth-only, not visible directly). ## Sample count | Count | Quality | Cost (approx.) | Use case | |-------|---------|----------------|----------| | 2 | Basic | ~1.1× | Low-end / battery | | **4** | **Good** | **~1.3–1.5×** | **Default, most games** | | 8 | Excellent | ~1.6–2.0× | High-end / static scenes | The cost is in the rasterizer/fill-rate (each pixel is shaded N times at triangles' edges). Interior pixels (covered by a single triangle) are shaded only once — MSAA only multiplies the **edge** cost. ## Runtime query ```rust // In AppHandler::setup or update: let active = app.renderer().msaa_enabled(); // true if sample_count > 1 let count = app.renderer().msaa_sample_count(); // 1, 2, 4, or 8 ``` MSAA is a **build-time** setting: the multi-sample textures are allocated at startup. Changing the sample count requires recreating the textures (window resize does this automatically). ## Configuration struct ```rust use wsg_lib::MsaaConfig; let config = MsaaConfig { sample_count: 4 }; // Or use the builder shortcut (validated): let app = AppBuilder::new().with_msaa(4).build().await?; ``` ## Compatibility | Feature | Compatible? | Notes | |---------|-------------|-------| | HDR + Tone Mapping | ✅ | MSAA texture is `Rgba16Float`, resolves into HDR | | Bloom | ✅ | Bloom reads the resolved (single-sample) HDR texture | | Shadows | ✅ | Shadow map is always single-sample | | Frustum Culling | ✅ | Independent (compute pass) | | LOD | ✅ | Independent (draw args) | | Emissive | ✅ | Per-entity, in the main pass | ## Limitations - **Does not smooth UV-dependent aliasing** (texture shimmer). For that, use mipmaps + anisotropic filtering (future: texture module). - **Cost scales with overdraw**: fully transparent or heavily overlapping geometry pays the full N× cost. - **GPU support**: most modern GPUs support 4× for all formats. 8× may be limited for float formats (check `Device::limits().max_color_attachment_samples`). ## Example ```rust use wsg_lib::app::AppBuilder; use wsg_lib::core::ToneMapper; let app = AppBuilder::new() .title("MSAA Demo") .size(1280, 720) .with_msaa(4) .with_hdr(ToneMapper::Aces) .build() .await?; ``` See `examples/msaa.rs` for a full interactive demo with cube, sphere, and ground plane where aliasing is clearly visible without MSAA.