pixels_window_renderer/
lib.rs

1//! An AnyRender WindowRenderer for rendering pixel buffers using the pixels crate
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5use anyrender::{ImageRenderer, WindowHandle, WindowRenderer};
6use debug_timer::debug_timer;
7use pixels::{Pixels, SurfaceTexture, wgpu::Color};
8use std::sync::Arc;
9
10// Simple struct to hold the state of the renderer
11pub struct ActiveRenderState {
12    // surface: SurfaceTexture<Arc<dyn WindowHandle>>,
13    pixels: Pixels<'static>,
14}
15
16#[allow(clippy::large_enum_variant)]
17pub enum RenderState {
18    Active(ActiveRenderState),
19    Suspended,
20}
21
22pub struct PixelsWindowRenderer<Renderer: ImageRenderer> {
23    // The fields MUST be in this order, so that the surface is dropped before the window
24    // Window is cached even when suspended so that it can be reused when the app is resumed after being suspended
25    render_state: RenderState,
26    window_handle: Option<Arc<dyn WindowHandle>>,
27    renderer: Renderer,
28}
29
30impl<Renderer: ImageRenderer> PixelsWindowRenderer<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) -> PixelsWindowRenderer<R> {
37        PixelsWindowRenderer {
38            render_state: RenderState::Suspended,
39            window_handle: None,
40            renderer,
41        }
42    }
43}
44
45impl<Renderer: ImageRenderer> WindowRenderer for PixelsWindowRenderer<Renderer> {
46    type ScenePainter<'a>
47        = <Renderer as ImageRenderer>::ScenePainter<'a>
48    where
49        Renderer: '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 surface = SurfaceTexture::new(width, height, window_handle.clone());
57        let mut pixels = Pixels::new(width, height, surface).unwrap();
58        pixels.enable_vsync(true);
59        pixels.clear_color(Color {
60            r: 1.0,
61            g: 1.0,
62            b: 1.0,
63            a: 1.0,
64        });
65        self.render_state = RenderState::Active(ActiveRenderState { pixels });
66        self.window_handle = Some(window_handle);
67
68        self.set_size(width, height);
69    }
70
71    fn suspend(&mut self) {
72        self.render_state = RenderState::Suspended;
73    }
74
75    fn set_size(&mut self, physical_width: u32, physical_height: u32) {
76        if let RenderState::Active(state) = &mut self.render_state {
77            state
78                .pixels
79                .resize_buffer(physical_width, physical_height)
80                .unwrap();
81            state
82                .pixels
83                .resize_surface(physical_width, physical_height)
84                .unwrap();
85            self.renderer.resize(physical_width, physical_height);
86        };
87    }
88
89    fn render<F: FnOnce(&mut Renderer::ScenePainter<'_>)>(&mut self, draw_fn: F) {
90        let RenderState::Active(state) = &mut self.render_state else {
91            return;
92        };
93
94        debug_timer!(timer, feature = "log_frame_times");
95
96        // Paint
97        self.renderer.render(draw_fn, state.pixels.frame_mut());
98        timer.record_time("render");
99
100        state.pixels.render().unwrap();
101        timer.record_time("present");
102        timer.print_times("pixels: ");
103
104        // Reset the renderer ready for the next render
105        self.renderer.reset();
106    }
107}