Skip to main content

freya_winit/drivers/
software.rs

1use std::num::NonZeroU32;
2
3use freya_engine::prelude::{
4    AlphaType,
5    ColorType,
6    ImageInfo,
7    Surface as SkiaSurface,
8    wrap_pixels,
9};
10use raw_window_handle::{
11    DisplayHandle,
12    HandleError,
13    HasDisplayHandle,
14    HasWindowHandle,
15    RawDisplayHandle,
16    RawWindowHandle,
17    WindowHandle,
18};
19use winit::{
20    dpi::PhysicalSize,
21    event_loop::ActiveEventLoop,
22    window::{
23        Window,
24        WindowAttributes,
25    },
26};
27
28struct DisplayHandleWrapper(RawDisplayHandle);
29
30impl HasDisplayHandle for DisplayHandleWrapper {
31    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
32        // SAFETY: the window owning this handle outlives the driver.
33        Ok(unsafe { DisplayHandle::borrow_raw(self.0) })
34    }
35}
36
37struct WindowHandleWrapper(RawWindowHandle);
38
39impl HasWindowHandle for WindowHandleWrapper {
40    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
41        // SAFETY: the window owning this handle outlives the driver.
42        Ok(unsafe { WindowHandle::borrow_raw(self.0) })
43    }
44}
45
46/// Graphics driver that renders in software via Skia and presents through softbuffer.
47pub struct SoftwareDriver {
48    _context: softbuffer::Context<DisplayHandleWrapper>,
49    surface: softbuffer::Surface<DisplayHandleWrapper, WindowHandleWrapper>,
50}
51
52impl SoftwareDriver {
53    pub fn new(
54        event_loop: &ActiveEventLoop,
55        window_attributes: WindowAttributes,
56    ) -> Result<(Self, Window), Box<dyn std::error::Error>> {
57        let window = event_loop.create_window(window_attributes)?;
58
59        let display_handle = window.display_handle()?.as_raw();
60        let window_handle = window.window_handle()?.as_raw();
61
62        let context = softbuffer::Context::new(DisplayHandleWrapper(display_handle))
63            .map_err(|err| format!("Could not create softbuffer context: {err}"))?;
64        let mut surface = softbuffer::Surface::new(&context, WindowHandleWrapper(window_handle))
65            .map_err(|err| format!("Could not create softbuffer surface: {err}"))?;
66
67        let size = window.inner_size();
68        if let (Some(width), Some(height)) =
69            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
70        {
71            surface
72                .resize(width, height)
73                .map_err(|err| format!("Could not size softbuffer surface: {err}"))?;
74        }
75
76        Ok((
77            Self {
78                _context: context,
79                surface,
80            },
81            window,
82        ))
83    }
84
85    pub fn present(
86        &mut self,
87        size: PhysicalSize<u32>,
88        window: &Window,
89        render: impl FnOnce(&mut SkiaSurface),
90    ) {
91        let (Some(width), Some(height)) =
92            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
93        else {
94            return;
95        };
96
97        let mut buffer = match self.surface.buffer_mut() {
98            Ok(buffer) => buffer,
99            Err(err) => {
100                tracing::error!("Failed to acquire software buffer: {err:?}");
101                return;
102            }
103        };
104
105        let info = ImageInfo::new(
106            (width.get() as i32, height.get() as i32),
107            ColorType::BGRA8888,
108            AlphaType::Premul,
109            None,
110        );
111        let row_bytes = width.get() as usize * 4;
112        let pixels: &mut [u32] = &mut buffer;
113        // SAFETY: u32 is 4-aligned and we own the buffer for the full borrow.
114        let bytes: &mut [u8] = unsafe {
115            std::slice::from_raw_parts_mut(pixels.as_mut_ptr() as *mut u8, pixels.len() * 4)
116        };
117
118        match wrap_pixels(&info, bytes, Some(row_bytes), None) {
119            Some(mut wrapped_surface) => render(&mut wrapped_surface),
120            None => {
121                tracing::error!("Failed to wrap software pixels into a Skia surface");
122                return;
123            }
124        }
125
126        window.pre_present_notify();
127        if let Err(err) = buffer.present() {
128            tracing::error!("Failed to present software buffer: {err:?}");
129        }
130    }
131
132    pub fn resize(&mut self, size: PhysicalSize<u32>) {
133        let (Some(width), Some(height)) =
134            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
135        else {
136            return;
137        };
138        if let Err(err) = self.surface.resize(width, height) {
139            tracing::error!("Failed to resize software surface: {err:?}");
140        }
141    }
142}