1use 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 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 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 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 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 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 self.failed = true;
408 }
409 }
410 }
411
412 self.debug_renderer.as_mut()
413 }
414
415 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}