| title | Depth/Stencil and Multi-Sample Rendering | |||||
|---|---|---|---|---|---|---|
| document_id | depth-stencil-msaa-2025-11-11 | |||||
| status | draft | |||||
| created | 2025-11-11T00:00:00Z | |||||
| last_updated | 2026-02-07T00:00:00Z | |||||
| version | 0.5.1 | |||||
| engine_workspace_version | 2023.1.30 | |||||
| wgpu_version | 26.0.1 | |||||
| shader_backend_default | naga | |||||
| winit_version | 0.29.10 | |||||
| repo_commit | 544444652b4dc3639f8b3e297e56c302183a7a0b | |||||
| owners |
|
|||||
| reviewers |
|
|||||
| tags |
|
Summary
- Add configurable depth testing/writes and multi-sample anti-aliasing (MSAA)
to the high-level rendering API via builders, without exposing
wgputypes. - Provide validation and predictable defaults to enable 3D scenes and higher-quality rasterization in example and production code.
- Reject unsupported sample counts based on device format capabilities while defaulting to safe fallbacks.
- Goals
- Expose depth/stencil and multi-sample configuration on
RenderPassBuilderandRenderPipelineBuilderusing engine/platform types;wgputypes are not exposed. - Validate device capabilities and configuration consistency at build time.
- Define defaults for depth clear, compare operation, and sample count.
- Map high-level configuration to
lambda-rs-platformandwgpuinternally.
- Expose depth/stencil and multi-sample configuration on
- Non-Goals
- Advanced per-draw depth bias configuration.
- Post-process or temporal anti-aliasing techniques.
- Vendor-specific tuning beyond standard device limits.
- Multi-sample anti-aliasing (MSAA): rasterization technique that stores multiple coverage samples per pixel and resolves them to a single color.
- Depth/stencil attachment: a
GPUtexture used for depth testing and optional stencil operations. - Sample count: number of samples per pixel for targets and pipelines.
- Resolve: the operation that produces a single-sample color target from a multi-sampled color target at the end of a pass.
- High-level builders in
lambda-rscollect depth/stencil and multi-sample configuration using engine/platform types. lambda-rs-platformtranslates those types into backend-specific representations forwgpucreation of textures, passes, and pipelines.
App Code
└── lambda-rs (RenderPassBuilder / RenderPipelineBuilder)
└── DepthStencil + MultiSample config (engine/platform types)
└── lambda-rs-platform (mapping/validation)
└── wgpu device/pipeline/pass
-
API Surface
-
Types (engine-level)
enum DepthFormat { Depth32Float, Depth24Plus, Depth24PlusStencil8 }enum CompareFunction { Never, Less, LessEqual, Greater, GreaterEqual, Equal, NotEqual, Always }struct MultiSample { sample_count: u32 }(MUST be >= 1 and supported)- Stencil per-face state and operations exist at the platform layer and are exposed through
RenderPipelineBuilder::with_stencil(...).
-
Builders (selected functions)
RenderPassBuilder::with_clear_color([f64; 4]) -> SelfRenderPassBuilder::with_depth() -> SelfRenderPassBuilder::with_depth_clear(f64) -> SelfRenderPassBuilder::with_stencil() -> SelfRenderPassBuilder::with_stencil_clear(u32) -> SelfRenderPassBuilder::with_multi_sample(u32) -> SelfRenderPipelineBuilder::with_depth_format(DepthFormat) -> SelfRenderPipelineBuilder::with_depth_compare(CompareFunction) -> SelfRenderPipelineBuilder::with_depth_write(bool) -> SelfRenderPipelineBuilder::with_stencil(StencilState) -> SelfRenderPipelineBuilder::with_multi_sample(u32) -> Self
-
Example (engine types only)
use lambda::render::render_pass::RenderPassBuilder; use lambda::render::pipeline::{RenderPipelineBuilder, CompareFunction}; use lambda::render::texture::DepthFormat; let pass = RenderPassBuilder::new() .with_clear_color([0.0, 0.0, 0.0, 1.0]) .with_depth_clear(1.0) .with_multi_sample(4) .build( render_context.gpu(), render_context.surface_format(), render_context.depth_format(), ); let pipeline = RenderPipelineBuilder::new() .with_multi_sample(4) .with_depth_format(DepthFormat::Depth32Float) .with_depth_compare(CompareFunction::Less) .build( render_context.gpu(), render_context.surface_format(), render_context.depth_format(), &pass, &vertex_shader, Some(&fragment_shader), );
-
-
Behavior
- Defaults
- If neither depth nor stencil is requested on the pass, the pass MUST NOT create a depth attachment and depth testing is disabled.
- When depth operations are enabled on the pass, the depth aspect defaults
to a clear value of
1.0when no explicit clear is provided. - Pipeline depth compare defaults to
CompareFunction::Lesswhen depth is enabled for a pipeline and no explicit compare is provided. MultiSample.sample_countdefaults to1(no multi-sampling).
- Attachment creation
- When depth is requested (
with_depth/with_depth_clear), the pass MUST create a depth attachment. - When stencil operations are requested on the pass
(
with_stencil/with_stencil_clear), the pass MUST attach a depth/stencil view and the depth format MUST include a stencil aspect. - If stencil is requested but the current depth format lacks a stencil
aspect, the engine upgrades to
Depth24PlusStencil8at pass build time or during encoding and logs an error. - When depth operations are present, the depth aspect MUST be cleared or
loaded according to the configured depth ops (defaulting to a clear of
1.0when no explicit clear is provided). When only stencil operations are present, the stencil aspect MUST be cleared or loaded according to the configured stencil ops and the depth aspect MUST remain untouched.
- When depth is requested (
- Defaults
-
Multi-sample semantics
- When
sample_count > 1, the pass MUST render into a multi-sampled color target and resolve to the single-sample swap chain target before present.- The pipeline
sample_countMUST equal the passsample_count. If a mismatch is detected during pipeline build, the engine aligns the pipeline to the pass sample count and logs an error.
- The pipeline
- Matching constraints
- If a pipeline declares a depth format, it MUST equal the pass depth
attachment format. Mismatches are errors at build time. When a pipeline
enables stencil, the engine upgrades its depth format to
Depth24PlusStencil8to guarantee compatibility.
- If a pipeline declares a depth format, it MUST equal the pass depth
attachment format. Mismatches are errors at build time. When a pipeline
enables stencil, the engine upgrades its depth format to
- When
- Validation is performed in
lambda-rsduring builder configuration andbuild(...). Current behavior prefers logging and safe fallbacks over returning errors to preserve API stability. - Multi-sample count validation
- Allowed counts: 1, 2, 4, 8. Other values are rejected with an error log and
clamped to
1duringwith_multi_sample(...). - On pipeline build, if the pipeline sample count differs from the pass, the engine aligns the pipeline to the pass and logs an error.
- Device capability validation rejects unsupported sample counts for the
surface format and active depth/stencil format, logging and falling back to
1when necessary.
- Allowed counts: 1, 2, 4, 8. Other values are rejected with an error log and
clamped to
- Depth clear validation
- Clear values outside
[0.0, 1.0]SHOULD be rejected; current engine path relies on caller-provided sane values andwgpuvalidation. A strict check MAY be added in a follow-up.
- Clear values outside
- Debug builds: all validations are enabled unconditionally (
debug_assertions). - Release builds: only cheap safety checks remain always-on; logging and
per-draw checks are controlled by Cargo features on
lambda-rs. - Feature flags
render-validation-msaa: validate/log MSAA counts; pass/pipeline mismatch logs.render-validation-depth: clamp/log depth clear; depth usage advisories.render-validation-stencil: stencil usage/format upgrade advisories.render-validation-device: device/format capability advisories (MSAA sample support).
Always-on safeguards (release and debug)
- Clamp depth clear to
[0.0, 1.0]. - Align pipeline
sample_countto the passsample_count. - Clamp invalid MSAA sample counts to
1.
- Multi-sample
sample_countMUST be one of the device-supported counts. It is typically {1, 2, 4, 8}. Non-supported counts MUST be rejected. Depth24PlusandDepth24PlusStencil8MAY be emulated by the backend. The platform layer MUST query support before allocation.- Depth clear values MUST be clamped to [0.0, 1.0] during validation.
- When the pass has no depth attachment, pipelines MUST behave as if depth testing and depth writes are disabled. Stencil-only passes still bind a depth/stencil attachment; in this case the stencil aspect is active and the depth aspect MUST remain unchanged when no depth operations are configured.
- Use 4x multi-sampling by default for higher quality at moderate cost.
- Rationale: 4x is widely supported and balances quality and performance.
- Prefer
Depth24Plusfor memory savings when stencil is not required.- Rationale:
Depth32Floatincreases memory bandwidth and storage.
- Rationale:
- Disable depth writes (
write = false) for purely transparent or overlay passes.- Rationale: Skips unnecessary bandwidth and improves parallelism.
- Functionality
- Depth testing: enable/disable, clear, compare; depth write toggle
(engine:
RenderPipelineBuilder::with_depth,.with_depth_clear,.with_depth_compare,.with_depth_write) - Stencil: clear/load/store, per-face ops, read/write mask, reference
(platform stencil state; pass-level ops +
SetStencilReference) - MSAA: sample count selection, resolve path, depth sample matching
- Format selection:
Depth32Float,Depth24Plus,Depth24PlusStencil8 - Edge cases: invalid sample counts (clamp/log), pass/pipeline sample mismatches (align/log); stencil implies stencil-capable format (upgrade)
- Depth testing: enable/disable, clear, compare; depth write toggle
(engine:
- API Surface
- RenderPassBuilder: color ops, depth ops, stencil ops, MSAA
- RenderPipelineBuilder: depth format/compare, stencil state, depth write, MSAA
- Commands: set stencil reference; existing draw/bind/viewport remain
- Validation and Errors
- Sample counts limited to {1,2,4,8}; invalid → clamp to 1 (log via features)
- Pass/pipeline sample mismatch → align to pass (log via features)
- Depth clear clamped to [0.0, 1.0] (log via features)
- Device/format MSAA support check with fallback to 1
- Performance
- 4x MSAA guidance; memory trade-offs for
Depth32FloatvsDepth24Plus - Recommend disabling depth writes for overlays/transparency
- 4x MSAA guidance; memory trade-offs for
- Documentation and Examples
- Minimal MSAA + depth example
- Reflective mirror (stencil) tutorial
- Unit Tests
- Validate mapping of engine types to platform/wgpu types.
- Validate rejection of unsupported sample counts and format mismatches.
- Commands:
cargo test -p lambda-rs -- --nocapture
- Integration Tests
- Render a depth-tested scene (e.g., overlapping cubes) at sample counts of 1 and 4; verify occlusion and smoother edges when multi-sampling is enabled.
- Commands:
cargo test --workspace
- Manual Checks (if necessary)
- Run
cargo run -p lambda-demos-minimal --bin minimalwith a toggle for multi-sampling and observe aliasing reduction with 4x multi-sampling.
- Run
- No breaking changes. New configuration is additive and does not expose
wgputypes in the high-level API. Existing examples continue to render with defaults (no depth, no multi-sampling) unless explicitly configured.
- 2026-02-05 (v0.5.1) — Update demo run commands for
demos/. - 2025-12-15 (v0.5.0) — Update example code to use
render_context.gpu()and addsurface_format/depth_formatparameters toRenderPassBuilderandRenderPipelineBuilder. - 2025-11-21 (v0.4.1) — Clarify depth attachment and clear behavior for stencil-only passes; align specification with engine behavior that preserves depth when only stencil operations are configured.
- 2025-11-21 (v0.4.0) — Add device/format sample-count validation with fallback to 1; update metadata and checklist; record implementation references for depth/stencil/MSAA.
- 2025-11-17 (v0.3.1) — Remove umbrella validation flags from this spec; list only feature flags related to MSAA, depth, and stencil; metadata updated.
- 2025-11-11 (v0.1.1) — Add MSAA validation in builders; align pipeline and pass sample counts; document logging-based fallback semantics.
- 2025-11-11 (v0.1.0) — Initial draft.