debug_overlay/
lib.rs

1//! A basic low-overhead debugging overlay for use with GPU APIs such as `wgpu`.
2//!
3//! # Features
4//!
5//! Enable one or several or the builtin runderers using the following cargo features:
6//! - `wgpu`
7//! - `wgpu-core` (TODO)
8//!
9
10mod counter;
11pub mod embedded_font;
12mod graph;
13mod table;
14#[cfg(feature = "wgpu")]
15pub mod wgpu;
16#[cfg(feature = "wgpu-core")]
17pub mod wgpu_core;
18#[cfg(any(feature = "wgpu", feature = "wgpu-core"))]
19mod wgpu_common;
20
21use bytemuck::{Pod, Zeroable};
22use embedded_font::*;
23
24pub use counter::*;
25pub use graph::*;
26pub use table::*;
27
28pub const BACKGROUND_LAYER: Layer = 0;
29pub const FRONT_LAYER: Layer = 1;
30
31/// A 2D position (in pixels).
32#[derive(Copy, Clone, Debug, PartialEq)]
33pub struct Point {
34    pub x: i32,
35    pub y: i32,
36}
37
38impl From<(f32, f32)> for Point {
39    fn from(val: (f32, f32)) -> Self {
40        Point {
41            x: val.0 as i32,
42            y: val.1 as i32,
43        }
44    }
45}
46
47impl From<(i32, i32)> for Point {
48    fn from(val: (i32, i32)) -> Self {
49        Point { x: val.0, y: val.1 }
50    }
51}
52
53/// A 2D position (in pixels).
54#[derive(Copy, Clone, Debug, PartialEq)]
55pub struct PointF {
56    pub x: f32,
57    pub y: f32,
58}
59
60/// An 8-bit per channel RGBA color value.
61pub type Color = (u8, u8, u8, u8);
62/// The index of an overlay layer.
63pub type Layer = usize;
64
65fn color_to_u32(color: Color) -> u32 {
66    (color.0 as u32) << 24 | (color.1 as u32) << 16 | (color.2 as u32) << 8 | color.3 as u32
67}
68
69#[repr(C)]
70#[derive(Copy, Clone, Debug)]
71pub struct Vertex {
72    pub x: f32,
73    pub y: f32,
74    pub uv: u32,
75    pub color: u32,
76}
77
78unsafe impl Pod for Vertex {}
79unsafe impl Zeroable for Vertex {}
80
81pub(crate) struct LayerGeometry {
82    pub indices: Vec<u16>,
83}
84
85pub struct OverlayGeometry {
86    vertices: Vec<Vertex>,
87    layers: Vec<LayerGeometry>,
88}
89
90impl OverlayGeometry {
91    pub fn new(layer_count: usize) -> Self {
92        let mut layers = Vec::new();
93        for _ in 0..layer_count {
94            layers.push(LayerGeometry {
95                indices: Vec::new(),
96            });
97        }
98        OverlayGeometry {
99            vertices: Vec::new(),
100            layers,
101        }
102    }
103
104    pub fn begin_frame(&mut self) {
105        self.vertices.clear();
106        for layer in &mut self.layers {
107            layer.indices.clear();
108        }
109    }
110
111    pub fn push_text(
112        &mut self,
113        layer: Layer,
114        text: &str,
115        mut position: Point,
116        color: Color,
117    ) -> (Point, Point) {
118        let color = color_to_u32(color);
119        let mut min = position;
120        let mut max = min;
121
122        for c in text.chars() {
123            if c == '\n' {
124                position.x = min.x;
125                position.y += FONT_HEIGHT as i32;
126                continue;
127            }
128
129            let idx = c as usize - FIRST_CHAR as usize;
130            if idx >= GLYPH_INFO.len() {
131                continue;
132            }
133            let glyph = &GLYPH_INFO[idx];
134
135            let uv0x = (glyph.uv0.0 as u32) << 16;
136            let uv0y = glyph.uv0.1 as u32;
137            let uv1x = (glyph.uv1.0 as u32) << 16;
138            let uv1y = glyph.uv1.1 as u32;
139
140            let x0 = position.x + glyph.offset.0 as i32;
141            let y0 = position.y + glyph.offset.1 as i32;
142            let x1 = x0 + (glyph.uv1.0 - glyph.uv0.0) as i32;
143            let y1 = y0 + (glyph.uv1.1 - glyph.uv0.1) as i32;
144
145            let offset = self.vertices.len() as u16;
146            self.vertices.push(Vertex {
147                x: x0 as f32,
148                y: y0 as f32,
149                uv: uv0x | uv0y,
150                color,
151            });
152            self.vertices.push(Vertex {
153                x: x1 as f32,
154                y: y0 as f32,
155                uv: uv1x | uv0y,
156                color,
157            });
158            self.vertices.push(Vertex {
159                x: x1 as f32,
160                y: y1 as f32,
161                uv: uv1x | uv1y,
162                color,
163            });
164            self.vertices.push(Vertex {
165                x: x0 as f32,
166                y: y1 as f32,
167                uv: uv0x | uv1y,
168                color,
169            });
170            let layer = &mut self.layers[layer];
171            for i in [0u16, 1, 2, 0, 2, 3] {
172                layer.indices.push(offset + i);
173            }
174
175            position.x += glyph.x_advance as i32;
176
177            min.x = min.x.min(x0);
178            min.y = min.y.min(y0);
179            max.x = max.x.max(x1);
180            max.y = max.y.max(y1);
181        }
182
183        (min, max)
184    }
185
186    pub fn push_rectangle(
187        &mut self,
188        layer: Layer,
189        rect: &(Point, Point),
190        color0: Color,
191        color1: Color,
192    ) {
193        let uv = (OPAQUE_PIXEL.0 as u32) << 16 | OPAQUE_PIXEL.1 as u32;
194        let x0 = rect.0.x;
195        let y0 = rect.0.y;
196        let x1 = rect.1.x;
197        let y1 = rect.1.y;
198        let color0 = color_to_u32(color0);
199        let color1 = color_to_u32(color1);
200
201        let offset = self.vertices.len() as u16;
202        self.vertices.push(Vertex {
203            x: x0 as f32,
204            y: y0 as f32,
205            uv,
206            color: color0,
207        });
208        self.vertices.push(Vertex {
209            x: x1 as f32,
210            y: y0 as f32,
211            uv,
212            color: color0,
213        });
214        self.vertices.push(Vertex {
215            x: x1 as f32,
216            y: y1 as f32,
217            uv,
218            color: color1,
219        });
220        self.vertices.push(Vertex {
221            x: x0 as f32,
222            y: y1 as f32,
223            uv,
224            color: color1,
225        });
226        let layer = &mut self.layers[layer];
227        for i in [0u16, 1, 2, 0, 2, 3] {
228            layer.indices.push(offset + i);
229        }
230    }
231
232    pub fn push_mesh(&mut self, layer: Layer, vertices: &[PointF], indices: &[u16], color: Color) {
233        let uv = (OPAQUE_PIXEL.0 as u32) << 16 | OPAQUE_PIXEL.1 as u32;
234        let layer = &mut self.layers[layer];
235        self.vertices.reserve(vertices.len());
236        layer.indices.reserve(indices.len());
237        let offset = self.vertices.len() as u16;
238        let color = color_to_u32(color);
239        for vertex in vertices {
240            self.vertices.push(Vertex {
241                x: vertex.x,
242                y: vertex.y,
243                uv,
244                color,
245            });
246        }
247        for idx in indices {
248            layer.indices.push(offset + *idx);
249        }
250    }
251}
252
253pub struct Overlay {
254    pub geometry: OverlayGeometry,
255    pub style: Style,
256    pub cursor: Point,
257    pub item_flow: Orientation,
258    pub group_flow: Orientation,
259    pub string_buffer: String,
260    group_area: (Point, Point),
261    in_group: bool,
262    max_x: i32,
263    max_y: i32,
264}
265
266impl Overlay {
267    pub fn new() -> Self {
268        let style = Style::default();
269        let cursor = Point {
270            x: style.margin,
271            y: style.margin,
272        };
273        Overlay {
274            geometry: OverlayGeometry::new(2),
275            style,
276            cursor,
277            item_flow: Orientation::Horizontal,
278            group_flow: Orientation::Vertical,
279            string_buffer: String::with_capacity(128),
280            group_area: (cursor, cursor),
281            in_group: false,
282            max_x: 0,
283            max_y: 0,
284        }
285    }
286
287    pub fn begin_frame(&mut self) {
288        self.geometry.begin_frame();
289
290        self.cursor = Point {
291            x: self.style.margin,
292            y: self.style.margin,
293        };
294        self.group_area = (self.cursor, self.cursor);
295        self.max_x = 0;
296        self.max_y = 0;
297        self.in_group = false;
298    }
299
300    pub fn current_group_width(&self) -> i32 {
301        self.group_area.1.x - self.group_area.0.x
302    }
303
304    pub fn current_group_height(&self) -> i32 {
305        self.group_area.1.y - self.group_area.0.y
306    }
307
308    pub fn draw_item(&mut self, item: &dyn OverlayItem) {
309        let first = !self.in_group;
310        if !self.in_group {
311            self.begin_group();
312        }
313
314        let margin = if first { 0 } else { self.style.margin };
315        self.cursor = match self.item_flow {
316            Orientation::Vertical => Point {
317                x: self.group_area.0.x,
318                y: self.group_area.1.y + margin,
319            },
320            Orientation::Horizontal => Point {
321                x: self.group_area.1.x + margin,
322                y: self.group_area.0.y,
323            },
324        };
325
326        let rect = item.draw(self.cursor, self);
327
328        self.group_area.0.x = self.group_area.0.x.min(rect.0.x);
329        self.group_area.0.y = self.group_area.0.y.min(rect.0.y);
330        self.group_area.1.x = self.group_area.1.x.max(rect.1.x);
331        self.group_area.1.y = self.group_area.1.y.max(rect.1.y);
332    }
333
334    pub fn push_separator(&mut self) {
335        if !self.in_group {
336            return;
337        }
338
339        match self.item_flow {
340            Orientation::Vertical => {
341                self.cursor.y += self.style.margin * 3;
342            }
343            Orientation::Horizontal => {
344                self.cursor.x += self.style.margin * 3;
345            }
346        }
347    }
348
349    pub fn push_column(&mut self) {
350        if self.in_group {
351            self.end_group();
352        }
353
354        let p = Point {
355            x: self.max_x + self.style.margin * 3,
356            y: self.style.margin,
357        };
358
359        self.group_area = (p, p);
360    }
361
362    fn begin_group(&mut self) {
363        match self.group_flow {
364            Orientation::Vertical => {
365                let margin = if self.group_area.1.y > self.style.margin {
366                    self.style.margin * 3
367                } else {
368                    0
369                };
370                self.cursor.x = self.group_area.0.x;
371                self.cursor.y = self.group_area.1.y + margin;
372            }
373            Orientation::Horizontal => {
374                let margin = if self.group_area.1.x > self.style.margin {
375                    self.style.margin * 3
376                } else {
377                    0
378                };
379                self.cursor.x = self.group_area.1.x + margin;
380                self.cursor.y = self.group_area.0.y;
381            }
382        }
383
384        self.group_area = (self.cursor, self.cursor);
385        self.in_group = true;
386    }
387
388    pub fn end_group(&mut self) {
389        self.in_group = false;
390        if self.group_area.0.x >= self.group_area.1.x || self.group_area.0.y >= self.group_area.1.y
391        {
392            return;
393        }
394
395        self.group_area.1.x = self
396            .group_area
397            .1
398            .x
399            .max(self.group_area.0.x + self.style.min_group_width);
400        self.group_area.1.y = self
401            .group_area
402            .1
403            .y
404            .max(self.group_area.0.y + self.style.min_group_height);
405
406        self.max_x = self.max_x.max(self.group_area.1.x);
407        self.max_y = self.max_y.max(self.group_area.1.y);
408
409        let margin = self.style.margin;
410        let mut bg = self.group_area;
411        bg.0.x -= margin;
412        bg.0.y -= margin;
413        bg.1.x += margin;
414        bg.1.y += margin;
415
416        self.geometry.push_rectangle(
417            BACKGROUND_LAYER,
418            &bg,
419            self.style.background[0],
420            self.style.background[1],
421        );
422    }
423
424    pub fn finish(&mut self) {
425        if self.in_group {
426            self.end_group();
427        }
428    }
429}
430
431pub trait OverlayItem {
432    fn draw(&self, position: Point, output: &mut Overlay) -> (Point, Point);
433}
434
435impl<'a> OverlayItem for &'a str {
436    fn draw(&self, position: Point, output: &mut Overlay) -> (Point, Point) {
437        let p = Point {
438            x: position.x,
439            y: position.y + FONT_HEIGHT as i32,
440        };
441
442        output
443            .geometry
444            .push_text(FRONT_LAYER, self, p, output.style.text_color[0])
445    }
446}
447
448#[derive(Copy, Clone, Debug, PartialEq)]
449pub struct Style {
450    pub margin: i32,
451    pub line_spacing: i32,
452    pub min_group_width: i32,
453    pub min_group_height: i32,
454    pub column_spacing: i32,
455    pub background: [Color; 2],
456    pub text_color: [Color; 2],
457    pub title_color: Color,
458    pub highlight_color: Color,
459}
460
461impl Default for Style {
462    fn default() -> Self {
463        Style {
464            margin: 10,
465            line_spacing: 2,
466            min_group_width: 0,
467            min_group_height: 0,
468            column_spacing: 20,
469            background: [(0, 0, 0, 255), (0, 0, 0, 200)],
470            text_color: [(255, 255, 255, 255), (200, 200, 200, 255)],
471            title_color: (120, 150, 255, 255),
472            highlight_color: (255, 100, 100, 255),
473        }
474    }
475}