blade_render/render/
debug.rs

1use std::{cell::Cell, mem, ptr};
2
3#[derive(Clone, Copy, Debug, Default)]
4pub struct DebugBlit {
5    pub input: blade_graphics::TextureView,
6    pub mip_level: u32,
7    pub target_offset: [i32; 2],
8    pub target_size: [u32; 2],
9}
10
11#[repr(C)]
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct DebugPoint {
14    pub pos: [f32; 3],
15    pub color: u32,
16}
17
18#[repr(C)]
19#[derive(Clone, Copy, Debug, PartialEq)]
20pub struct DebugLine {
21    pub a: DebugPoint,
22    pub b: DebugPoint,
23}
24
25#[derive(blade_macros::ShaderData)]
26struct DebugDrawData {
27    camera: super::CameraParams,
28    debug_lines: blade_graphics::BufferPiece,
29    depth: blade_graphics::TextureView,
30}
31
32#[repr(C)]
33#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
34struct DebugBlitParams {
35    target_offset: [f32; 2],
36    target_size: [f32; 2],
37    mip_level: f32,
38    unused: u32,
39}
40
41#[derive(blade_macros::ShaderData)]
42struct DebugBlitData {
43    input: blade_graphics::TextureView,
44    samp: blade_graphics::Sampler,
45    params: DebugBlitParams,
46}
47
48// Has to match the shader!
49#[repr(C)]
50#[derive(Debug)]
51pub struct DebugVariance {
52    pub color_sum: [f32; 3],
53    pad: u32,
54    pub color2_sum: [f32; 3],
55    pub count: u32,
56}
57
58// Has to match the shader!
59#[repr(C)]
60#[derive(Debug)]
61pub struct DebugEntry {
62    pub custom_index: u32,
63    pub depth: f32,
64    pub tex_coords: [f32; 2],
65    pub base_color_texture: u32,
66    pub normal_texture: u32,
67    pad: [u32; 2],
68    pub position: [f32; 3],
69    position_w: f32,
70    pub normal: [f32; 3],
71    normal_w: f32,
72}
73
74fn create_draw_pipeline(
75    shader: &blade_graphics::Shader,
76    format: blade_graphics::TextureFormat,
77    gpu: &blade_graphics::Context,
78) -> blade_graphics::RenderPipeline {
79    shader.check_struct_size::<DebugPoint>();
80    shader.check_struct_size::<DebugLine>();
81    let layout = <DebugDrawData as blade_graphics::ShaderData>::layout();
82    gpu.create_render_pipeline(blade_graphics::RenderPipelineDesc {
83        name: "debug-draw",
84        data_layouts: &[&layout],
85        vertex: shader.at("debug_vs"),
86        vertex_fetches: &[],
87        primitive: blade_graphics::PrimitiveState {
88            topology: blade_graphics::PrimitiveTopology::LineList,
89            ..Default::default()
90        },
91        depth_stencil: None,
92        fragment: Some(shader.at("debug_fs")),
93        color_targets: &[blade_graphics::ColorTargetState {
94            format,
95            blend: Some(blade_graphics::BlendState::ALPHA_BLENDING),
96            write_mask: blade_graphics::ColorWrites::all(),
97        }],
98        multisample_state: blade_graphics::MultisampleState::default(),
99    })
100}
101
102fn create_blit_pipeline(
103    shader: &blade_graphics::Shader,
104    format: blade_graphics::TextureFormat,
105    gpu: &blade_graphics::Context,
106) -> blade_graphics::RenderPipeline {
107    shader.check_struct_size::<DebugBlitParams>();
108    let layout = <DebugBlitData as blade_graphics::ShaderData>::layout();
109    gpu.create_render_pipeline(blade_graphics::RenderPipelineDesc {
110        name: "debug-blit",
111        data_layouts: &[&layout],
112        vertex: shader.at("blit_vs"),
113        vertex_fetches: &[],
114        primitive: blade_graphics::PrimitiveState {
115            topology: blade_graphics::PrimitiveTopology::TriangleStrip,
116            ..Default::default()
117        },
118        depth_stencil: None,
119        fragment: Some(shader.at("blit_fs")),
120        color_targets: &[format.into()],
121        multisample_state: blade_graphics::MultisampleState::default(),
122    })
123}
124
125pub struct DebugRender {
126    capacity: u32,
127    surface_format: blade_graphics::TextureFormat,
128    buffer: blade_graphics::Buffer,
129    variance_buffer: blade_graphics::Buffer,
130    entry_buffer: blade_graphics::Buffer,
131    cpu_lines_buffer: blade_graphics::Buffer,
132    //Note: allows immutable `add_lines`
133    cpu_lines_offset: Cell<u64>,
134    draw_pipeline: blade_graphics::RenderPipeline,
135    blit_pipeline: blade_graphics::RenderPipeline,
136    line_size: u32,
137    buffer_size: u32,
138}
139
140impl DebugRender {
141    pub(super) fn init(
142        encoder: &mut blade_graphics::CommandEncoder,
143        gpu: &blade_graphics::Context,
144        shader_draw: &blade_graphics::Shader,
145        shader_blit: &blade_graphics::Shader,
146        capacity: u32,
147        surface_info: blade_graphics::SurfaceInfo,
148    ) -> Self {
149        let line_size = shader_draw.get_struct_size("DebugLine");
150        let buffer_size = shader_draw.get_struct_size("DebugBuffer");
151        let this = Self {
152            capacity,
153            surface_format: surface_info.format,
154            buffer: gpu.create_buffer(blade_graphics::BufferDesc {
155                name: "debug",
156                size: (buffer_size + capacity.saturating_sub(1) * line_size) as u64,
157                memory: blade_graphics::Memory::Device,
158            }),
159            variance_buffer: gpu.create_buffer(blade_graphics::BufferDesc {
160                name: "variance",
161                size: mem::size_of::<DebugVariance>() as u64,
162                memory: blade_graphics::Memory::Shared,
163            }),
164            entry_buffer: gpu.create_buffer(blade_graphics::BufferDesc {
165                name: "debug entry",
166                size: mem::size_of::<DebugEntry>() as u64,
167                memory: blade_graphics::Memory::Shared,
168            }),
169            cpu_lines_buffer: gpu.create_buffer(blade_graphics::BufferDesc {
170                name: "CPU debug lines",
171                size: (capacity * line_size) as u64,
172                memory: blade_graphics::Memory::Shared,
173            }),
174            cpu_lines_offset: Cell::new(0),
175            draw_pipeline: create_draw_pipeline(shader_draw, surface_info.format, gpu),
176            blit_pipeline: create_blit_pipeline(shader_blit, surface_info.format, gpu),
177            line_size,
178            buffer_size,
179        };
180
181        let init_data = [2u32, 0, 0, 0, capacity];
182        let init_size = init_data.len() * mem::size_of::<u32>();
183        assert!(init_size <= mem::size_of::<DebugEntry>());
184        unsafe {
185            ptr::write_bytes(
186                this.variance_buffer.data(),
187                0,
188                mem::size_of::<DebugVariance>(),
189            );
190            ptr::write_bytes(this.entry_buffer.data(), 0, mem::size_of::<DebugEntry>());
191            // piggyback on the staging buffers to upload the data
192            ptr::copy_nonoverlapping(
193                init_data.as_ptr(),
194                this.entry_buffer.data() as *mut u32,
195                init_data.len(),
196            );
197        }
198
199        let mut transfers = encoder.transfer("upload debug");
200        transfers.copy_buffer_to_buffer(
201            this.entry_buffer.at(0),
202            this.buffer.at(0),
203            init_size as u64,
204        );
205
206        this
207    }
208
209    pub(super) fn destroy(&mut self, gpu: &blade_graphics::Context) {
210        gpu.destroy_buffer(self.buffer);
211        gpu.destroy_buffer(self.variance_buffer);
212        gpu.destroy_buffer(self.entry_buffer);
213        gpu.destroy_buffer(self.cpu_lines_buffer);
214        gpu.destroy_render_pipeline(&mut self.draw_pipeline);
215        gpu.destroy_render_pipeline(&mut self.blit_pipeline);
216    }
217
218    pub(super) fn recreate_draw_pipeline(
219        &mut self,
220        shader: &blade_graphics::Shader,
221        gpu: &blade_graphics::Context,
222    ) {
223        assert_eq!(shader.get_struct_size("DebugLine"), self.line_size);
224        assert_eq!(shader.get_struct_size("DebugBuffer"), self.buffer_size);
225        self.draw_pipeline = create_draw_pipeline(shader, self.surface_format, gpu);
226    }
227
228    pub(super) fn recreate_blit_pipeline(
229        &mut self,
230        shader: &blade_graphics::Shader,
231        gpu: &blade_graphics::Context,
232    ) {
233        self.draw_pipeline = create_blit_pipeline(shader, self.surface_format, gpu);
234    }
235
236    fn add_lines(&self, lines: &[DebugLine]) -> (blade_graphics::BufferPiece, u32) {
237        let required_size = lines.len() as u64 * self.line_size as u64;
238        let old_offset = self.cpu_lines_offset.get();
239        let (original_offset, count) =
240            if old_offset + required_size <= (self.capacity * self.line_size) as u64 {
241                (old_offset, lines.len())
242            } else {
243                let count = lines.len().min(self.capacity as usize);
244                if count < lines.len() {
245                    log::warn!("Reducing the debug lines from {} to {}", lines.len(), count);
246                }
247                (0, count)
248            };
249
250        unsafe {
251            ptr::copy_nonoverlapping(
252                lines.as_ptr(),
253                self.cpu_lines_buffer.data().add(original_offset as usize) as *mut DebugLine,
254                count,
255            );
256        }
257
258        self.cpu_lines_offset
259            .set(original_offset + count as u64 * self.line_size as u64);
260        (self.cpu_lines_buffer.at(original_offset), count as u32)
261    }
262
263    pub(super) fn render_lines(
264        &self,
265        debug_lines: &[DebugLine],
266        camera: super::CameraParams,
267        depth: blade_graphics::TextureView,
268        pass: &mut blade_graphics::RenderCommandEncoder,
269    ) {
270        let mut pc = pass.with(&self.draw_pipeline);
271        let lines_offset = 32 + mem::size_of::<DebugVariance>() + mem::size_of::<DebugEntry>();
272        pc.bind(
273            0,
274            &DebugDrawData {
275                camera,
276                debug_lines: self.buffer.at(lines_offset as u64),
277                depth,
278            },
279        );
280        pc.draw_indirect(self.buffer.at(0));
281
282        if !debug_lines.is_empty() {
283            let (lines_buf, count) = self.add_lines(debug_lines);
284            pc.bind(
285                0,
286                &DebugDrawData {
287                    camera,
288                    debug_lines: lines_buf,
289                    depth,
290                },
291            );
292            pc.draw(0, 2, 0, count);
293        }
294    }
295
296    pub(super) fn render_blits(
297        &self,
298        debug_blits: &[DebugBlit],
299        samp: blade_graphics::Sampler,
300        screen_size: blade_graphics::Extent,
301        pass: &mut blade_graphics::RenderCommandEncoder,
302    ) {
303        let mut pc = pass.with(&self.blit_pipeline);
304        for db in debug_blits {
305            pc.bind(
306                0,
307                &DebugBlitData {
308                    input: db.input,
309                    samp,
310                    params: DebugBlitParams {
311                        target_offset: [
312                            db.target_offset[0] as f32 / screen_size.width as f32,
313                            db.target_offset[1] as f32 / screen_size.height as f32,
314                        ],
315                        target_size: [
316                            db.target_size[0] as f32 / screen_size.width as f32,
317                            db.target_size[1] as f32 / screen_size.height as f32,
318                        ],
319                        mip_level: db.mip_level as f32,
320                        unused: 0,
321                    },
322                },
323            );
324            pc.draw(0, 4, 0, 1);
325        }
326    }
327
328    pub fn buffer_resource(&self) -> blade_graphics::BufferPiece {
329        self.buffer.into()
330    }
331
332    pub fn enable_draw(&self, transfer: &mut blade_graphics::TransferCommandEncoder, enable: bool) {
333        transfer.fill_buffer(self.buffer.at(20), 4, enable as _);
334    }
335
336    pub fn reset_lines(&self, transfer: &mut blade_graphics::TransferCommandEncoder) {
337        transfer.fill_buffer(self.buffer.at(4), 4, 0);
338    }
339
340    pub fn reset_variance(&self, transfer: &mut blade_graphics::TransferCommandEncoder) {
341        transfer.fill_buffer(
342            self.buffer.at(32),
343            mem::size_of::<DebugVariance>() as u64,
344            0,
345        );
346    }
347
348    /// Copy the previous frame variance into the CPU-shared buffer.
349    pub fn update_variance(&self, transfer: &mut blade_graphics::TransferCommandEncoder) {
350        transfer.copy_buffer_to_buffer(
351            self.buffer.at(32),
352            self.variance_buffer.into(),
353            mem::size_of::<DebugVariance>() as u64,
354        );
355    }
356
357    /// Copy the previous frame entry into the CPU-shared buffer.
358    pub fn update_entry(&self, transfer: &mut blade_graphics::TransferCommandEncoder) {
359        transfer.copy_buffer_to_buffer(
360            self.buffer.at(32 + mem::size_of::<DebugVariance>() as u64),
361            self.entry_buffer.into(),
362            mem::size_of::<DebugEntry>() as u64,
363        );
364    }
365
366    pub fn read_shared_data(&self) -> (&DebugVariance, &DebugEntry) {
367        let db_v = unsafe { &*(self.variance_buffer.data() as *const DebugVariance) };
368        let db_e = unsafe { &*(self.entry_buffer.data() as *const DebugEntry) };
369        (db_v, db_e)
370    }
371}