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, 'active_event_loop> M:
48        Middleware<
49                PixelsInit<'init>,
50                PixelsContext<'context>,
51                PixelsSurface<'surface, 'w>,
52                PixelsEvent,
53                PixelsEventContext<'event_context, 'w, 'active_event_loop>,
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, 'active_event_loop> M:
171        Middleware<
172                PixelsInit<'init>,
173                PixelsContext<'control>,
174                PixelsSurface<'surface, 'w>,
175                PixelsEvent,
176                PixelsEventContext<'event_context, 'w, 'active_event_loop>,
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 mut context = PixelsEventContext {
203                surface,
204                event_loop,
205            };
206
207            if let Some(event) = self
208                .middleware
209                .on_event(PixelsEvent::Window(event), &mut context)
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 mut context = PixelsEventContext {
234                surface,
235                event_loop,
236            };
237            let _ = self
238                .middleware
239                .on_event(PixelsEvent::Device(device_id, event), &mut context);
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, 'a> {
341    surface: PixelsSurface<'s, 'w>,
342    event_loop: &'a ActiveEventLoop,
343}
344
345impl EventContext<PhysicalPosition<f64>> for PixelsEventContext<'_, '_, '_> {
346    type SurfaceSpace = Option<PhysicalPosition<u32>>;
347
348    fn estimate_surface_space(&self, event_space: PhysicalPosition<f64>) -> Self::SurfaceSpace {
349        let PhysicalPosition { x, y } = event_space;
350        let (x, y) = (x as f32, y as f32);
351        self.surface
352            .pixels
353            .window_pos_to_pixel((x, y))
354            .ok()
355            .map(|(x, y)| PhysicalPosition::new(x as _, y as _))
356    }
357}
358
359impl PixelsEventContext<'_, '_, '_> {
360    /// Request event loop termination.
361    pub fn shutdown(&self) {
362        self.event_loop.exit();
363    }
364}
365
366/// Render surface implementation.
367pub struct PixelsSurface<'s, 'w> {
368    pixels: &'s mut pixels::Pixels<'w>,
369    dimensions: PhysicalSize<u32>,
370}
371
372impl Surface for PixelsSurface<'_, '_> {
373    type Texel = [u8; 4];
374
375    fn texel(&self, x: u32, y: u32) -> Option<[u8; 4]> {
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 set_texel(&mut self, x: u32, y: u32, value: [u8; 4]) {
387        if x < self.dimensions.width && y < self.dimensions.height {
388            let buffer = self.pixels.frame_mut();
389            let offset = (4 * (x + y * self.dimensions.width)) as usize;
390            let slice = &mut buffer[offset..(offset + 4)];
391            slice.copy_from_slice(&value);
392        }
393    }
394
395    fn clear(&mut self, value: Self::Texel) {
396        let frame = self.pixels.frame_mut();
397        for pixel in frame.chunks_exact_mut(4) {
398            pixel.copy_from_slice(&value);
399        }
400    }
401
402    fn width(&self) -> u32 {
403        self.dimensions.width
404    }
405
406    fn height(&self) -> u32 {
407        self.dimensions.height
408    }
409}
410
411impl<I> Fill<I> for PixelsSurface<'_, '_>
412where
413    I: Iterator,
414    <I as Iterator>::Item: Iterator<Item = [u8; 4]>,
415{
416    fn fill_from(&mut self, data: I) {
417        let data = data.flatten();
418
419        let frame = self.pixels.frame_mut();
420        for (texel, data) in frame.chunks_exact_mut(4).zip(data) {
421            texel.copy_from_slice(&data);
422        }
423    }
424}
425
426/// An error generalization.
427#[derive(Debug)]
428pub enum Error {
429    /// Winit event loop error.
430    WinitEventLoopError(EventLoopError),
431    /// Os error.
432    OsError(OsError),
433    /// Pixels error.
434    PixelsError(pixels::Error),
435}
436
437impl From<EventLoopError> for Error {
438    fn from(value: EventLoopError) -> Self {
439        Self::WinitEventLoopError(value)
440    }
441}
442
443impl From<OsError> for Error {
444    fn from(value: OsError) -> Self {
445        Self::OsError(value)
446    }
447}
448
449impl From<pixels::Error> for Error {
450    fn from(value: pixels::Error) -> Self {
451        Self::PixelsError(value)
452    }
453}