webrender/renderer/
debug.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use api::{ColorU, ImageFormat, ImageBufferKind};
6use api::units::*;
7use crate::debug_font_data;
8use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO};
9use crate::device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint};
10use euclid::{Point2D, Rect, Size2D, Transform3D, default};
11use crate::internal_types::Swizzle;
12use std::f32;
13
14#[derive(Debug, Copy, Clone)]
15enum DebugSampler {
16    Font,
17}
18
19impl Into<TextureSlot> for DebugSampler {
20    fn into(self) -> TextureSlot {
21        match self {
22            DebugSampler::Font => TextureSlot(0),
23        }
24    }
25}
26
27const DESC_FONT: VertexDescriptor = VertexDescriptor {
28    vertex_attributes: &[
29        VertexAttribute {
30            name: "aPosition",
31            count: 2,
32            kind: VertexAttributeKind::F32,
33        },
34        VertexAttribute {
35            name: "aColor",
36            count: 4,
37            kind: VertexAttributeKind::U8Norm,
38        },
39        VertexAttribute {
40            name: "aColorTexCoord",
41            count: 2,
42            kind: VertexAttributeKind::F32,
43        },
44    ],
45    instance_attributes: &[],
46};
47
48const DESC_COLOR: VertexDescriptor = VertexDescriptor {
49    vertex_attributes: &[
50        VertexAttribute {
51            name: "aPosition",
52            count: 2,
53            kind: VertexAttributeKind::F32,
54        },
55        VertexAttribute {
56            name: "aColor",
57            count: 4,
58            kind: VertexAttributeKind::U8Norm,
59        },
60    ],
61    instance_attributes: &[],
62};
63
64#[repr(C)]
65pub struct DebugFontVertex {
66    pub x: f32,
67    pub y: f32,
68    pub color: ColorU,
69    pub u: f32,
70    pub v: f32,
71}
72
73impl DebugFontVertex {
74    pub fn new(x: f32, y: f32, u: f32, v: f32, color: ColorU) -> DebugFontVertex {
75        DebugFontVertex { x, y, color, u, v }
76    }
77}
78
79#[repr(C)]
80pub struct DebugColorVertex {
81    pub x: f32,
82    pub y: f32,
83    pub color: ColorU,
84}
85
86impl DebugColorVertex {
87    pub fn new(x: f32, y: f32, color: ColorU) -> DebugColorVertex {
88        DebugColorVertex { x, y, color }
89    }
90}
91
92pub struct DebugRenderer {
93    font_vertices: Vec<DebugFontVertex>,
94    font_indices: Vec<u32>,
95    font_program: Program,
96    font_vao: VAO,
97    font_texture: Texture,
98
99    tri_vertices: Vec<DebugColorVertex>,
100    tri_indices: Vec<u32>,
101    tri_vao: VAO,
102    line_vertices: Vec<DebugColorVertex>,
103    line_vao: VAO,
104    color_program: Program,
105}
106
107impl DebugRenderer {
108    pub fn new(device: &mut Device) -> Result<Self, ShaderError> {
109        let font_program = device.create_program_linked(
110            "debug_font",
111            &[],
112            &DESC_FONT,
113        )?;
114        device.bind_program(&font_program);
115        device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
116
117        let color_program = device.create_program_linked(
118            "debug_color",
119            &[],
120            &DESC_COLOR,
121        )?;
122
123        let font_vao = device.create_vao(&DESC_FONT, 1);
124        let line_vao = device.create_vao(&DESC_COLOR, 1);
125        let tri_vao = device.create_vao(&DESC_COLOR, 1);
126
127        let font_texture = device.create_texture(
128            ImageBufferKind::Texture2D,
129            ImageFormat::R8,
130            debug_font_data::BMP_WIDTH,
131            debug_font_data::BMP_HEIGHT,
132            TextureFilter::Linear,
133            None,
134        );
135        device.upload_texture_immediate(
136            &font_texture,
137            &debug_font_data::FONT_BITMAP
138        );
139
140        Ok(DebugRenderer {
141            font_vertices: Vec::new(),
142            font_indices: Vec::new(),
143            line_vertices: Vec::new(),
144            tri_vao,
145            tri_vertices: Vec::new(),
146            tri_indices: Vec::new(),
147            font_program,
148            color_program,
149            font_vao,
150            line_vao,
151            font_texture,
152        })
153    }
154
155    pub fn deinit(self, device: &mut Device) {
156        device.delete_texture(self.font_texture);
157        device.delete_program(self.font_program);
158        device.delete_program(self.color_program);
159        device.delete_vao(self.tri_vao);
160        device.delete_vao(self.line_vao);
161        device.delete_vao(self.font_vao);
162    }
163
164    pub fn line_height(&self) -> f32 {
165        debug_font_data::FONT_SIZE as f32 * 1.1
166    }
167
168    /// Draws a line of text at the provided starting coordinates.
169    ///
170    /// If |bounds| is specified, glyphs outside the bounds are discarded.
171    ///
172    /// Y-coordinates is relative to screen top, along with everything else in
173    /// this file.
174    pub fn add_text(
175        &mut self,
176        x: f32,
177        y: f32,
178        text: &str,
179        color: ColorU,
180        bounds: Option<DeviceRect>,
181    ) -> default::Rect<f32> {
182        let mut x_start = x;
183        let ipw = 1.0 / debug_font_data::BMP_WIDTH as f32;
184        let iph = 1.0 / debug_font_data::BMP_HEIGHT as f32;
185
186        let mut min_x = f32::MAX;
187        let mut max_x = -f32::MAX;
188        let mut min_y = f32::MAX;
189        let mut max_y = -f32::MAX;
190
191        for c in text.chars() {
192            let c = c as usize - debug_font_data::FIRST_GLYPH_INDEX as usize;
193            if c < debug_font_data::GLYPHS.len() {
194                let glyph = &debug_font_data::GLYPHS[c];
195
196                let x0 = (x_start + glyph.xo + 0.5).floor();
197                let y0 = (y + glyph.yo + 0.5).floor();
198
199                let x1 = x0 + glyph.x1 as f32 - glyph.x0 as f32;
200                let y1 = y0 + glyph.y1 as f32 - glyph.y0 as f32;
201
202                // If either corner of the glyph will end up out of bounds, drop it.
203                if let Some(b) = bounds {
204                    let rect = DeviceRect {
205                        min: DevicePoint::new(x0, y0),
206                        max: DevicePoint::new(x1, y1),
207                    };
208                    if !b.contains_box(&rect) {
209                        continue;
210                    }
211                }
212
213                let s0 = glyph.x0 as f32 * ipw;
214                let t0 = glyph.y0 as f32 * iph;
215                let s1 = glyph.x1 as f32 * ipw;
216                let t1 = glyph.y1 as f32 * iph;
217
218                x_start += glyph.xa;
219
220                let vertex_count = self.font_vertices.len() as u32;
221
222                self.font_vertices
223                    .push(DebugFontVertex::new(x0, y0, s0, t0, color));
224                self.font_vertices
225                    .push(DebugFontVertex::new(x1, y0, s1, t0, color));
226                self.font_vertices
227                    .push(DebugFontVertex::new(x0, y1, s0, t1, color));
228                self.font_vertices
229                    .push(DebugFontVertex::new(x1, y1, s1, t1, color));
230
231                self.font_indices.push(vertex_count + 0);
232                self.font_indices.push(vertex_count + 1);
233                self.font_indices.push(vertex_count + 2);
234                self.font_indices.push(vertex_count + 2);
235                self.font_indices.push(vertex_count + 1);
236                self.font_indices.push(vertex_count + 3);
237
238                min_x = min_x.min(x0);
239                max_x = max_x.max(x1);
240                min_y = min_y.min(y0);
241                max_y = max_y.max(y1);
242            }
243        }
244
245        Rect::new(
246            Point2D::new(min_x, min_y),
247            Size2D::new(max_x - min_x, max_y - min_y),
248        )
249    }
250
251    pub fn add_quad(
252        &mut self,
253        x0: f32,
254        y0: f32,
255        x1: f32,
256        y1: f32,
257        color_top: ColorU,
258        color_bottom: ColorU,
259    ) {
260        let vertex_count = self.tri_vertices.len() as u32;
261
262        self.tri_vertices
263            .push(DebugColorVertex::new(x0, y0, color_top));
264        self.tri_vertices
265            .push(DebugColorVertex::new(x1, y0, color_top));
266        self.tri_vertices
267            .push(DebugColorVertex::new(x0, y1, color_bottom));
268        self.tri_vertices
269            .push(DebugColorVertex::new(x1, y1, color_bottom));
270
271        self.tri_indices.push(vertex_count + 0);
272        self.tri_indices.push(vertex_count + 1);
273        self.tri_indices.push(vertex_count + 2);
274        self.tri_indices.push(vertex_count + 2);
275        self.tri_indices.push(vertex_count + 1);
276        self.tri_indices.push(vertex_count + 3);
277    }
278
279    #[allow(dead_code)]
280    pub fn add_line(&mut self, x0: i32, y0: i32, color0: ColorU, x1: i32, y1: i32, color1: ColorU) {
281        self.line_vertices
282            .push(DebugColorVertex::new(x0 as f32, y0 as f32, color0));
283        self.line_vertices
284            .push(DebugColorVertex::new(x1 as f32, y1 as f32, color1));
285    }
286
287
288    pub fn add_rect(&mut self, rect: &DeviceIntRect, thickness: i32, color: ColorU) {
289        let p0 = rect.min;
290        let p1 = rect.max;
291        if thickness > 1 && rect.width() > thickness * 2 && rect.height() > thickness * 2 {
292            let w = thickness as f32;
293            let p0 = p0.to_f32();
294            let p1 = p1.to_f32();
295            self.add_quad(p0.x, p0.y, p1.x, p0.y + w, color, color);
296            self.add_quad(p1.x - w, p0.y + w, p1.x, p1.y - w, color, color);
297            self.add_quad(p0.x, p1.y - w, p1.x, p1.y, color, color);
298            self.add_quad(p0.x, p0.y + w, p0.x + w, p1.y - w, color, color);
299        } else {
300            self.add_line(p0.x, p0.y, color, p1.x, p0.y, color);
301            self.add_line(p1.x, p0.y, color, p1.x, p1.y, color);
302            self.add_line(p1.x, p1.y, color, p0.x, p1.y, color);
303            self.add_line(p0.x, p1.y, color, p0.x, p0.y, color);    
304        }
305    }
306
307    pub fn render(
308        &mut self,
309        device: &mut Device,
310        viewport_size: Option<DeviceIntSize>,
311        scale: f32,
312        surface_origin_is_top_left: bool,
313    ) {
314        if let Some(viewport_size) = viewport_size {
315            device.disable_depth();
316            device.set_blend(true);
317            device.set_blend_mode_premultiplied_alpha();
318
319            let (bottom, top) = if surface_origin_is_top_left {
320                (0.0, viewport_size.height as f32 * scale)
321            } else {
322                (viewport_size.height as f32 * scale, 0.0)
323            };
324
325            let projection = Transform3D::ortho(
326                0.0,
327                viewport_size.width as f32 * scale,
328                bottom,
329                top,
330                device.ortho_near_plane(),
331                device.ortho_far_plane(),
332            );
333
334            // Triangles
335            if !self.tri_vertices.is_empty() {
336                device.bind_program(&self.color_program);
337                device.set_uniforms(&self.color_program, &projection);
338                device.bind_vao(&self.tri_vao);
339                device.update_vao_indices(&self.tri_vao, &self.tri_indices, VertexUsageHint::Dynamic);
340                device.update_vao_main_vertices(
341                    &self.tri_vao,
342                    &self.tri_vertices,
343                    VertexUsageHint::Dynamic,
344                );
345                device.draw_triangles_u32(0, self.tri_indices.len() as i32);
346            }
347
348            // Lines
349            if !self.line_vertices.is_empty() {
350                device.bind_program(&self.color_program);
351                device.set_uniforms(&self.color_program, &projection);
352                device.bind_vao(&self.line_vao);
353                device.update_vao_main_vertices(
354                    &self.line_vao,
355                    &self.line_vertices,
356                    VertexUsageHint::Dynamic,
357                );
358                device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
359            }
360
361            // Glyph
362            if !self.font_indices.is_empty() {
363                device.bind_program(&self.font_program);
364                device.set_uniforms(&self.font_program, &projection);
365                device.bind_texture(DebugSampler::Font, &self.font_texture, Swizzle::default());
366                device.bind_vao(&self.font_vao);
367                device.update_vao_indices(&self.font_vao, &self.font_indices, VertexUsageHint::Dynamic);
368                device.update_vao_main_vertices(
369                    &self.font_vao,
370                    &self.font_vertices,
371                    VertexUsageHint::Dynamic,
372                );
373                device.draw_triangles_u32(0, self.font_indices.len() as i32);
374            }
375        }
376
377        self.font_indices.clear();
378        self.font_vertices.clear();
379        self.line_vertices.clear();
380        self.tri_vertices.clear();
381        self.tri_indices.clear();
382    }
383}
384
385pub struct LazyInitializedDebugRenderer {
386    debug_renderer: Option<DebugRenderer>,
387    failed: bool,
388}
389
390impl LazyInitializedDebugRenderer {
391    pub fn new() -> Self {
392        Self {
393            debug_renderer: None,
394            failed: false,
395        }
396    }
397
398    pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> {
399        if self.failed {
400            return None;
401        }
402        if self.debug_renderer.is_none() {
403            match DebugRenderer::new(device) {
404                Ok(renderer) => { self.debug_renderer = Some(renderer); }
405                Err(_) => {
406                    // The shader compilation code already logs errors.
407                    self.failed = true;
408                }
409            }
410        }
411
412        self.debug_renderer.as_mut()
413    }
414
415    /// Returns mut ref to `debug::DebugRenderer` if one already exists, otherwise returns `None`.
416    pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> {
417        self.debug_renderer.as_mut()
418    }
419
420    pub fn deinit(self, device: &mut Device) {
421        if let Some(debug_renderer) = self.debug_renderer {
422            debug_renderer.deinit(device);
423        }
424    }
425}