-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathlib.rs
More file actions
125 lines (103 loc) · 3.92 KB
/
lib.rs
File metadata and controls
125 lines (103 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! An AnyRender WindowRenderer for rendering pixel buffers using the softbuffer crate
#![cfg_attr(docsrs, feature(doc_cfg))]
use anyrender::{ImageRenderer, WindowHandle, WindowRenderer};
use debug_timer::debug_timer;
use softbuffer::{Context, Surface};
use std::{num::NonZero, sync::Arc};
// Simple struct to hold the state of the renderer
pub struct ActiveRenderState {
_context: Context<Arc<dyn WindowHandle>>,
surface: Surface<Arc<dyn WindowHandle>, Arc<dyn WindowHandle>>,
}
#[allow(clippy::large_enum_variant)]
pub enum RenderState {
Active(ActiveRenderState),
Suspended,
}
pub struct SoftbufferWindowRenderer<Renderer: ImageRenderer> {
// The fields MUST be in this order, so that the surface is dropped before the window
// Window is cached even when suspended so that it can be reused when the app is resumed after being suspended
render_state: RenderState,
window_handle: Option<Arc<dyn WindowHandle>>,
renderer: Renderer,
buffer: Vec<u8>,
}
impl<Renderer: ImageRenderer> SoftbufferWindowRenderer<Renderer> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::with_renderer(Renderer::new(0, 0))
}
pub fn with_renderer<R: ImageRenderer>(renderer: R) -> SoftbufferWindowRenderer<R> {
SoftbufferWindowRenderer {
render_state: RenderState::Suspended,
window_handle: None,
renderer,
buffer: Vec::new(),
}
}
}
impl<Renderer: ImageRenderer> WindowRenderer for SoftbufferWindowRenderer<Renderer> {
type ScenePainter<'a>
= Renderer::ScenePainter<'a>
where
Self: 'a;
fn is_active(&self) -> bool {
matches!(self.render_state, RenderState::Active(_))
}
async fn resume(&mut self, window_handle: Arc<dyn WindowHandle>, width: u32, height: u32) {
let context = Context::new(window_handle.clone()).unwrap();
let surface = Surface::new(&context, window_handle.clone()).unwrap();
self.render_state = RenderState::Active(ActiveRenderState {
_context: context,
surface,
});
self.window_handle = Some(window_handle);
self.set_size(width, height);
}
fn suspend(&mut self) {
self.render_state = RenderState::Suspended;
}
fn set_size(&mut self, physical_width: u32, physical_height: u32) {
if let RenderState::Active(state) = &mut self.render_state {
state
.surface
.resize(
NonZero::new(physical_width.max(1)).unwrap(),
NonZero::new(physical_height.max(1)).unwrap(),
)
.unwrap();
self.renderer.resize(physical_width, physical_height);
};
}
fn render<F: FnOnce(&mut Renderer::ScenePainter<'_>)>(&mut self, draw_fn: F) {
let RenderState::Active(state) = &mut self.render_state else {
return;
};
debug_timer!(timer, feature = "log_frame_times");
let Ok(mut surface_buffer) = state.surface.buffer_mut() else {
return;
};
timer.record_time("buffer_mut");
// Paint
self.renderer.render_to_vec(draw_fn, &mut self.buffer);
timer.record_time("render");
let out = surface_buffer.as_mut();
let (chunks, remainder) = self.buffer.as_chunks::<4>();
assert_eq!(chunks.len(), out.len());
assert_eq!(remainder.len(), 0);
for (&src, dest) in chunks.iter().zip(out.iter_mut()) {
let [r, g, b, a] = src;
if a == 0 {
*dest = u32::MAX;
} else {
*dest = (r as u32) << 16 | (g as u32) << 8 | b as u32;
}
}
timer.record_time("swizel");
surface_buffer.present().unwrap();
timer.record_time("present");
timer.print_times("softbuffer: ");
// Reset the renderer ready for the next render
self.renderer.reset();
}
}