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 { into(self) -> TextureSlot20 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 { new(x: f32, y: f32, u: f32, v: f32, color: ColorU) -> DebugFontVertex74 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 { new(x: f32, y: f32, color: ColorU) -> DebugColorVertex87 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 { new(device: &mut Device) -> Result<Self, ShaderError>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 deinit(self, device: &mut Device)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 line_height(&self) -> f32164 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. add_text( &mut self, x: f32, y: f32, text: &str, color: ColorU, bounds: Option<DeviceRect>, ) -> default::Rect<f32>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 add_quad( &mut self, x0: f32, y0: f32, x1: f32, y1: f32, color_top: ColorU, color_bottom: ColorU, )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)] add_line(&mut self, x0: i32, y0: i32, color0: ColorU, x1: i32, y1: i32, color1: ColorU)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 add_rect(&mut self, rect: &DeviceIntRect, color: ColorU)288 pub fn add_rect(&mut self, rect: &DeviceIntRect, color: ColorU) { 289 let p0 = rect.min; 290 let p1 = rect.max; 291 self.add_line(p0.x, p0.y, color, p1.x, p0.y, color); 292 self.add_line(p1.x, p0.y, color, p1.x, p1.y, color); 293 self.add_line(p1.x, p1.y, color, p0.x, p1.y, color); 294 self.add_line(p0.x, p1.y, color, p0.x, p0.y, color); 295 } 296 render( &mut self, device: &mut Device, viewport_size: Option<DeviceIntSize>, scale: f32, surface_origin_is_top_left: bool, )297 pub fn render( 298 &mut self, 299 device: &mut Device, 300 viewport_size: Option<DeviceIntSize>, 301 scale: f32, 302 surface_origin_is_top_left: bool, 303 ) { 304 if let Some(viewport_size) = viewport_size { 305 device.disable_depth(); 306 device.set_blend(true); 307 device.set_blend_mode_premultiplied_alpha(); 308 309 let (bottom, top) = if surface_origin_is_top_left { 310 (0.0, viewport_size.height as f32 * scale) 311 } else { 312 (viewport_size.height as f32 * scale, 0.0) 313 }; 314 315 let projection = Transform3D::ortho( 316 0.0, 317 viewport_size.width as f32 * scale, 318 bottom, 319 top, 320 device.ortho_near_plane(), 321 device.ortho_far_plane(), 322 ); 323 324 // Triangles 325 if !self.tri_vertices.is_empty() { 326 device.bind_program(&self.color_program); 327 device.set_uniforms(&self.color_program, &projection); 328 device.bind_vao(&self.tri_vao); 329 device.update_vao_indices(&self.tri_vao, &self.tri_indices, VertexUsageHint::Dynamic); 330 device.update_vao_main_vertices( 331 &self.tri_vao, 332 &self.tri_vertices, 333 VertexUsageHint::Dynamic, 334 ); 335 device.draw_triangles_u32(0, self.tri_indices.len() as i32); 336 } 337 338 // Lines 339 if !self.line_vertices.is_empty() { 340 device.bind_program(&self.color_program); 341 device.set_uniforms(&self.color_program, &projection); 342 device.bind_vao(&self.line_vao); 343 device.update_vao_main_vertices( 344 &self.line_vao, 345 &self.line_vertices, 346 VertexUsageHint::Dynamic, 347 ); 348 device.draw_nonindexed_lines(0, self.line_vertices.len() as i32); 349 } 350 351 // Glyph 352 if !self.font_indices.is_empty() { 353 device.bind_program(&self.font_program); 354 device.set_uniforms(&self.font_program, &projection); 355 device.bind_texture(DebugSampler::Font, &self.font_texture, Swizzle::default()); 356 device.bind_vao(&self.font_vao); 357 device.update_vao_indices(&self.font_vao, &self.font_indices, VertexUsageHint::Dynamic); 358 device.update_vao_main_vertices( 359 &self.font_vao, 360 &self.font_vertices, 361 VertexUsageHint::Dynamic, 362 ); 363 device.draw_triangles_u32(0, self.font_indices.len() as i32); 364 } 365 } 366 367 self.font_indices.clear(); 368 self.font_vertices.clear(); 369 self.line_vertices.clear(); 370 self.tri_vertices.clear(); 371 self.tri_indices.clear(); 372 } 373 } 374 375 pub struct LazyInitializedDebugRenderer { 376 debug_renderer: Option<DebugRenderer>, 377 failed: bool, 378 } 379 380 impl LazyInitializedDebugRenderer { new() -> Self381 pub fn new() -> Self { 382 Self { 383 debug_renderer: None, 384 failed: false, 385 } 386 } 387 get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer>388 pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> { 389 if self.failed { 390 return None; 391 } 392 if self.debug_renderer.is_none() { 393 match DebugRenderer::new(device) { 394 Ok(renderer) => { self.debug_renderer = Some(renderer); } 395 Err(_) => { 396 // The shader compilation code already logs errors. 397 self.failed = true; 398 } 399 } 400 } 401 402 self.debug_renderer.as_mut() 403 } 404 405 /// Returns mut ref to `debug::DebugRenderer` if one already exists, otherwise returns `None`. try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer>406 pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> { 407 self.debug_renderer.as_mut() 408 } 409 deinit(self, device: &mut Device)410 pub fn deinit(self, device: &mut Device) { 411 if let Some(debug_renderer) = self.debug_renderer { 412 debug_renderer.deinit(device); 413 } 414 } 415 } 416