loam_render/passes/
hyperslice.rs1use 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 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}