From 4a589f42fcc7cb4bc6bfc60f64a313d63354bbae Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Sat, 31 Oct 2026 17:16:52 +0400 Subject: fix(render): preserve projected shadow depth bias in Vulkan pipelines --- crates/fparkan-render/src/lib.rs | 46 +++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'crates/fparkan-render/src') diff --git a/crates/fparkan-render/src/lib.rs b/crates/fparkan-render/src/lib.rs index 08a285d..c4873a0 100644 --- a/crates/fparkan-render/src/lib.rs +++ b/crates/fparkan-render/src/lib.rs @@ -361,6 +361,17 @@ pub enum LegacyCullMode { FrontFace, } +/// Raster depth adjustment applied to one pipeline variant. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum LegacyDepthBias { + /// Preserve the interpolated depth exactly. + #[default] + Disabled, + /// Move projected decals toward the camera so they pass against their + /// coplanar receiver under a less-or-equal depth test. + ProjectedDecal, +} + /// Legacy fixed-function state that changes graphics-pipeline structure. /// /// Alpha reference is deliberately not present: it is dynamic material data, @@ -375,6 +386,8 @@ pub struct LegacyPipelineState { pub cull: LegacyCullMode, /// Whether alpha-test shader logic is enabled. pub alpha_test: bool, + /// Raster depth bias for projected decals. + pub depth_bias: LegacyDepthBias, } impl LegacyPipelineState { @@ -405,15 +418,16 @@ impl LegacyPipelineState { /// Canonical, backend-neutral key for a graphics-pipeline variant. /// /// The value is explicitly packed rather than hashed, so captures and caches -/// remain stable across processes and Rust toolchain updates. Three bits are -/// reserved for the five native material blend modes. +/// remain stable across processes and Rust toolchain updates. Bits 0..7 encode +/// blend, depth, cull and alpha-test state; bit 8 distinguishes projected +/// decal depth bias. #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct PipelineKey(u8); +pub struct PipelineKey(u16); impl PipelineKey { /// Returns the canonical packed representation. #[must_use] - pub const fn packed(self) -> u8 { + pub const fn packed(self) -> u16 { self.0 } } @@ -437,7 +451,11 @@ impl From for PipelineKey { LegacyCullMode::BackFace => 1, LegacyCullMode::FrontFace => 2, }; - Self(blend | (depth << 3) | (cull << 5) | (u8::from(state.alpha_test) << 7)) + let depth_bias = match state.depth_bias { + LegacyDepthBias::Disabled => 0, + LegacyDepthBias::ProjectedDecal => 1 << 8, + }; + Self(blend | (depth << 3) | (cull << 5) | (u16::from(state.alpha_test) << 7) | depth_bias) } } @@ -613,6 +631,24 @@ mod tests { assert_eq!(base.with_material_category(5), None); } + #[test] + fn projected_decal_bias_is_a_distinct_preserved_pipeline_state() { + let ordinary = LegacyPipelineState::default(); + let decal = LegacyPipelineState { + depth_bias: LegacyDepthBias::ProjectedDecal, + ..ordinary + }; + assert_eq!(ordinary.depth_bias, LegacyDepthBias::Disabled); + assert_ne!(PipelineKey::from(ordinary), PipelineKey::from(decal)); + assert_eq!( + decal + .with_material_category(1) + .expect("native category is represented") + .depth_bias, + LegacyDepthBias::ProjectedDecal + ); + } + #[test] fn native_material_categories_have_distinct_pipeline_keys() { let base = LegacyPipelineState { -- cgit v1.2.3