loam_render/passes/
line.rs1use std::cell::RefCell;
2use std::rc::Rc;
3
4use glam::Vec2;
5use loam_runtime::{Eye, SegmentRecord};
6use wgpu::{CommandEncoder, Device, Queue, RenderPass};
7
8use crate::device::GpuContext;
9use crate::pass::{FrameFormat, FramePass, FrameTarget, PassStage};
10use crate::{DepthConvention, DepthMode, LineRasterNode};
11
12struct State {
13 eye: Eye,
14 segments: Vec<SegmentRecord>,
15 uploaded: bool,
16 node: Option<LineRasterNode>,
17 device: Option<Device>,
18 queue: Option<Queue>,
19}
20
21#[derive(Clone)]
22pub struct LinePass {
23 name: &'static str,
24 shared: Rc<RefCell<State>>,
25}
26
27impl LinePass {
28 pub fn new(name: &'static str) -> Self {
29 Self {
30 name,
31 shared: Rc::new(RefCell::new(State {
32 eye: Eye::default(),
33 segments: Vec::new(),
34 uploaded: false,
35 node: None,
36 device: None,
37 queue: None,
38 })),
39 }
40 }
41
42 pub fn publish(&self, eye: &Eye, segments: &[SegmentRecord]) {
43 let mut state = self.shared.borrow_mut();
44 state.eye = *eye;
45 state.uploaded &= state.segments == segments;
46 state.segments.clear();
47 state.segments.extend_from_slice(segments);
48 }
49
50 pub fn segment_count(&self) -> usize {
51 self.shared.borrow().segments.len()
52 }
53}
54
55impl FramePass for LinePass {
56 fn name(&self) -> &'static str {
57 self.name
58 }
59
60 fn stage(&self) -> PassStage {
61 PassStage::Scene
62 }
63
64 fn depth_read(&self) -> Option<DepthConvention> {
65 Some(DepthConvention::ReversedZ)
66 }
67
68 fn shares_pass(&self) -> bool {
69 true
70 }
71
72 fn prepare(
73 &mut self,
74 _encoder: &mut CommandEncoder,
75 target: &FrameTarget<'_>,
76 ) -> anyhow::Result<()> {
77 let mut state = self.shared.borrow_mut();
78 let State {
79 eye,
80 segments,
81 uploaded,
82 node,
83 device,
84 queue,
85 ..
86 } = &mut *state;
87 let (Some(node), Some(device), Some(queue)) =
88 (node.as_mut(), device.as_ref(), queue.as_ref())
89 else {
90 return Ok(());
91 };
92 node.set_camera(
93 queue,
94 crate::view::root_view_projection(eye),
95 Vec2::new(target.size.0 as f32, target.size.1 as f32),
96 );
97 if !*uploaded {
98 node.upload_segments(device, queue, segments);
99 *uploaded = true;
100 }
101 Ok(())
102 }
103
104 fn draw(&mut self, pass: &mut RenderPass<'_>, _target: &FrameTarget<'_>) -> anyhow::Result<()> {
105 if let Some(node) = self.shared.borrow().node.as_ref() {
106 node.draw(pass, None);
107 }
108 Ok(())
109 }
110
111 fn record(
112 &mut self,
113 encoder: &mut CommandEncoder,
114 target: &FrameTarget<'_>,
115 ) -> anyhow::Result<()> {
116 let Some(depth) = target.depth else {
117 return Ok(());
118 };
119 self.prepare(encoder, target)?;
120 if let Some(node) = self.shared.borrow().node.as_ref() {
121 node.record(encoder, target.color, Some(depth), None);
122 }
123 Ok(())
124 }
125
126 fn attach(&mut self, gpu: &GpuContext, frame: FrameFormat) -> anyhow::Result<()> {
127 let mut state = self.shared.borrow_mut();
128 state.node = Some(LineRasterNode::new(
129 &gpu.device,
130 frame.color,
131 DepthMode::ReadOnly {
132 format: frame.depth,
133 },
134 DepthConvention::ReversedZ,
135 ));
136 state.device = Some(gpu.device.clone());
137 state.queue = Some(gpu.queue.clone());
138 state.uploaded = false;
139 Ok(())
140 }
141}