Skip to main content

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