forked from mirrors/gecko-dev
415 lines
13 KiB
Rust
415 lines
13 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
use api::{ColorU, ImageFormat, ImageBufferKind};
|
|
use api::units::*;
|
|
use crate::debug_font_data;
|
|
use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO};
|
|
use crate::device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint};
|
|
use euclid::{Point2D, Rect, Size2D, Transform3D, default};
|
|
use crate::internal_types::Swizzle;
|
|
use std::f32;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
enum DebugSampler {
|
|
Font,
|
|
}
|
|
|
|
impl Into<TextureSlot> for DebugSampler {
|
|
fn into(self) -> TextureSlot {
|
|
match self {
|
|
DebugSampler::Font => TextureSlot(0),
|
|
}
|
|
}
|
|
}
|
|
|
|
const DESC_FONT: VertexDescriptor = VertexDescriptor {
|
|
vertex_attributes: &[
|
|
VertexAttribute {
|
|
name: "aPosition",
|
|
count: 2,
|
|
kind: VertexAttributeKind::F32,
|
|
},
|
|
VertexAttribute {
|
|
name: "aColor",
|
|
count: 4,
|
|
kind: VertexAttributeKind::U8Norm,
|
|
},
|
|
VertexAttribute {
|
|
name: "aColorTexCoord",
|
|
count: 2,
|
|
kind: VertexAttributeKind::F32,
|
|
},
|
|
],
|
|
instance_attributes: &[],
|
|
};
|
|
|
|
const DESC_COLOR: VertexDescriptor = VertexDescriptor {
|
|
vertex_attributes: &[
|
|
VertexAttribute {
|
|
name: "aPosition",
|
|
count: 2,
|
|
kind: VertexAttributeKind::F32,
|
|
},
|
|
VertexAttribute {
|
|
name: "aColor",
|
|
count: 4,
|
|
kind: VertexAttributeKind::U8Norm,
|
|
},
|
|
],
|
|
instance_attributes: &[],
|
|
};
|
|
|
|
#[repr(C)]
|
|
pub struct DebugFontVertex {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub color: ColorU,
|
|
pub u: f32,
|
|
pub v: f32,
|
|
}
|
|
|
|
impl DebugFontVertex {
|
|
pub fn new(x: f32, y: f32, u: f32, v: f32, color: ColorU) -> DebugFontVertex {
|
|
DebugFontVertex { x, y, color, u, v }
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct DebugColorVertex {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub color: ColorU,
|
|
}
|
|
|
|
impl DebugColorVertex {
|
|
pub fn new(x: f32, y: f32, color: ColorU) -> DebugColorVertex {
|
|
DebugColorVertex { x, y, color }
|
|
}
|
|
}
|
|
|
|
pub struct DebugRenderer {
|
|
font_vertices: Vec<DebugFontVertex>,
|
|
font_indices: Vec<u32>,
|
|
font_program: Program,
|
|
font_vao: VAO,
|
|
font_texture: Texture,
|
|
|
|
tri_vertices: Vec<DebugColorVertex>,
|
|
tri_indices: Vec<u32>,
|
|
tri_vao: VAO,
|
|
line_vertices: Vec<DebugColorVertex>,
|
|
line_vao: VAO,
|
|
color_program: Program,
|
|
}
|
|
|
|
impl DebugRenderer {
|
|
pub fn new(device: &mut Device) -> Result<Self, ShaderError> {
|
|
let font_program = device.create_program_linked(
|
|
"debug_font",
|
|
&[],
|
|
&DESC_FONT,
|
|
)?;
|
|
device.bind_program(&font_program);
|
|
device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
|
|
|
|
let color_program = device.create_program_linked(
|
|
"debug_color",
|
|
&[],
|
|
&DESC_COLOR,
|
|
)?;
|
|
|
|
let font_vao = device.create_vao(&DESC_FONT, 1);
|
|
let line_vao = device.create_vao(&DESC_COLOR, 1);
|
|
let tri_vao = device.create_vao(&DESC_COLOR, 1);
|
|
|
|
let font_texture = device.create_texture(
|
|
ImageBufferKind::Texture2D,
|
|
ImageFormat::R8,
|
|
debug_font_data::BMP_WIDTH,
|
|
debug_font_data::BMP_HEIGHT,
|
|
TextureFilter::Linear,
|
|
None,
|
|
);
|
|
device.upload_texture_immediate(
|
|
&font_texture,
|
|
&debug_font_data::FONT_BITMAP
|
|
);
|
|
|
|
Ok(DebugRenderer {
|
|
font_vertices: Vec::new(),
|
|
font_indices: Vec::new(),
|
|
line_vertices: Vec::new(),
|
|
tri_vao,
|
|
tri_vertices: Vec::new(),
|
|
tri_indices: Vec::new(),
|
|
font_program,
|
|
color_program,
|
|
font_vao,
|
|
line_vao,
|
|
font_texture,
|
|
})
|
|
}
|
|
|
|
pub fn deinit(self, device: &mut Device) {
|
|
device.delete_texture(self.font_texture);
|
|
device.delete_program(self.font_program);
|
|
device.delete_program(self.color_program);
|
|
device.delete_vao(self.tri_vao);
|
|
device.delete_vao(self.line_vao);
|
|
device.delete_vao(self.font_vao);
|
|
}
|
|
|
|
pub fn line_height(&self) -> f32 {
|
|
debug_font_data::FONT_SIZE as f32 * 1.1
|
|
}
|
|
|
|
/// Draws a line of text at the provided starting coordinates.
|
|
///
|
|
/// If |bounds| is specified, glyphs outside the bounds are discarded.
|
|
///
|
|
/// Y-coordinates is relative to screen top, along with everything else in
|
|
/// this file.
|
|
pub fn add_text(
|
|
&mut self,
|
|
x: f32,
|
|
y: f32,
|
|
text: &str,
|
|
color: ColorU,
|
|
bounds: Option<DeviceRect>,
|
|
) -> default::Rect<f32> {
|
|
let mut x_start = x;
|
|
let ipw = 1.0 / debug_font_data::BMP_WIDTH as f32;
|
|
let iph = 1.0 / debug_font_data::BMP_HEIGHT as f32;
|
|
|
|
let mut min_x = f32::MAX;
|
|
let mut max_x = -f32::MAX;
|
|
let mut min_y = f32::MAX;
|
|
let mut max_y = -f32::MAX;
|
|
|
|
for c in text.chars() {
|
|
let c = c as usize - debug_font_data::FIRST_GLYPH_INDEX as usize;
|
|
if c < debug_font_data::GLYPHS.len() {
|
|
let glyph = &debug_font_data::GLYPHS[c];
|
|
|
|
let x0 = (x_start + glyph.xo + 0.5).floor();
|
|
let y0 = (y + glyph.yo + 0.5).floor();
|
|
|
|
let x1 = x0 + glyph.x1 as f32 - glyph.x0 as f32;
|
|
let y1 = y0 + glyph.y1 as f32 - glyph.y0 as f32;
|
|
|
|
// If either corner of the glyph will end up out of bounds, drop it.
|
|
if let Some(b) = bounds {
|
|
let rect = DeviceRect {
|
|
min: DevicePoint::new(x0, y0),
|
|
max: DevicePoint::new(x1, y1),
|
|
};
|
|
if !b.contains_box(&rect) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
let s0 = glyph.x0 as f32 * ipw;
|
|
let t0 = glyph.y0 as f32 * iph;
|
|
let s1 = glyph.x1 as f32 * ipw;
|
|
let t1 = glyph.y1 as f32 * iph;
|
|
|
|
x_start += glyph.xa;
|
|
|
|
let vertex_count = self.font_vertices.len() as u32;
|
|
|
|
self.font_vertices
|
|
.push(DebugFontVertex::new(x0, y0, s0, t0, color));
|
|
self.font_vertices
|
|
.push(DebugFontVertex::new(x1, y0, s1, t0, color));
|
|
self.font_vertices
|
|
.push(DebugFontVertex::new(x0, y1, s0, t1, color));
|
|
self.font_vertices
|
|
.push(DebugFontVertex::new(x1, y1, s1, t1, color));
|
|
|
|
self.font_indices.push(vertex_count + 0);
|
|
self.font_indices.push(vertex_count + 1);
|
|
self.font_indices.push(vertex_count + 2);
|
|
self.font_indices.push(vertex_count + 2);
|
|
self.font_indices.push(vertex_count + 1);
|
|
self.font_indices.push(vertex_count + 3);
|
|
|
|
min_x = min_x.min(x0);
|
|
max_x = max_x.max(x1);
|
|
min_y = min_y.min(y0);
|
|
max_y = max_y.max(y1);
|
|
}
|
|
}
|
|
|
|
Rect::new(
|
|
Point2D::new(min_x, min_y),
|
|
Size2D::new(max_x - min_x, max_y - min_y),
|
|
)
|
|
}
|
|
|
|
pub fn add_quad(
|
|
&mut self,
|
|
x0: f32,
|
|
y0: f32,
|
|
x1: f32,
|
|
y1: f32,
|
|
color_top: ColorU,
|
|
color_bottom: ColorU,
|
|
) {
|
|
let vertex_count = self.tri_vertices.len() as u32;
|
|
|
|
self.tri_vertices
|
|
.push(DebugColorVertex::new(x0, y0, color_top));
|
|
self.tri_vertices
|
|
.push(DebugColorVertex::new(x1, y0, color_top));
|
|
self.tri_vertices
|
|
.push(DebugColorVertex::new(x0, y1, color_bottom));
|
|
self.tri_vertices
|
|
.push(DebugColorVertex::new(x1, y1, color_bottom));
|
|
|
|
self.tri_indices.push(vertex_count + 0);
|
|
self.tri_indices.push(vertex_count + 1);
|
|
self.tri_indices.push(vertex_count + 2);
|
|
self.tri_indices.push(vertex_count + 2);
|
|
self.tri_indices.push(vertex_count + 1);
|
|
self.tri_indices.push(vertex_count + 3);
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn add_line(&mut self, x0: i32, y0: i32, color0: ColorU, x1: i32, y1: i32, color1: ColorU) {
|
|
self.line_vertices
|
|
.push(DebugColorVertex::new(x0 as f32, y0 as f32, color0));
|
|
self.line_vertices
|
|
.push(DebugColorVertex::new(x1 as f32, y1 as f32, color1));
|
|
}
|
|
|
|
|
|
pub fn add_rect(&mut self, rect: &DeviceIntRect, color: ColorU) {
|
|
let p0 = rect.min;
|
|
let p1 = rect.max;
|
|
self.add_line(p0.x, p0.y, color, p1.x, p0.y, color);
|
|
self.add_line(p1.x, p0.y, color, p1.x, p1.y, color);
|
|
self.add_line(p1.x, p1.y, color, p0.x, p1.y, color);
|
|
self.add_line(p0.x, p1.y, color, p0.x, p0.y, color);
|
|
}
|
|
|
|
pub fn render(
|
|
&mut self,
|
|
device: &mut Device,
|
|
viewport_size: Option<DeviceIntSize>,
|
|
scale: f32,
|
|
surface_origin_is_top_left: bool,
|
|
) {
|
|
if let Some(viewport_size) = viewport_size {
|
|
device.disable_depth();
|
|
device.set_blend(true);
|
|
device.set_blend_mode_premultiplied_alpha();
|
|
|
|
let (bottom, top) = if surface_origin_is_top_left {
|
|
(0.0, viewport_size.height as f32 * scale)
|
|
} else {
|
|
(viewport_size.height as f32 * scale, 0.0)
|
|
};
|
|
|
|
let projection = Transform3D::ortho(
|
|
0.0,
|
|
viewport_size.width as f32 * scale,
|
|
bottom,
|
|
top,
|
|
device.ortho_near_plane(),
|
|
device.ortho_far_plane(),
|
|
);
|
|
|
|
// Triangles
|
|
if !self.tri_vertices.is_empty() {
|
|
device.bind_program(&self.color_program);
|
|
device.set_uniforms(&self.color_program, &projection);
|
|
device.bind_vao(&self.tri_vao);
|
|
device.update_vao_indices(&self.tri_vao, &self.tri_indices, VertexUsageHint::Dynamic);
|
|
device.update_vao_main_vertices(
|
|
&self.tri_vao,
|
|
&self.tri_vertices,
|
|
VertexUsageHint::Dynamic,
|
|
);
|
|
device.draw_triangles_u32(0, self.tri_indices.len() as i32);
|
|
}
|
|
|
|
// Lines
|
|
if !self.line_vertices.is_empty() {
|
|
device.bind_program(&self.color_program);
|
|
device.set_uniforms(&self.color_program, &projection);
|
|
device.bind_vao(&self.line_vao);
|
|
device.update_vao_main_vertices(
|
|
&self.line_vao,
|
|
&self.line_vertices,
|
|
VertexUsageHint::Dynamic,
|
|
);
|
|
device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
|
|
}
|
|
|
|
// Glyph
|
|
if !self.font_indices.is_empty() {
|
|
device.bind_program(&self.font_program);
|
|
device.set_uniforms(&self.font_program, &projection);
|
|
device.bind_texture(DebugSampler::Font, &self.font_texture, Swizzle::default());
|
|
device.bind_vao(&self.font_vao);
|
|
device.update_vao_indices(&self.font_vao, &self.font_indices, VertexUsageHint::Dynamic);
|
|
device.update_vao_main_vertices(
|
|
&self.font_vao,
|
|
&self.font_vertices,
|
|
VertexUsageHint::Dynamic,
|
|
);
|
|
device.draw_triangles_u32(0, self.font_indices.len() as i32);
|
|
}
|
|
}
|
|
|
|
self.font_indices.clear();
|
|
self.font_vertices.clear();
|
|
self.line_vertices.clear();
|
|
self.tri_vertices.clear();
|
|
self.tri_indices.clear();
|
|
}
|
|
}
|
|
|
|
pub struct LazyInitializedDebugRenderer {
|
|
debug_renderer: Option<DebugRenderer>,
|
|
failed: bool,
|
|
}
|
|
|
|
impl LazyInitializedDebugRenderer {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
debug_renderer: None,
|
|
failed: false,
|
|
}
|
|
}
|
|
|
|
pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> {
|
|
if self.failed {
|
|
return None;
|
|
}
|
|
if self.debug_renderer.is_none() {
|
|
match DebugRenderer::new(device) {
|
|
Ok(renderer) => { self.debug_renderer = Some(renderer); }
|
|
Err(_) => {
|
|
// The shader compilation code already logs errors.
|
|
self.failed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
self.debug_renderer.as_mut()
|
|
}
|
|
|
|
/// Returns mut ref to `debug::DebugRenderer` if one already exists, otherwise returns `None`.
|
|
pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> {
|
|
self.debug_renderer.as_mut()
|
|
}
|
|
|
|
pub fn deinit(self, device: &mut Device) {
|
|
if let Some(debug_renderer) = self.debug_renderer {
|
|
debug_renderer.deinit(device);
|
|
}
|
|
}
|
|
}
|