debug.rs (13785B)
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 5 use api::{ColorU, ImageFormat, ImageBufferKind}; 6 use api::units::*; 7 use crate::debug_font_data; 8 use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO}; 9 use crate::device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint}; 10 use euclid::{Point2D, Rect, Size2D, Transform3D, default}; 11 use crate::internal_types::Swizzle; 12 use std::f32; 13 14 #[derive(Debug, Copy, Clone)] 15 enum DebugSampler { 16 Font, 17 } 18 19 impl Into<TextureSlot> for DebugSampler { 20 fn into(self) -> TextureSlot { 21 match self { 22 DebugSampler::Font => TextureSlot(0), 23 } 24 } 25 } 26 27 const 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 48 const 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)] 65 pub struct DebugFontVertex { 66 pub x: f32, 67 pub y: f32, 68 pub color: ColorU, 69 pub u: f32, 70 pub v: f32, 71 } 72 73 impl 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)] 80 pub struct DebugColorVertex { 81 pub x: f32, 82 pub y: f32, 83 pub color: ColorU, 84 } 85 86 impl DebugColorVertex { 87 pub fn new(x: f32, y: f32, color: ColorU) -> DebugColorVertex { 88 DebugColorVertex { x, y, color } 89 } 90 } 91 92 pub 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 107 impl 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 385 pub struct LazyInitializedDebugRenderer { 386 debug_renderer: Option<DebugRenderer>, 387 failed: bool, 388 } 389 390 impl 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 }