renderling/debug/
cpu.rs

1//! CPU side of drawing debugging overlays.
2
3use std::sync::{Arc, Mutex};
4
5#[derive(Clone)]
6pub struct DebugOverlay {
7    pipeline: Arc<wgpu::RenderPipeline>,
8    bindgroup_layout: Arc<wgpu::BindGroupLayout>,
9    bindgroup: Arc<Mutex<Option<wgpu::BindGroup>>>,
10}
11
12impl DebugOverlay {
13    const LABEL: Option<&'static str> = Some("debug-overlay");
14
15    fn create_bindgroup_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
16        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
17            label: Self::LABEL,
18            entries: &[
19                // stage slab
20                wgpu::BindGroupLayoutEntry {
21                    binding: 0,
22                    visibility: wgpu::ShaderStages::FRAGMENT,
23                    ty: wgpu::BindingType::Buffer {
24                        ty: wgpu::BufferBindingType::Storage { read_only: true },
25                        has_dynamic_offset: false,
26                        min_binding_size: None,
27                    },
28                    count: None,
29                },
30                // draw calls
31                wgpu::BindGroupLayoutEntry {
32                    binding: 1,
33                    visibility: wgpu::ShaderStages::FRAGMENT,
34                    ty: wgpu::BindingType::Buffer {
35                        ty: wgpu::BufferBindingType::Storage { read_only: true },
36                        has_dynamic_offset: false,
37                        min_binding_size: None,
38                    },
39                    count: None,
40                },
41            ],
42        })
43    }
44
45    fn create_pipeline_layout(
46        device: &wgpu::Device,
47        bindgroup_layout: &wgpu::BindGroupLayout,
48    ) -> wgpu::PipelineLayout {
49        device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
50            label: Self::LABEL,
51            bind_group_layouts: &[bindgroup_layout],
52            push_constant_ranges: &[],
53        })
54    }
55
56    fn create_pipeline(
57        device: &wgpu::Device,
58        bindgroup_layout: &wgpu::BindGroupLayout,
59        format: wgpu::TextureFormat,
60    ) -> wgpu::RenderPipeline {
61        let vertex = crate::linkage::debug_overlay_vertex::linkage(device);
62        let fragment = crate::linkage::debug_overlay_fragment::linkage(device);
63        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
64            label: Self::LABEL,
65            layout: Some(&Self::create_pipeline_layout(device, bindgroup_layout)),
66            vertex: wgpu::VertexState {
67                module: &vertex.module,
68                entry_point: None,
69                compilation_options: wgpu::PipelineCompilationOptions::default(),
70                buffers: &[],
71            },
72            primitive: wgpu::PrimitiveState {
73                topology: wgpu::PrimitiveTopology::TriangleList,
74                strip_index_format: None,
75                front_face: wgpu::FrontFace::Ccw,
76                cull_mode: None,
77                unclipped_depth: false,
78                polygon_mode: wgpu::PolygonMode::Fill,
79                conservative: false,
80            },
81
82            depth_stencil: None,
83            multisample: wgpu::MultisampleState::default(),
84            fragment: Some(wgpu::FragmentState {
85                module: &fragment.module,
86                entry_point: None,
87                compilation_options: wgpu::PipelineCompilationOptions::default(),
88                targets: &[Some(wgpu::ColorTargetState {
89                    format,
90                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
91                    write_mask: wgpu::ColorWrites::all(),
92                })],
93            }),
94            multiview: None,
95            cache: None,
96        })
97    }
98
99    pub fn create_bindgroup(
100        &self,
101        device: &wgpu::Device,
102        slab_buffer: &wgpu::Buffer,
103        indirect_draw_buffer: &wgpu::Buffer,
104    ) -> wgpu::BindGroup {
105        device.create_bind_group(&wgpu::BindGroupDescriptor {
106            label: Self::LABEL,
107            layout: &self.bindgroup_layout,
108            entries: &[
109                wgpu::BindGroupEntry {
110                    binding: 0,
111                    resource: wgpu::BindingResource::Buffer(slab_buffer.as_entire_buffer_binding()),
112                },
113                wgpu::BindGroupEntry {
114                    binding: 1,
115                    resource: wgpu::BindingResource::Buffer(
116                        indirect_draw_buffer.as_entire_buffer_binding(),
117                    ),
118                },
119            ],
120        })
121    }
122
123    pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
124        let bindgroup_layout = Arc::new(Self::create_bindgroup_layout(device));
125        Self {
126            pipeline: Self::create_pipeline(device, &bindgroup_layout, format).into(),
127            bindgroup_layout,
128            bindgroup: Arc::new(Mutex::new(None)),
129        }
130    }
131
132    pub fn render(
133        &self,
134        device: &wgpu::Device,
135        queue: &wgpu::Queue,
136        view: &wgpu::TextureView,
137        slab_buffer: &wgpu::Buffer,
138        indirect_draw_buffer: &wgpu::Buffer,
139    ) {
140        let mut encoder =
141            device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Self::LABEL });
142        {
143            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
144                label: Self::LABEL,
145                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
146                    view,
147                    resolve_target: None,
148                    ops: wgpu::Operations {
149                        load: wgpu::LoadOp::Load,
150                        store: wgpu::StoreOp::Store,
151                    },
152                    depth_slice: None,
153                })],
154                depth_stencil_attachment: None,
155                timestamp_writes: None,
156                occlusion_query_set: None,
157            });
158            render_pass.set_pipeline(&self.pipeline);
159            // UNWRAP: panic on purpose
160            let mut guard = self.bindgroup.lock().unwrap();
161            if guard.is_none() {
162                *guard = Some(self.create_bindgroup(device, slab_buffer, indirect_draw_buffer));
163            }
164            render_pass.set_bind_group(0, guard.as_ref(), &[]);
165            render_pass.draw(0..6, 0..1);
166        }
167        queue.submit(Some(encoder.finish()));
168    }
169}