Files
wsg/docs/user/msaa.md
T
Jérôme Bousquié 9614156848 MSAA
2026-09-25 11:20:20 +02:00

5.6 KiB
Raw Blame History

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:

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:

┌─────────────────────────────────────────────────────────────────────┐
│ 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

// 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

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

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.