Skip to main content

i_slint_backend_winit/renderer/
sw.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Delegate the rendering to the [`i_slint_renderer_software::SoftwareRenderer`]
5
6use core::num::NonZeroU32;
7use core::ops::DerefMut;
8use i_slint_core::graphics::Rgb8Pixel;
9use i_slint_core::platform::PlatformError;
10use i_slint_core::renderer::DrawOutcome;
11pub use i_slint_renderer_software::SoftwareRenderer;
12use i_slint_renderer_software::{PremultipliedRgbaColor, RepaintBufferType, TargetPixel};
13use std::cell::RefCell;
14use std::rc::Rc;
15use std::sync::Arc;
16use winit::event_loop::ActiveEventLoop;
17
18use super::WinitCompatibleRenderer;
19
20pub struct WinitSoftwareRenderer {
21    renderer: SoftwareRenderer,
22    _context: RefCell<Option<softbuffer::Context<Arc<winit::window::Window>>>>,
23    surface: RefCell<
24        Option<softbuffer::Surface<Arc<winit::window::Window>, Arc<winit::window::Window>>>,
25    >,
26}
27
28#[repr(transparent)]
29#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
30struct SoftBufferPixel(pub u32);
31
32impl From<SoftBufferPixel> for PremultipliedRgbaColor {
33    #[inline]
34    fn from(pixel: SoftBufferPixel) -> Self {
35        let v = pixel.0;
36        PremultipliedRgbaColor {
37            red: (v >> 16) as u8,
38            green: (v >> 8) as u8,
39            blue: v as u8,
40            alpha: (v >> 24) as u8,
41        }
42    }
43}
44
45impl From<PremultipliedRgbaColor> for SoftBufferPixel {
46    #[inline]
47    fn from(pixel: PremultipliedRgbaColor) -> Self {
48        Self(
49            (pixel.alpha as u32) << 24
50                | ((pixel.red as u32) << 16)
51                | ((pixel.green as u32) << 8)
52                | (pixel.blue as u32),
53        )
54    }
55}
56
57impl TargetPixel for SoftBufferPixel {
58    fn blend(&mut self, color: PremultipliedRgbaColor) {
59        let mut x = PremultipliedRgbaColor::from(*self);
60        x.blend(color);
61        *self = x.into();
62    }
63
64    fn from_rgb(r: u8, g: u8, b: u8) -> Self {
65        Self(0xff000000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
66    }
67
68    fn background() -> Self {
69        Self(0)
70    }
71}
72
73impl WinitSoftwareRenderer {
74    pub fn new_suspended(
75        _shared_backend_data: &Rc<crate::SharedBackendData>,
76    ) -> Result<Box<dyn WinitCompatibleRenderer>, PlatformError> {
77        Ok(Box::new(Self {
78            renderer: SoftwareRenderer::new(),
79            _context: RefCell::new(None),
80            surface: RefCell::new(None),
81        }))
82    }
83}
84
85impl super::WinitCompatibleRenderer for WinitSoftwareRenderer {
86    fn render(&self, window: &i_slint_core::api::Window) -> Result<DrawOutcome, PlatformError> {
87        let size = window.size();
88
89        let Some((width, height)) = size.width.try_into().ok().zip(size.height.try_into().ok())
90        else {
91            // Nothing to render
92            return Ok(DrawOutcome::Success);
93        };
94
95        let mut borrowed_surface = self.surface.borrow_mut();
96        let Some(surface) = borrowed_surface.as_mut() else {
97            // Nothing to render
98            return Ok(DrawOutcome::Success);
99        };
100
101        let winit_window = surface.window().clone();
102
103        surface
104            .resize(width, height)
105            .map_err(|e| format!("Error resizing softbuffer surface: {e}"))?;
106
107        let mut target_buffer = surface
108            .buffer_mut()
109            .map_err(|e| format!("Error retrieving softbuffer rendering buffer: {e}"))?;
110
111        let age = target_buffer.age();
112        self.renderer.set_repaint_buffer_type(match age {
113            1 => RepaintBufferType::ReusedBuffer,
114            2 => RepaintBufferType::SwappedBuffers,
115            _ => RepaintBufferType::NewBuffer,
116        });
117
118        let region = if std::env::var_os("SLINT_LINE_BY_LINE").is_none() {
119            let buffer: &mut [SoftBufferPixel] =
120                bytemuck::cast_slice_mut(target_buffer.deref_mut());
121            self.renderer.render(buffer, width.get() as usize)
122        } else {
123            // SLINT_LINE_BY_LINE is set and this is a debug mode where we also render in a Rgb565Pixel
124            struct FrameBuffer<'a> {
125                buffer: &'a mut [u32],
126                line: Vec<i_slint_renderer_software::Rgb565Pixel>,
127            }
128            impl i_slint_renderer_software::LineBufferProvider for FrameBuffer<'_> {
129                type TargetPixel = i_slint_renderer_software::Rgb565Pixel;
130                fn process_line(
131                    &mut self,
132                    line: usize,
133                    range: core::ops::Range<usize>,
134                    render_fn: impl FnOnce(&mut [Self::TargetPixel]),
135                ) {
136                    let line_begin = line * self.line.len();
137                    let sub = &mut self.line[..range.len()];
138                    render_fn(sub);
139                    for (dst, src) in self.buffer[line_begin..][range].iter_mut().zip(sub) {
140                        let p = Rgb8Pixel::from(*src);
141                        *dst =
142                            0xff000000 | ((p.r as u32) << 16) | ((p.g as u32) << 8) | (p.b as u32);
143                    }
144                }
145            }
146            self.renderer.render_by_line(FrameBuffer {
147                buffer: &mut target_buffer,
148                line: vec![Default::default(); width.get() as usize],
149            })
150        };
151
152        let size = region.bounding_box_size();
153        if let Some((w, h)) = Option::zip(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
154        {
155            winit_window.pre_present_notify();
156            let pos = region.bounding_box_origin();
157            target_buffer
158                .present_with_damage(&[softbuffer::Rect {
159                    width: w,
160                    height: h,
161                    x: pos.x as u32,
162                    y: pos.y as u32,
163                }])
164                .map_err(|e| format!("Error presenting softbuffer buffer: {e}"))?;
165        }
166        Ok(DrawOutcome::Success)
167    }
168
169    fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer {
170        &self.renderer
171    }
172
173    fn occluded(&self, _: bool) {
174        // On X11 and Windows, the buffer is completely cleared when the window is hidden
175        // and the buffer age doesn't respect that, so clean the partial rendering cache
176        self.renderer.set_repaint_buffer_type(RepaintBufferType::NewBuffer);
177    }
178
179    fn resume(
180        &self,
181        active_event_loop: &ActiveEventLoop,
182        window_attributes: winit::window::WindowAttributes,
183    ) -> Result<Arc<winit::window::Window>, PlatformError> {
184        let winit_window =
185            active_event_loop.create_window(window_attributes).map_err(|winit_os_error| {
186                PlatformError::from(format!(
187                    "Error creating native window for software rendering: {winit_os_error}"
188                ))
189            })?;
190        let winit_window = Arc::new(winit_window);
191
192        let context = softbuffer::Context::new(winit_window.clone())
193            .map_err(|e| format!("Error creating softbuffer context: {e}"))?;
194
195        let surface = softbuffer::Surface::new(&context, winit_window.clone()).map_err(
196            |softbuffer_error| format!("Error creating softbuffer surface: {softbuffer_error}"),
197        )?;
198
199        *self._context.borrow_mut() = Some(context);
200        *self.surface.borrow_mut() = Some(surface);
201
202        Ok(winit_window)
203    }
204
205    fn suspend(&self) -> Result<(), PlatformError> {
206        drop(self.surface.borrow_mut().take());
207        drop(self._context.borrow_mut().take());
208        Ok(())
209    }
210}