Skip to main content

khora_lanes/render_lane/shadows_lane/
standard.rs

1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Full-quality shadows lane.
16//!
17//! The lane **is** its quality — its atlas dimensions are declared as
18//! `const` on the type, not received as configuration. `ShadowAgent`
19//! picks this lane (vs [`super::LowResShadowsLane`]) by name when the
20//! GORNA budget allows the higher VRAM / time cost.
21
22use std::any::Any;
23
24use khora_core::lane::{Lane, LaneContext, LaneError, LaneKind, Ref};
25use khora_core::renderer::GraphicsDevice;
26use khora_data::render::RenderWorld;
27
28use super::algo::ShadowsLaneState;
29
30/// Stable strategy name advertised to the agent / GORNA.
31pub const STRATEGY_NAME: &str = "StandardShadows";
32
33/// Full-quality shadows: 2048² × 4-layer 2D atlas + 512² × 4-cube cube
34/// atlas. Drives the canonical CSM / spot perspective / 6-pass point
35/// pipeline; publishes a [`khora_data::render::ShadowGpuBindings`]
36/// bundle plus per-light entries into the per-frame lane context.
37#[derive(Default)]
38pub struct StandardShadowsLane {
39    state: ShadowsLaneState,
40}
41
42impl StandardShadowsLane {
43    /// 2D depth atlas resolution (per layer).
44    pub const ATLAS_2D_RESOLUTION: u32 = 2048;
45    /// Number of directional / spot shadow casters per frame.
46    pub const ATLAS_2D_MAX_LIGHTS: u32 = 4;
47    /// Cube atlas per-face resolution.
48    pub const CUBE_FACE_RESOLUTION: u32 = 512;
49    /// Number of point shadow casters per frame.
50    pub const CUBE_MAX_LIGHTS: u32 = 4;
51
52    /// Creates a new lane in its uninitialised state.
53    pub fn new() -> Self {
54        Self::default()
55    }
56}
57
58impl Lane for StandardShadowsLane {
59    fn strategy_name(&self) -> &'static str {
60        STRATEGY_NAME
61    }
62
63    fn lane_kind(&self) -> LaneKind {
64        LaneKind::Shadow
65    }
66
67    fn estimate_cost(&self, ctx: &LaneContext) -> f32 {
68        let render_world = match ctx.get::<Ref<RenderWorld>>() {
69            Some(slot) => slot.get(),
70            None => return 1.0,
71        };
72        super::cost_estimate(render_world)
73    }
74
75    fn on_initialize(&self, ctx: &mut LaneContext) -> Result<(), LaneError> {
76        let device = ctx
77            .get::<std::sync::Arc<dyn GraphicsDevice>>()
78            .ok_or(LaneError::missing("Arc<dyn GraphicsDevice>"))?
79            .clone();
80        let pipeline_system = ctx
81            .get::<std::sync::Arc<dyn khora_core::renderer::traits::PipelineSystem>>()
82            .ok_or(LaneError::missing("Arc<dyn PipelineSystem>"))?
83            .clone();
84        self.state
85            .init_gpu(
86                device.as_ref(),
87                pipeline_system.as_ref(),
88                Self::ATLAS_2D_RESOLUTION,
89                Self::ATLAS_2D_MAX_LIGHTS,
90                Self::CUBE_FACE_RESOLUTION,
91                Self::CUBE_MAX_LIGHTS,
92                "StandardShadows",
93            )
94            .map_err(|e| LaneError::InitializationFailed(Box::new(e)))
95    }
96
97    fn execute(&self, ctx: &mut LaneContext) -> Result<(), LaneError> {
98        super::execute_shared(
99            &self.state,
100            Self::ATLAS_2D_MAX_LIGHTS,
101            Self::CUBE_MAX_LIGHTS,
102            STRATEGY_NAME,
103            ctx,
104        )
105    }
106
107    fn on_shutdown(&self, ctx: &mut LaneContext) {
108        if let Some(device) = ctx.get::<std::sync::Arc<dyn GraphicsDevice>>() {
109            self.state.shutdown(device.as_ref());
110        }
111    }
112
113    fn as_any(&self) -> &dyn Any {
114        self
115    }
116
117    fn as_any_mut(&mut self) -> &mut dyn Any {
118        self
119    }
120}