devotee_backend_pixels/
lib.rs

1#![deny(missing_docs)]
2
3//! [Pixels](https://crates.io/crates/pixels)-based backend for the devotee project.
4
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7
8use devotee_backend::Middleware;
9use devotee_backend::middling::{
10    EventContext, Fill, Surface, TexelDesignatorMut, TexelDesignatorRef, TexelMut, TexelRef,
11};
12use winit::application::ApplicationHandler;
13use winit::dpi::{PhysicalPosition, PhysicalSize};
14use winit::error::{EventLoopError, OsError};
15use winit::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
16use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
17use winit::window::{Window, WindowAttributes, WindowId};
18
19pub use winit;
20
21/// A [Pixels](https://crates.io/crates/pixels)-based backend implementation for the devotee project.
22pub struct PixelsBackend<'w, M> {
23    middleware: M,
24    internal: Option<Internal<'w>>,
25    settings: Settings,
26    last: Instant,
27}
28
29impl<M> PixelsBackend<'_, M> {
30    /// Create new backend instance.
31    pub fn new(middleware: M) -> Self {
32        let internal = None;
33        let last = Instant::now();
34        let settings = Settings {
35            render_window_size: PhysicalSize::new(32, 32),
36            updates_per_second: 60.0,
37        };
38        Self {
39            middleware,
40            internal,
41            settings,
42            last,
43        }
44    }
45}
46
47impl<'w, M> PixelsBackend<'w, M>
48where
49    for<'init, 'context, 'surface, 'event_context, 'event_control> M: Middleware<
50            PixelsInit<'init>,
51            PixelsContext<'context>,
52            PixelsSurface<'surface, 'w>,
53            PixelsEvent,
54            PixelsEventContext<'event_context, 'w>,
55            PixelsEventControl<'event_control>,
56        >,
57{
58    /// Run this backend to completion.
59    pub fn run(&mut self) -> Result<(), Error> {
60        let event_loop = EventLoop::new()?;
61
62        event_loop.set_control_flow(ControlFlow::WaitUntil(
63            Instant::now() + Duration::from_secs_f32(1.0 / 60.0),
64        ));
65        event_loop.run_app(self)?;
66
67        Ok(())
68    }
69
70    fn init(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Error> {
71        let window = Arc::new(event_loop.create_window(WindowAttributes::default())?);
72
73        let mut init = PixelsInit {
74            window: &window,
75            settings: &mut self.settings,
76        };
77
78        window.set_visible(true);
79        self.middleware.on_init(&mut init);
80        window.set_min_inner_size(Some(self.settings.render_window_size));
81
82        let surface_size = window.inner_size();
83        let surface_texture =
84            pixels::SurfaceTexture::new(surface_size.width, surface_size.height, window.clone());
85
86        let pixels = pixels::PixelsBuilder::new(
87            self.settings.render_window_size.width,
88            self.settings.render_window_size.height,
89            surface_texture,
90        )
91        .build()?;
92
93        let mut internal = Internal {
94            window,
95            pixels,
96            surface_size,
97        };
98        let _ = internal.on_resize(surface_size);
99
100        self.internal = Some(internal);
101
102        Ok(())
103    }
104
105    fn handle_update(&mut self, event_loop: &ActiveEventLoop) {
106        let now = Instant::now();
107        if let Some(internal) = &self.internal {
108            let delta = now - self.last;
109
110            let mut control = PixelsContext {
111                shutdown: false,
112                window: &internal.window,
113                delta,
114            };
115            self.middleware.on_update(&mut control);
116
117            if control.shutdown {
118                event_loop.exit();
119            }
120        }
121        self.last = now;
122        event_loop.set_control_flow(ControlFlow::WaitUntil(
123            now + Duration::from_secs_f32(1.0 / self.settings.updates_per_second),
124        ));
125    }
126
127    fn handle_window_event(
128        settings: &Settings,
129        event_loop: &ActiveEventLoop,
130        middleware: &mut M,
131        internal: &mut Internal<'w>,
132        event: WindowEvent,
133    ) {
134        match event {
135            WindowEvent::Resized(physical_size) => {
136                internal.on_resize(physical_size);
137            }
138            WindowEvent::CloseRequested => {
139                event_loop.exit();
140            }
141            WindowEvent::Destroyed => {
142                event_loop.exit();
143            }
144            WindowEvent::RedrawRequested => {
145                let mut surface = PixelsSurface {
146                    pixels: &mut internal.pixels,
147                    dimensions: settings.render_window_size,
148                };
149                middleware.on_render(&mut surface);
150                let _ = internal.pixels.render();
151                internal.window.request_redraw();
152            }
153            _ => {}
154        }
155    }
156
157    fn handle_event(
158        settings: &Settings,
159        event_loop: &ActiveEventLoop,
160        middleware: &mut M,
161        internal: &mut Internal<'w>,
162        event: PixelsEvent,
163    ) {
164        if let PixelsEvent::Window(event) = event {
165            Self::handle_window_event(settings, event_loop, middleware, internal, event)
166        }
167    }
168}
169
170impl<'w, M> ApplicationHandler for PixelsBackend<'w, M>
171where
172    for<'init, 'control, 'surface, 'event_context, 'event_control> M: Middleware<
173            PixelsInit<'init>,
174            PixelsContext<'control>,
175            PixelsSurface<'surface, 'w>,
176            PixelsEvent,
177            PixelsEventContext<'event_context, 'w>,
178            PixelsEventControl<'event_control>,
179        >,
180{
181    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
182        if self.internal.is_none() && self.init(event_loop).is_err() {
183            event_loop.exit();
184        }
185    }
186
187    fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
188        if let StartCause::ResumeTimeReached { .. } = cause {
189            self.handle_update(event_loop);
190        }
191    }
192
193    fn window_event(
194        &mut self,
195        event_loop: &ActiveEventLoop,
196        _window_id: WindowId,
197        event: WindowEvent,
198    ) {
199        if let Some(internal) = &mut self.internal {
200            let surface = PixelsSurface {
201                pixels: &mut internal.pixels,
202                dimensions: self.settings.render_window_size,
203            };
204            let context = PixelsEventContext { surface };
205            let mut control = PixelsEventControl { event_loop };
206
207            if let Some(event) =
208                self.middleware
209                    .on_event(PixelsEvent::Window(event), &context, &mut control)
210            {
211                Self::handle_event(
212                    &self.settings,
213                    event_loop,
214                    &mut self.middleware,
215                    internal,
216                    event,
217                );
218            }
219        }
220    }
221
222    fn device_event(
223        &mut self,
224        event_loop: &ActiveEventLoop,
225        device_id: DeviceId,
226        event: DeviceEvent,
227    ) {
228        if let Some(internal) = &mut self.internal {
229            let surface = PixelsSurface {
230                pixels: &mut internal.pixels,
231                dimensions: self.settings.render_window_size,
232            };
233            let context = PixelsEventContext { surface };
234            let mut control = PixelsEventControl { event_loop };
235            let _ = self.middleware.on_event(
236                PixelsEvent::Device(device_id, event),
237                &context,
238                &mut control,
239            );
240        }
241    }
242}
243
244struct Settings {
245    render_window_size: PhysicalSize<u32>,
246    updates_per_second: f32,
247}
248
249impl Settings {
250    /// # Panics
251    ///
252    /// Panics if `updates_per_second` is less or equal to `0`.
253    fn set_updates_per_second(&mut self, updates_per_second: f32) {
254        assert!(
255            updates_per_second > 0.0,
256            "Update rate has to be greater than 0"
257        );
258        self.updates_per_second = updates_per_second;
259    }
260}
261
262struct Internal<'w> {
263    window: Arc<Window>,
264    pixels: pixels::Pixels<'w>,
265    surface_size: PhysicalSize<u32>,
266}
267
268impl Internal<'_> {
269    fn on_resize(&mut self, size: PhysicalSize<u32>) -> Option<()> {
270        self.pixels.resize_surface(size.width, size.height).ok()?;
271
272        self.surface_size = size;
273
274        Some(())
275    }
276}
277
278/// An initialization argument passed to the application.
279pub struct PixelsInit<'a> {
280    window: &'a Window,
281    settings: &'a mut Settings,
282}
283
284impl PixelsInit<'_> {
285    /// Get reference to the underlying `winit` `Window` reference.
286    pub fn window(&self) -> &Window {
287        self.window
288    }
289
290    /// Set the internal render window size.
291    pub fn set_render_window_size(&mut self, width: u32, height: u32) {
292        let size = PhysicalSize::new(width, height);
293        self.settings.render_window_size = size;
294        let _ = self.window.request_inner_size(size);
295    }
296
297    /// Set update framerate in updates per second count.
298    ///
299    /// # Panics
300    ///
301    /// Panics if `updates_per_second` is less or equal to `0`.
302    pub fn set_updates_per_second(&mut self, updates_per_second: f32) {
303        self.settings.set_updates_per_second(updates_per_second);
304    }
305}
306
307/// An update argument passed to the application.
308pub struct PixelsContext<'a> {
309    shutdown: bool,
310    window: &'a Window,
311    delta: Duration,
312}
313
314impl PixelsContext<'_> {
315    /// Get reference to the underlying `winit` `Window` reference.
316    pub fn window(&self) -> &Window {
317        self.window
318    }
319
320    /// Tell the backend to shut itself down.
321    pub fn shutdown(&mut self) {
322        self.shutdown = true;
323    }
324
325    /// Get delta time estimation.
326    pub fn delta(&self) -> Duration {
327        self.delta
328    }
329}
330
331/// Pixels-based backend event.
332pub enum PixelsEvent {
333    /// Winit window event.
334    Window(WindowEvent),
335    /// Winit device event.
336    Device(DeviceId, DeviceEvent),
337}
338
339/// A context passed to the event handler.
340pub struct PixelsEventContext<'s, 'w> {
341    surface: PixelsSurface<'s, 'w>,
342}
343
344impl EventContext<PhysicalPosition<f64>> for PixelsEventContext<'_, '_> {
345    type SurfaceSpace = Option<PhysicalPosition<u32>>;
346
347    fn estimate_surface_space(&self, event_space: PhysicalPosition<f64>) -> Self::SurfaceSpace {
348        let PhysicalPosition { x, y } = event_space;
349        let (x, y) = (x as f32, y as f32);
350        self.surface
351            .pixels
352            .window_pos_to_pixel((x, y))
353            .ok()
354            .map(|(x, y)| PhysicalPosition::new(x as _, y as _))
355    }
356}
357
358/// Render surface implementation.
359pub struct PixelsSurface<'s, 'w> {
360    pixels: &'s mut pixels::Pixels<'w>,
361    dimensions: PhysicalSize<u32>,
362}
363
364impl<'a> TexelDesignatorRef<'a> for PixelsSurface<'_, '_> {
365    type TexelRef = &'a [u8; 4];
366}
367
368impl<'a> TexelDesignatorMut<'a> for PixelsSurface<'_, '_> {
369    type TexelMut = &'a mut [u8; 4];
370}
371
372impl Surface for PixelsSurface<'_, '_> {
373    type Texel = [u8; 4];
374
375    fn texel(&self, x: u32, y: u32) -> Option<TexelRef<'_, Self>> {
376        if x >= self.dimensions.width || y >= self.dimensions.height {
377            None
378        } else {
379            let buffer = self.pixels.frame();
380            let offset = (4 * (x + y * self.dimensions.width)) as usize;
381            let slice = &buffer[offset..(offset + 4)];
382            slice.try_into().ok()
383        }
384    }
385
386    fn texel_mut(&mut self, x: u32, y: u32) -> Option<TexelMut<'_, Self>> {
387        if x >= self.dimensions.width || y >= self.dimensions.height {
388            None
389        } else {
390            let buffer = self.pixels.frame_mut();
391            let offset = (4 * (x + y * self.dimensions.width)) as usize;
392            let slice = &mut buffer[offset..(offset + 4)];
393            slice.try_into().ok()
394        }
395    }
396
397    unsafe fn unsafe_texel(&self, x: u32, y: u32) -> TexelRef<'_, Self> {
398        let buffer = self.pixels.frame();
399        let offset = (4 * (x + y * self.dimensions.width)) as usize;
400        let slice = &buffer[offset..(offset + 4)];
401        slice.try_into().unwrap()
402    }
403
404    unsafe fn unsafe_texel_mut(&mut self, x: u32, y: u32) -> TexelMut<'_, Self> {
405        let buffer = self.pixels.frame_mut();
406        let offset = (4 * (x + y * self.dimensions.width)) as usize;
407        let slice = &mut buffer[offset..(offset + 4)];
408        slice.try_into().unwrap()
409    }
410
411    fn clear(&mut self, value: Self::Texel) {
412        let frame = self.pixels.frame_mut();
413        for pixel in frame.chunks_exact_mut(4) {
414            pixel.copy_from_slice(&value);
415        }
416    }
417
418    fn width(&self) -> u32 {
419        self.dimensions.width
420    }
421
422    fn height(&self) -> u32 {
423        self.dimensions.height
424    }
425}
426
427impl Fill for PixelsSurface<'_, '_> {
428    fn fill_from(&mut self, data: &[Self::Texel]) {
429        let frame = self.pixels.frame_mut();
430        for (texel, data) in frame.chunks_exact_mut(4).zip(data) {
431            texel.copy_from_slice(data);
432        }
433    }
434}
435
436/// Event control structure.
437pub struct PixelsEventControl<'a> {
438    event_loop: &'a ActiveEventLoop,
439}
440
441impl PixelsEventControl<'_> {
442    /// Tell the backend to shut itself down.
443    pub fn shutdown(&self) {
444        self.event_loop.exit();
445    }
446}
447
448/// An error generalization.
449#[derive(Debug)]
450pub enum Error {
451    /// Winit event loop error.
452    WinitEventLoopError(EventLoopError),
453    /// Os error.
454    OsError(OsError),
455    /// Pixels error.
456    PixelsError(pixels::Error),
457}
458
459impl From<EventLoopError> for Error {
460    fn from(value: EventLoopError) -> Self {
461        Self::WinitEventLoopError(value)
462    }
463}
464
465impl From<OsError> for Error {
466    fn from(value: OsError) -> Self {
467        Self::OsError(value)
468    }
469}
470
471impl From<pixels::Error> for Error {
472    fn from(value: pixels::Error) -> Self {
473        Self::PixelsError(value)
474    }
475}