1use std::cell::Cell;
5use std::cell::RefCell;
6use std::mem::needs_drop;
7use std::mem::replace;
8use std::num::NonZeroU16;
9use std::num::NonZeroU32;
10use std::ops::Add;
11use std::ops::DerefMut as _;
12use std::ops::Sub;
13
14use crate::guard::Guard;
15use crate::Point;
16use crate::Rect;
17
18use super::gl;
19use super::Context;
20use super::Texture;
21
22
23const VERTEX_BUFFER_CAPACITY: usize = 1024;
27
28
29#[derive(Clone, Copy, Debug)]
30#[allow(dead_code)]
31#[repr(packed)]
32struct Vertex {
33 u: gl::GLfloat,
35 v: gl::GLfloat,
36
37 r: gl::GLubyte,
39 g: gl::GLubyte,
40 b: gl::GLubyte,
41 a: gl::GLubyte,
42
43 x: gl::GLfloat,
45 y: gl::GLfloat,
46 z: gl::GLfloat,
47}
48
49
50#[derive(Clone, Copy, Debug, PartialEq)]
51#[repr(packed)]
52pub(crate) struct Color {
53 pub(crate) r: gl::GLubyte,
54 pub(crate) g: gl::GLubyte,
55 pub(crate) b: gl::GLubyte,
56 pub(crate) a: gl::GLubyte,
57}
58
59impl Color {
60 #[inline]
61 fn as_floats(&self) -> (gl::GLfloat, gl::GLfloat, gl::GLfloat, gl::GLfloat) {
62 (
63 self.r as gl::GLfloat / gl::GLubyte::MAX as gl::GLfloat,
64 self.g as gl::GLfloat / gl::GLubyte::MAX as gl::GLfloat,
65 self.b as gl::GLfloat / gl::GLubyte::MAX as gl::GLfloat,
66 self.a as gl::GLfloat / gl::GLubyte::MAX as gl::GLfloat,
67 )
68 }
69
70 const fn cadd(self, other: Color) -> Self {
72 Self {
73 r: self.r.saturating_add(other.r),
74 g: self.g.saturating_add(other.g),
75 b: self.b.saturating_add(other.b),
76 a: self.a.saturating_add(other.a),
77 }
78 }
79
80 pub const fn csub(self, other: Color) -> Self {
82 Self {
83 r: self.r.saturating_sub(other.r),
84 g: self.g.saturating_sub(other.g),
85 b: self.b.saturating_sub(other.b),
86 a: self.a.saturating_sub(other.a),
87 }
88 }
89
90
91 #[inline]
92 pub(crate) const fn black() -> Self {
93 Self {
94 r: gl::GLubyte::MIN,
95 g: gl::GLubyte::MIN,
96 b: gl::GLubyte::MIN,
97 a: gl::GLubyte::MAX,
98 }
99 }
100
101 #[inline]
102 pub(crate) const fn white() -> Self {
103 Self {
104 r: gl::GLubyte::MAX,
105 g: gl::GLubyte::MAX,
106 b: gl::GLubyte::MAX,
107 a: gl::GLubyte::MAX,
108 }
109 }
110
111 #[inline]
112 pub(crate) const fn red() -> Self {
113 Self {
114 r: gl::GLubyte::MAX,
115 g: gl::GLubyte::MIN,
116 b: gl::GLubyte::MIN,
117 a: gl::GLubyte::MAX,
118 }
119 }
120
121 #[inline]
122 pub(crate) const fn green() -> Self {
123 Self {
124 r: gl::GLubyte::MIN,
125 g: gl::GLubyte::MAX,
126 b: gl::GLubyte::MIN,
127 a: gl::GLubyte::MAX,
128 }
129 }
130
131 #[inline]
132 pub(crate) const fn blue() -> Self {
133 Self {
134 r: gl::GLubyte::MIN,
135 g: gl::GLubyte::MIN,
136 b: gl::GLubyte::MAX,
137 a: gl::GLubyte::MAX,
138 }
139 }
140
141 #[inline]
142 pub(crate) const fn yellow() -> Self {
143 Self::red().cadd(Self::green())
144 }
145
146 #[inline]
147 pub(crate) const fn violet() -> Self {
148 Self::red().cadd(Self::blue())
149 }
150
151 #[inline]
152 pub(crate) const fn cyan() -> Self {
153 Self::green().cadd(Self::blue())
154 }
155
156 #[inline]
157 pub(crate) const fn orange() -> Self {
158 Self {
159 r: gl::GLubyte::MAX,
160 g: gl::GLubyte::MAX / 2,
161 b: gl::GLubyte::MIN,
162 a: gl::GLubyte::MAX,
163 }
164 }
165
166 #[inline]
167 pub(crate) const fn gray() -> Self {
168 Self {
169 r: gl::GLubyte::MAX / 2,
170 g: gl::GLubyte::MAX / 2,
171 b: gl::GLubyte::MAX / 2,
172 a: gl::GLubyte::MAX,
173 }
174 }
175}
176
177impl Add<Color> for Color {
178 type Output = Color;
179
180 fn add(self, other: Color) -> Self::Output {
181 self.cadd(other)
182 }
183}
184
185impl Sub<Color> for Color {
186 type Output = Color;
187
188 fn sub(self, other: Color) -> Self::Output {
189 self.csub(other)
190 }
191}
192
193
194#[derive(Clone, Copy, Debug, Eq, PartialEq)]
195#[repr(u32)]
196enum Primitive {
197 Line = gl::LINES,
198 Quad = gl::QUADS,
199}
200
201impl Primitive {
202 fn as_glenum(&self) -> gl::GLenum {
203 *self as _
204 }
205}
206
207
208#[derive(Clone, Debug)]
209enum TextureState {
210 Bound { bound: Texture },
212 Unbound {
216 unbound: Texture,
217 still_bound: Texture,
218 },
219}
220
221impl TextureState {
222 fn activate(&mut self, texture: Texture) -> Texture {
224 match self {
225 Self::Bound { bound } if texture == *bound => texture,
226 Self::Bound { bound } => {
227 let state = Self::Unbound {
228 unbound: texture,
229 still_bound: bound.clone(),
230 };
231 replace(self, state).into_texture()
232 },
233 Self::Unbound { unbound, .. } => replace(unbound, texture),
234 }
235 }
236
237 fn ensure_bound(&mut self) {
239 match self {
240 Self::Bound { .. } => (),
241 Self::Unbound {
242 unbound,
243 still_bound,
244 } => {
245 if unbound != still_bound {
246 let () = unbound.bind();
247 }
248
249 let bound = unbound.clone();
253 let _prev = replace(self, Self::Bound { bound });
254 },
255 }
256 }
257
258 fn texture(&self) -> &Texture {
260 match self {
261 Self::Bound { bound: texture }
262 | Self::Unbound {
263 unbound: texture, ..
264 } => texture,
265 }
266 }
267
268 fn into_texture(self) -> Texture {
270 match self {
271 Self::Bound { bound: texture }
272 | Self::Unbound {
273 unbound: texture, ..
274 } => texture,
275 }
276 }
277}
278
279
280#[derive(Debug)]
282pub struct ActiveRenderer<'renderer> {
283 renderer: &'renderer Renderer,
285 invalid_texture: Texture,
287 origin: Cell<Point<i16>>,
289 color: Cell<Color>,
291 texture: RefCell<TextureState>,
293 vertices: RefCell<Vec<Vertex>>,
295 primitive: Cell<Primitive>,
297}
298
299impl<'renderer> ActiveRenderer<'renderer> {
300 fn new(renderer: &'renderer Renderer) -> Self {
301 let invalid_texture = Texture::invalid();
302 Self {
303 renderer,
304 invalid_texture: invalid_texture.clone(),
305 origin: Cell::new(Point::default()),
306 color: Cell::new(Color::black()),
307 texture: RefCell::new(TextureState::Bound {
312 bound: invalid_texture,
313 }),
314 vertices: RefCell::new(Vec::with_capacity(VERTEX_BUFFER_CAPACITY)),
315 primitive: Cell::new(Primitive::Quad),
316 }
317 }
318
319 #[inline]
321 pub(crate) fn set_origin(&self, origin: Point<i16>) -> Guard<'_, impl FnOnce() + '_> {
322 let new_origin = self.origin.get() + origin;
323 let prev_origin = self.origin.replace(new_origin);
324 Guard::new(move || self.origin.set(prev_origin))
325 }
326
327 #[inline]
329 pub(crate) fn set_color(&self, color: Color) -> Guard<'_, impl FnOnce() + '_> {
330 let prev_color = self.color.replace(color);
331 Guard::new(move || self.color.set(prev_color))
332 }
333
334 #[inline]
335 pub(crate) fn set_texture(&self, texture: &Texture) -> Guard<'_, impl FnOnce() + '_> {
336 fn set(renderer: &ActiveRenderer, texture: Texture) -> Texture {
337 let mut state = renderer.texture.borrow_mut();
338 let state = state.deref_mut();
339
340 if texture != *state.texture() {
341 let () = renderer.flush_vertex_buffer(state);
342 }
343
344 state.activate(texture)
345 }
346
347 let texture = texture.clone();
348 let prev_texture = set(self, texture);
349
350 Guard::new(move || {
351 let _prev = set(self, prev_texture);
352 })
353 }
354
355 #[inline]
356 pub(crate) fn set_no_texture(&self) -> Guard<'_, impl FnOnce() + '_> {
357 self.set_texture(&self.invalid_texture)
358 }
359
360 pub(crate) fn clear_screen(&self, color: Color) {
362 let (r, g, b, a) = color.as_floats();
363
364 unsafe { gl::ClearColor(r, g, b, a) };
365 unsafe { gl::Clear(gl::COLOR_BUFFER_BIT) };
366
367 debug_assert_eq!(unsafe { gl::GetError() }, gl::NO_ERROR);
368 }
369
370 pub(crate) fn render_line(&self, mut p1: Point<i16>, mut p2: Point<i16>) {
372 const VERTEX_COUNT_LINE: usize = 2;
373
374 let origin = self.origin.get();
375 p1 += origin;
376 p2 += origin;
377
378 let () = self.set_primitive(Primitive::Line, VERTEX_COUNT_LINE);
379 let color = self.color.get();
380
381 let mut vertex = Vertex {
382 u: 0.0,
383 v: 0.0,
384 r: color.r,
385 g: color.g,
386 b: color.b,
387 a: color.a,
388 x: p1.x.into(),
389 y: p1.y.into(),
390 z: 0.0,
391 };
392
393 let mut buffer = self.vertices.borrow_mut();
394 let vertices = buffer.spare_capacity_mut();
395 vertices[0].write(vertex);
396
397 vertex.x = p2.x.into();
399 vertex.y = p2.y.into();
400 vertices[1].write(vertex);
401
402 let len = buffer.len();
403 let () = unsafe { buffer.set_len(len + VERTEX_COUNT_LINE) };
404 }
405
406 pub(crate) fn render_rect(&self, rect: Rect<i16>) {
408 let () = self.render_rect_f32(rect.into_other());
409 }
410
411 pub(crate) fn render_rect_f32(&self, rect: Rect<f32>) {
413 let coords = Rect::new(0.0, 0.0, 1.0, 1.0);
416 let () = self.render_rect_with_tex_coords_f32(rect, coords);
417 }
418
419 pub(crate) fn render_rect_with_tex_coords(&self, rect: Rect<i16>, coords: Rect<f32>) {
421 self.render_rect_with_tex_coords_f32(rect.into_other(), coords)
422 }
423
424 pub(crate) fn render_rect_with_tex_coords_f32(&self, mut rect: Rect<f32>, coords: Rect<f32>) {
426 const VERTEX_COUNT_QUAD: usize = 4;
427
428 let origin = self.origin.get();
429 rect += origin.into_other();
430
431 let () = self.set_primitive(Primitive::Quad, VERTEX_COUNT_QUAD);
432 let color = self.color.get();
433
434 let mut vertex = Vertex {
435 u: coords.x,
436 v: coords.y,
437 r: color.r,
438 g: color.g,
439 b: color.b,
440 a: color.a,
441 x: rect.x,
442 y: rect.y,
443 z: 0.0,
444 };
445
446 let mut buffer = self.vertices.borrow_mut();
447 let vertices = buffer.spare_capacity_mut();
448 vertices[0].write(vertex);
449
450 vertex.u += coords.w;
452 vertex.x += rect.w;
453 vertices[1].write(vertex);
454
455 vertex.v += coords.h;
457 vertex.y += rect.h;
458 vertices[2].write(vertex);
459
460 vertex.u = coords.x;
462 vertex.x = rect.x;
463 vertices[3].write(vertex);
464
465 let len = buffer.len();
466 let () = unsafe { buffer.set_len(len + VERTEX_COUNT_QUAD) };
467 }
468
469 fn set_primitive(&self, primitive: Primitive, vertex_cnt: usize) {
473 if primitive != self.primitive.get()
474 || self.vertices.borrow_mut().spare_capacity_mut().len() < vertex_cnt
475 {
476 let () = self.flush_vertex_buffer(self.texture.borrow_mut().deref_mut());
477 let () = self.primitive.set(primitive);
478 }
479 }
480
481 fn flush_vertex_buffer(&self, texture: &mut TextureState) {
483 let mut buffer = self.vertices.borrow_mut();
484 let size = buffer.len() as _;
485 if size > 0 {
486 let () = texture.ensure_bound();
487
488 unsafe {
489 gl::InterleavedArrays(gl::T2F_C4UB_V3F, 0, buffer.as_ptr().cast());
490 gl::DrawArrays(self.primitive.get().as_glenum(), 0, size);
491
492 debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
493 }
494
495 debug_assert!(!needs_drop::<Vertex>());
496 unsafe { buffer.set_len(0) };
499 }
500 }
501
502 #[inline]
504 pub(crate) fn phys_width(&self) -> u32 {
505 self.renderer.phys_w as _
506 }
507
508 #[inline]
510 pub(crate) fn phys_height(&self) -> u32 {
511 self.renderer.phys_h as _
512 }
513
514 #[inline]
517 pub(crate) fn logic_width(&self) -> f32 {
518 self.renderer.logic_w as _
519 }
520
521 #[inline]
524 pub(crate) fn logic_height(&self) -> f32 {
525 self.renderer.logic_h as _
526 }
527}
528
529impl Drop for ActiveRenderer<'_> {
530 fn drop(&mut self) {
531 let () = self.flush_vertex_buffer(self.texture.borrow_mut().deref_mut());
532 let () = self.renderer.on_post_render();
533 }
534}
535
536
537#[derive(Debug)]
539pub struct Renderer {
540 phys_w: gl::GLsizei,
542 phys_h: gl::GLsizei,
544 logic_w: gl::GLfloat,
546 logic_h: gl::GLfloat,
548}
549
550impl Renderer {
551 pub fn new(
554 phys_w: NonZeroU32,
555 phys_h: NonZeroU32,
556 logic_w: NonZeroU16,
557 logic_h: NonZeroU16,
558 ) -> Self {
559 let (logic_w, logic_h) = Self::calculate_view(phys_w, phys_h, logic_w, logic_h);
560
561 Self {
562 phys_w: gl::GLsizei::try_from(phys_w.get()).unwrap_or(gl::GLsizei::MAX),
563 phys_h: gl::GLsizei::try_from(phys_h.get()).unwrap_or(gl::GLsizei::MAX),
564 logic_w,
565 logic_h,
566 }
567 }
568
569 fn calculate_view(
570 phys_w: NonZeroU32,
571 phys_h: NonZeroU32,
572 logic_w: NonZeroU16,
573 logic_h: NonZeroU16,
574 ) -> (gl::GLfloat, gl::GLfloat) {
575 let phys_w = phys_w.get() as gl::GLfloat;
576 let phys_h = phys_h.get() as gl::GLfloat;
577 let logic_w = logic_w.get() as gl::GLfloat;
578 let logic_h = logic_h.get() as gl::GLfloat;
579
580 let phys_ratio = phys_w / phys_h;
581 let logic_ratio = logic_w / logic_h;
582
583 let mut width = logic_w;
584 let mut height = logic_h;
585
586 if logic_ratio > phys_ratio {
595 height += logic_w * phys_h / phys_w - logic_h;
596 } else {
597 width += logic_h * phys_w / phys_h - logic_w;
598 }
599 (width, height)
600 }
601
602 pub fn update_view(
605 &mut self,
606 phys_w: NonZeroU32,
607 phys_h: NonZeroU32,
608 logic_w: NonZeroU16,
609 logic_h: NonZeroU16,
610 ) {
611 let (logic_w, logic_h) = Self::calculate_view(phys_w, phys_h, logic_w, logic_h);
612
613 self.phys_w = gl::GLsizei::try_from(phys_w.get()).unwrap_or(gl::GLsizei::MAX);
614 self.phys_h = gl::GLsizei::try_from(phys_h.get()).unwrap_or(gl::GLsizei::MAX);
615 self.logic_w = logic_w;
616 self.logic_h = logic_h;
617 }
618
619 fn push_states(&self) {
620 unsafe {
621 gl::PushAttrib(
622 gl::CURRENT_BIT
623 | gl::COLOR_BUFFER_BIT
624 | gl::DEPTH_BUFFER_BIT
625 | gl::ENABLE_BIT
626 | gl::FOG_BIT
627 | gl::LIGHTING_BIT
628 | gl::LINE_BIT
629 | gl::POINT_BIT
630 | gl::SCISSOR_BIT
631 | gl::STENCIL_BUFFER_BIT
632 | gl::TEXTURE_BIT
633 | gl::TRANSFORM_BIT
634 | gl::VIEWPORT_BIT,
635 );
636
637 gl::Disable(gl::FOG);
638 gl::Disable(gl::LIGHTING);
639 gl::Disable(gl::COLOR_MATERIAL);
640 gl::Disable(gl::DEPTH_TEST);
641 gl::Disable(gl::SCISSOR_TEST);
642 gl::Disable(gl::CULL_FACE);
643
644 gl::Enable(gl::TEXTURE_2D);
645
646 gl::PointSize(1.0);
647 gl::LineWidth(1.0);
648
649 gl::Viewport(0, 0, self.phys_w, self.phys_h);
650
651 debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
652 }
653 }
654
655 fn pop_states(&self) {
656 unsafe {
657 gl::PopAttrib();
658
659 debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
660 }
661 }
662
663 fn push_matrizes(&self) {
664 unsafe {
665 gl::MatrixMode(gl::PROJECTION);
668 gl::PushMatrix();
669 gl::LoadIdentity();
670 gl::Ortho(
674 0.0,
675 self.logic_w.into(),
676 0.0,
677 self.logic_h.into(),
678 -0.5,
679 0.5,
680 );
681
682 gl::MatrixMode(gl::TEXTURE);
683 gl::PushMatrix();
684 gl::LoadIdentity();
685
686 gl::MatrixMode(gl::MODELVIEW);
687 gl::PushMatrix();
688 gl::LoadIdentity();
689
690 debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
691 }
692 }
693
694 fn pop_matrizes(&self) {
695 unsafe {
696 gl::MatrixMode(gl::MODELVIEW);
697 gl::PopMatrix();
698
699 gl::MatrixMode(gl::TEXTURE);
700 gl::PopMatrix();
701
702 gl::MatrixMode(gl::PROJECTION);
703 gl::PopMatrix();
704
705 debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
706 }
707 }
708
709 pub fn on_pre_render<'ctx>(&'ctx self, context: &'ctx mut Context) -> ActiveRenderer<'ctx> {
715 let _ = context;
716 let () = self.push_states();
717 let () = self.push_matrizes();
718
719 ActiveRenderer::new(self)
720 }
721
722 fn on_post_render(&self) {
723 let () = self.pop_matrizes();
724 let () = self.pop_states();
725 }
726}
727
728
729#[cfg(test)]
730mod tests {
731 use super::*;
732
733
734 #[test]
737 fn color_float_conversion() {
738 let (r, g, b, a) = Color::white().as_floats();
739 let e = gl::GLfloat::EPSILON;
740 assert!(1.0 - e <= r && r <= 1.0 + e);
741 assert!(1.0 - e <= g && g <= 1.0 + e);
742 assert!(1.0 - e <= b && b <= 1.0 + e);
743 assert!(1.0 - e <= a && a <= 1.0 + e);
744 }
745}