Skip to main content

loam_render/passes/
hyperslice.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use wgpu::{CommandEncoder, Device, Queue};
5
6use crate::device::GpuContext;
7use crate::pass::{ColorLoad, FrameFormat, FramePass, FrameTarget, PassStage};
8use crate::raymarch::{BodyUniform, Hyperslice4DNode, Hyperslice4DUniforms};
9use crate::{DepthConvention, DepthMode, Viewport};
10
11struct State {
12    enabled: bool,
13    source: String,
14    uniforms: Hyperslice4DUniforms,
15    bodies: Vec<BodyUniform>,
16    cells: Vec<(Viewport, f32, BodyUniform)>,
17    node: Option<Hyperslice4DNode>,
18    device: Option<Device>,
19    queue: Option<Queue>,
20}
21
22#[derive(Clone)]
23pub struct HyperslicePass {
24    shared: Rc<RefCell<State>>,
25    load: ColorLoad,
26}
27
28impl HyperslicePass {
29    pub fn new(source: String) -> Self {
30        Self {
31            load: ColorLoad::Load,
32            shared: Rc::new(RefCell::new(State {
33                enabled: true,
34                source,
35                uniforms: Hyperslice4DUniforms::default(),
36                bodies: Vec::new(),
37                cells: Vec::new(),
38                node: None,
39                device: None,
40                queue: None,
41            })),
42        }
43    }
44
45    pub fn publish(&self, uniforms: Hyperslice4DUniforms, bodies: &[BodyUniform]) {
46        let mut state = self.shared.borrow_mut();
47        state.uniforms = uniforms;
48        state.bodies.clear();
49        state.bodies.extend_from_slice(bodies);
50    }
51
52    /// One draw per cell within the frame, each with its own w and body; an empty strip returns the pass to its full-frame draw.
53    pub fn publish_strip(&self, cells: &[(Viewport, f32, BodyUniform)]) {
54        let mut state = self.shared.borrow_mut();
55        state.cells.clear();
56        state.cells.extend_from_slice(cells);
57    }
58
59    pub fn body_count(&self) -> usize {
60        self.shared.borrow().bodies.len()
61    }
62
63    pub fn set_enabled(&self, enabled: bool) {
64        self.shared.borrow_mut().enabled = enabled;
65    }
66
67    pub fn strip_cells(&self) -> usize {
68        self.shared.borrow().cells.len()
69    }
70}
71
72impl FramePass for HyperslicePass {
73    fn name(&self) -> &'static str {
74        "hyperslice"
75    }
76
77    fn stage(&self) -> PassStage {
78        PassStage::Background
79    }
80
81    fn color_load(&self) -> ColorLoad {
82        self.load
83    }
84
85    fn depth_convention(&self) -> Option<DepthConvention> {
86        Some(DepthConvention::ReversedZ)
87    }
88
89    fn depth_read(&self) -> Option<DepthConvention> {
90        Some(DepthConvention::ReversedZ)
91    }
92
93    fn record(
94        &mut self,
95        encoder: &mut CommandEncoder,
96        target: &FrameTarget<'_>,
97    ) -> anyhow::Result<()> {
98        let mut state = self.shared.borrow_mut();
99        if !state.enabled {
100            return Ok(());
101        }
102        let State {
103            uniforms,
104            bodies,
105            cells,
106            node,
107            device,
108            queue,
109            ..
110        } = &mut *state;
111        let (Some(node), Some(device), Some(queue)) =
112            (node.as_mut(), device.as_ref(), queue.as_ref())
113        else {
114            return Ok(());
115        };
116        uniforms.resolution = [target.size.0 as f32, target.size.1 as f32];
117        uniforms.viewport_origin = [0.0, 0.0];
118        *node.uniforms_mut() = *uniforms;
119        if !cells.is_empty() {
120            node.record_strip(device, queue, encoder, target.color, cells, self.load)?;
121            return Ok(());
122        }
123        let Some(depth) = target.depth else {
124            return Ok(());
125        };
126        node.set_bodies(bodies);
127        node.flush_uniforms(queue);
128        node.record(
129            encoder,
130            target.color,
131            Some(depth),
132            Viewport::full([target.size.0, target.size.1]),
133            self.load,
134        );
135        Ok(())
136    }
137
138    fn attach(&mut self, gpu: &GpuContext, frame: FrameFormat) -> anyhow::Result<()> {
139        let mut state = self.shared.borrow_mut();
140        crate::shader::validate_hyperslice_wgsl(&state.source)?;
141        let module = gpu
142            .device
143            .create_shader_module(wgpu::ShaderModuleDescriptor {
144                label: Some("loam-render::passes::hyperslice"),
145                source: wgpu::ShaderSource::Wgsl(state.source.clone().into()),
146            });
147        state.node = Some(Hyperslice4DNode::with_depth(
148            &gpu.device,
149            frame.color,
150            &module,
151            DepthMode::ReadWrite {
152                format: frame.depth,
153            },
154        ));
155        state.device = Some(gpu.device.clone());
156        state.queue = Some(gpu.queue.clone());
157        Ok(())
158    }
159}