softbuffer_window_renderer/
lib.rs1#![cfg_attr(docsrs, feature(doc_cfg))]
4
5use anyrender::{ImageRenderer, WindowHandle, WindowRenderer};
6use debug_timer::debug_timer;
7use softbuffer::{Context, Surface};
8use std::{num::NonZero, sync::Arc};
9
10pub struct ActiveRenderState {
12 _context: Context<Arc<dyn WindowHandle>>,
13 surface: Surface<Arc<dyn WindowHandle>, Arc<dyn WindowHandle>>,
14}
15
16#[allow(clippy::large_enum_variant)]
17pub enum RenderState {
18 Active(ActiveRenderState),
19 Suspended,
20}
21
22pub struct SoftbufferWindowRenderer<Renderer: ImageRenderer> {
23 render_state: RenderState,
26 window_handle: Option<Arc<dyn WindowHandle>>,
27 renderer: Renderer,
28}
29
30impl<Renderer: ImageRenderer> SoftbufferWindowRenderer<Renderer> {
31 #[allow(clippy::new_without_default)]
32 pub fn new() -> Self {
33 Self::with_renderer(Renderer::new(0, 0))
34 }
35
36 pub fn with_renderer<R: ImageRenderer>(renderer: R) -> SoftbufferWindowRenderer<R> {
37 SoftbufferWindowRenderer {
38 render_state: RenderState::Suspended,
39 window_handle: None,
40 renderer,
41 }
42 }
43}
44
45impl<Renderer: ImageRenderer> WindowRenderer for SoftbufferWindowRenderer<Renderer> {
46 type ScenePainter<'a>
47 = Renderer::ScenePainter<'a>
48 where
49 Self: 'a;
50
51 fn is_active(&self) -> bool {
52 matches!(self.render_state, RenderState::Active(_))
53 }
54
55 fn resume(&mut self, window_handle: Arc<dyn WindowHandle>, width: u32, height: u32) {
56 let context = Context::new(window_handle.clone()).unwrap();
57 let surface = Surface::new(&context, window_handle.clone()).unwrap();
58 self.render_state = RenderState::Active(ActiveRenderState {
59 _context: context,
60 surface,
61 });
62 self.window_handle = Some(window_handle);
63
64 self.set_size(width, height);
65 }
66
67 fn suspend(&mut self) {
68 self.render_state = RenderState::Suspended;
69 }
70
71 fn set_size(&mut self, physical_width: u32, physical_height: u32) {
72 if let RenderState::Active(state) = &mut self.render_state {
73 state
74 .surface
75 .resize(
76 NonZero::new(physical_width.max(1)).unwrap(),
77 NonZero::new(physical_height.max(1)).unwrap(),
78 )
79 .unwrap();
80 self.renderer.resize(physical_width, physical_height);
81 };
82 }
83
84 fn render<F: FnOnce(&mut Renderer::ScenePainter<'_>)>(&mut self, draw_fn: F) {
85 let RenderState::Active(state) = &mut self.render_state else {
86 return;
87 };
88
89 debug_timer!(timer, feature = "log_frame_times");
90
91 let Ok(mut surface_buffer) = state.surface.buffer_mut() else {
92 return;
93 };
94 timer.record_time("buffer_mut");
95
96 let mut vec = Vec::new();
98 self.renderer.render_to_vec(draw_fn, &mut vec);
99 timer.record_time("render");
100
101 let out = surface_buffer.as_mut();
102
103 let chunks = vec.chunks_exact(4);
105 assert_eq!(chunks.size_hint().0, out.len());
106 assert_eq!(chunks.remainder().len(), 0);
107
108 for (src, dest) in chunks.zip(out.iter_mut()) {
109 let [r, g, b, a]: [u8; 4] = src.try_into().unwrap();
110 if a == 0 {
111 *dest = u32::MAX;
112 } else {
113 *dest = (r as u32) << 16 | (g as u32) << 8 | b as u32;
114 }
115 }
116 timer.record_time("swizel");
117
118 surface_buffer.present().unwrap();
119 timer.record_time("present");
120 timer.print_times("softbuffer: ");
121
122 self.renderer.reset();
124 }
125}