1use 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 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 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 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}