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        let driver = Self::from_window(&window)?;
59        Ok((driver, window))
60    }
61
62    /// Build the driver on an existing window instead of creating a new one.
63    pub fn from_window(window: &Window) -> Result<Self, Box<dyn std::error::Error>> {
64        let display_handle = window.display_handle()?.as_raw();
65        let window_handle = window.window_handle()?.as_raw();
66
67        let context = softbuffer::Context::new(DisplayHandleWrapper(display_handle))
68            .map_err(|err| format!("Could not create softbuffer context: {err}"))?;
69        let mut surface = softbuffer::Surface::new(&context, WindowHandleWrapper(window_handle))
70            .map_err(|err| format!("Could not create softbuffer surface: {err}"))?;
71
72        let size = window.inner_size();
73        if let (Some(width), Some(height)) =
74            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
75        {
76            surface
77                .resize(width, height)
78                .map_err(|err| format!("Could not size softbuffer surface: {err}"))?;
79        }
80
81        Ok(Self {
82            _context: context,
83            surface,
84        })
85    }
86
87    pub fn present(
88        &mut self,
89        size: PhysicalSize<u32>,
90        window: &Window,
91        render: impl FnOnce(&mut SkiaSurface),
92    ) {
93        let (Some(width), Some(height)) =
94            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
95        else {
96            return;
97        };
98
99        let mut buffer = match self.surface.buffer_mut() {
100            Ok(buffer) => buffer,
101            Err(err) => {
102                tracing::error!("Failed to acquire software buffer: {err:?}");
103                return;
104            }
105        };
106
107        let info = ImageInfo::new(
108            (width.get() as i32, height.get() as i32),
109            ColorType::BGRA8888,
110            AlphaType::Premul,
111            None,
112        );
113        let row_bytes = width.get() as usize * 4;
114        let pixels: &mut [u32] = &mut buffer;
115        // SAFETY: u32 is 4-aligned and we own the buffer for the full borrow.
116        let bytes: &mut [u8] = unsafe {
117            std::slice::from_raw_parts_mut(pixels.as_mut_ptr() as *mut u8, pixels.len() * 4)
118        };
119
120        match wrap_pixels(&info, bytes, Some(row_bytes), None) {
121            Some(mut wrapped_surface) => render(&mut wrapped_surface),
122            None => {
123                tracing::error!("Failed to wrap software pixels into a Skia surface");
124                return;
125            }
126        }
127
128        window.pre_present_notify();
129        if let Err(err) = buffer.present() {
130            tracing::error!("Failed to present software buffer: {err:?}");
131        }
132    }
133
134    pub fn resize(&mut self, size: PhysicalSize<u32>) {
135        let (Some(width), Some(height)) =
136            (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
137        else {
138            return;
139        };
140        if let Err(err) = self.surface.resize(width, height) {
141            tracing::error!("Failed to resize software surface: {err:?}");
142        }
143    }
144}