Skip to main content

loam_render/passes/
mod.rs

1mod hyperslice;
2mod line;
3mod point;
4mod raymarch;
5mod sky_ground;
6
7pub use hyperslice::HyperslicePass;
8pub use line::LinePass;
9pub use point::PointPass;
10pub use raymarch::RaymarchPass;
11pub use sky_ground::SkyGroundPass;
12
13#[cfg(test)]
14mod tests {
15    use loam_runtime::{Eye, PointRecord};
16    use wgpu::{
17        BackendOptions, Backends, Extent3d, Instance, InstanceDescriptor, NoopBackendOptions,
18        TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
19        TextureViewDescriptor,
20    };
21
22    use super::*;
23    use crate::device::{FeatureRequest, GpuContext};
24    use crate::pass::{FrameFormat, FramePass, FrameTarget, PassSchedule};
25    use crate::view::DEPTH_FORMAT;
26    use crate::DepthConvention;
27
28    const FORMAT: TextureFormat = TextureFormat::Rgba8Unorm;
29    const SIZE: (u32, u32) = (32, 24);
30
31    fn noop_gpu() -> GpuContext {
32        let instance = Instance::new(&InstanceDescriptor {
33            backends: Backends::NOOP,
34            backend_options: BackendOptions {
35                noop: NoopBackendOptions { enable: true },
36                ..Default::default()
37            },
38            ..Default::default()
39        });
40        pollster::block_on(GpuContext::new(instance, FeatureRequest::default(), None))
41            .expect("the noop backend always yields a context")
42    }
43
44    fn frame() -> FrameFormat {
45        FrameFormat {
46            color: FORMAT,
47            depth: DEPTH_FORMAT,
48        }
49    }
50
51    fn attachment(gpu: &GpuContext, format: TextureFormat) -> TextureView {
52        gpu.device
53            .create_texture(&TextureDescriptor {
54                label: None,
55                size: Extent3d {
56                    width: SIZE.0,
57                    height: SIZE.1,
58                    depth_or_array_layers: 1,
59                },
60                mip_level_count: 1,
61                sample_count: 1,
62                dimension: TextureDimension::D2,
63                format,
64                usage: TextureUsages::RENDER_ATTACHMENT,
65                view_formats: &[],
66            })
67            .create_view(&TextureViewDescriptor::default())
68    }
69
70    fn record_once(gpu: &GpuContext, schedule: &mut PassSchedule) {
71        let color = attachment(gpu, FORMAT);
72        let depth = attachment(gpu, DEPTH_FORMAT);
73        let mut encoder = gpu
74            .device
75            .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
76        let target = FrameTarget {
77            color: &color,
78            depth: Some(&depth),
79            size: SIZE,
80        };
81        schedule.begin_frame();
82        schedule
83            .record(crate::PassStage::Background, &mut encoder, &target)
84            .expect("background recorded");
85        schedule
86            .record(crate::PassStage::Scene, &mut encoder, &target)
87            .expect("scene recorded");
88        schedule
89            .record(crate::PassStage::Overlay, &mut encoder, &target)
90            .expect("overlays recorded");
91        schedule.end_frame(&mut encoder);
92        gpu.queue.submit(Some(encoder.finish()));
93    }
94
95    #[test]
96    fn a_publish_that_changes_only_a_color_still_reaches_the_gpu() {
97        let gpu = noop_gpu();
98        let mut point = PointPass::new("vertices");
99        let record = PointRecord {
100            position: [0.0, 0.0, -2.0],
101            radius_px: 4.0,
102            color: [1.0, 0.0, 0.0, 1.0],
103        };
104        point.publish(&Eye::default(), &[record]);
105        point.attach(&gpu, frame()).expect("attach");
106
107        let mut schedule = PassSchedule::new(DepthConvention::ReversedZ);
108        schedule
109            .register(Box::new(point.clone()))
110            .expect("registered");
111        record_once(&gpu, &mut schedule);
112        let after_first = point.uploads();
113
114        point.publish(
115            &Eye::default(),
116            &[PointRecord {
117                color: [0.0, 1.0, 0.0, 1.0],
118                ..record
119            }],
120        );
121        record_once(&gpu, &mut schedule);
122        assert_eq!(
123            point.uploads(),
124            after_first + 1,
125            "the color change never left the CPU: the pass uploaded {} times across both frames",
126            point.uploads()
127        );
128    }
129}