diff --git a/internal/backends/linuxkms/display/swdisplay.rs b/internal/backends/linuxkms/display/swdisplay.rs index ef772c7ad36..070f5ce4248 100644 --- a/internal/backends/linuxkms/display/swdisplay.rs +++ b/internal/backends/linuxkms/display/swdisplay.rs @@ -9,7 +9,11 @@ pub trait SoftwareBufferDisplay { fn size(&self) -> (u32, u32); fn map_back_buffer( &self, - callback: &mut dyn FnMut(&'_ mut [u8], u8) -> Result<(), PlatformError>, + callback: &mut dyn FnMut( + &'_ mut [u8], + u8, + drm::buffer::DrmFourcc, + ) -> Result<(), PlatformError>, ) -> Result<(), PlatformError>; fn as_presenter(self: Rc) -> Rc; } diff --git a/internal/backends/linuxkms/display/swdisplay/dumbbuffer.rs b/internal/backends/linuxkms/display/swdisplay/dumbbuffer.rs index 98d307a9e7d..3e30fbbd640 100644 --- a/internal/backends/linuxkms/display/swdisplay/dumbbuffer.rs +++ b/internal/backends/linuxkms/display/swdisplay/dumbbuffer.rs @@ -22,8 +22,16 @@ impl DumbBufferDisplay { //eprintln!("mode {}/{}", width, height); - let front_buffer = DumbBuffer::allocate(&drm_output.drm_device, drm_output.size())?.into(); - let back_buffer = DumbBuffer::allocate(&drm_output.drm_device, drm_output.size())?.into(); + let front_buffer: RefCell = + DumbBuffer::allocate(&drm_output.drm_device, drm_output.size())?.into(); + let back_buffer = DumbBuffer::allocate_with_format( + &drm_output.drm_device, + drm_output.size(), + front_buffer.borrow().format, + front_buffer.borrow().depth, + front_buffer.borrow().bpp, + )? + .into(); Ok(Rc::new(Self { drm_output, front_buffer, back_buffer })) } @@ -36,15 +44,20 @@ impl super::SoftwareBufferDisplay for DumbBufferDisplay { fn map_back_buffer( &self, - callback: &mut dyn FnMut(&'_ mut [u8], u8) -> Result<(), PlatformError>, + callback: &mut dyn FnMut( + &'_ mut [u8], + u8, + drm::buffer::DrmFourcc, + ) -> Result<(), PlatformError>, ) -> Result<(), PlatformError> { let mut back_buffer = self.back_buffer.borrow_mut(); let age = back_buffer.age; + let format = back_buffer.format; self.drm_output .drm_device .map_dumb_buffer(&mut back_buffer.buffer_handle) .map_err(|e| PlatformError::Other(format!("Error mapping dumb buffer: {e}").into())) - .and_then(|mut buffer| callback(buffer.as_mut(), age)) + .and_then(|mut buffer| callback(buffer.as_mut(), age, format)) } fn as_presenter(self: Rc) -> Rc { @@ -90,6 +103,9 @@ struct DumbBuffer { fb_handle: drm::control::framebuffer::Handle, buffer_handle: drm::control::dumbbuffer::DumbBuffer, age: u8, + format: drm::buffer::DrmFourcc, + depth: u32, + bpp: u32, } impl DumbBuffer { @@ -97,13 +113,37 @@ impl DumbBuffer { device: &impl drm::control::Device, (width, height): (u32, u32), ) -> Result { - let buffer_handle = device - .create_dumb_buffer((width, height), drm::buffer::DrmFourcc::Xrgb8888, 32) - .map_err(|e| format!("Error creating dumb buffer ({}/{}): {}", width, height, e))?; - let fb_handle = device - .add_framebuffer(&buffer_handle, 24, 32) - .map_err(|e| format!("Error creating framebuffer for dumb buffer: {e}"))?; - - Ok(Self { fb_handle, buffer_handle, age: 0 }) + let mut last_err = None; + for (format, depth, bpp) in + [(drm::buffer::DrmFourcc::Xrgb8888, 24, 32), (drm::buffer::DrmFourcc::Rgb565, 16, 16)] + { + match Self::allocate_with_format(device, (width, height), format, depth, bpp) { + Ok(buf) => return Ok(buf), + Err(err) => last_err = Some(err), + } + } + + Err(last_err.unwrap_or_else(|| "Could not allocate drm dumb buffer".into())) + } + + fn allocate_with_format( + device: &impl drm::control::Device, + (width, height): (u32, u32), + format: drm::buffer::DrmFourcc, + depth: u32, + bpp: u32, + ) -> Result { + let buffer_handle = + device.create_dumb_buffer((width, height), format, bpp).map_err(|e| { + format!( + "Error creating dumb buffer ({}/{}): {} for format {format}", + width, height, e + ) + })?; + let fb_handle = device.add_framebuffer(&buffer_handle, depth, bpp).map_err(|e| { + format!("Error creating framebuffer for dumb buffer for format {format}: {e}") + })?; + + Ok(Self { fb_handle, buffer_handle, age: 0, format, depth, bpp }) } } diff --git a/internal/backends/linuxkms/display/swdisplay/linuxfb.rs b/internal/backends/linuxkms/display/swdisplay/linuxfb.rs index 5b852d1c1b1..5d9c8f38d91 100644 --- a/internal/backends/linuxkms/display/swdisplay/linuxfb.rs +++ b/internal/backends/linuxkms/display/swdisplay/linuxfb.rs @@ -14,6 +14,7 @@ pub struct LinuxFBDisplay { height: u32, presenter: Rc, first_frame: Cell, + format: drm::buffer::DrmFourcc, } impl LinuxFBDisplay { @@ -55,8 +56,18 @@ impl LinuxFBDisplay { finfo }; - if vinfo.bits_per_pixel != 32 { - return Err(format!("Error using linux framebuffer: Only 32-bpp framebuffers are supported right now, found {}", vinfo.bits_per_pixel).into()); + let format = if vinfo.bits_per_pixel == 32 { + drm::buffer::DrmFourcc::Xrgb8888 + } else if vinfo.bits_per_pixel == 16 { + if vinfo.red != RGB565_EXPECTED_RED_CHANNEL + || vinfo.green != RGB565_EXPECTED_GREEN_CHANNEL + || vinfo.blue != RGB565_EXPECTED_BLUE_CHANNEL + { + return Err(format!("Error using linux framebuffer: 16-bpp framebuffer does not have expected 565 format. Found {}/{}/{}", vinfo.red.length, vinfo.green.length, vinfo.blue.length).into()); + } + drm::buffer::DrmFourcc::Rgb565 + } else { + return Err(format!("Error using linux framebuffer: Only 32- and 16-bpp framebuffers are supported right now, found {}", vinfo.bits_per_pixel).into()); }; let bpp = vinfo.bits_per_pixel / 8; @@ -89,6 +100,7 @@ impl LinuxFBDisplay { height, presenter: crate::display::timeranimations::TimerBasedAnimationDriver::new(), first_frame: Cell::new(true), + format, })) } } @@ -100,11 +112,15 @@ impl super::SoftwareBufferDisplay for LinuxFBDisplay { fn map_back_buffer( &self, - callback: &mut dyn FnMut(&'_ mut [u8], u8) -> Result<(), PlatformError>, + callback: &mut dyn FnMut( + &'_ mut [u8], + u8, + drm::buffer::DrmFourcc, + ) -> Result<(), PlatformError>, ) -> Result<(), PlatformError> { let age = if self.first_frame.get() { 0 } else { 1 }; self.first_frame.set(false); - callback(self.back_buffer.borrow_mut().as_mut(), age)?; + callback(self.back_buffer.borrow_mut().as_mut(), age, self.format)?; let mut fb = self.fb.borrow_mut(); fb.as_mut().copy_from_slice(&self.back_buffer.borrow()); @@ -116,10 +132,19 @@ impl super::SoftwareBufferDisplay for LinuxFBDisplay { } } +const RGB565_EXPECTED_RED_CHANNEL: fb_bitfield = + fb_bitfield { offset: 11, length: 5, msb_right: 0 }; + +const RGB565_EXPECTED_GREEN_CHANNEL: fb_bitfield = + fb_bitfield { offset: 5, length: 6, msb_right: 0 }; + +const RGB565_EXPECTED_BLUE_CHANNEL: fb_bitfield = + fb_bitfield { offset: 0, length: 5, msb_right: 0 }; + const FBIOGET_VSCREENINFO: u32 = 0x4600; #[repr(C)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] #[allow(non_camel_case_types)] pub struct fb_bitfield { diff --git a/internal/backends/linuxkms/renderer/skia.rs b/internal/backends/linuxkms/renderer/skia.rs index cda6750bf89..b8d3a8d476c 100644 --- a/internal/backends/linuxkms/renderer/skia.rs +++ b/internal/backends/linuxkms/renderer/skia.rs @@ -184,8 +184,22 @@ impl i_slint_renderer_skia::software_surface::RenderBuffer for DrmDumbBufferAcce return Ok(()); }; - self.display.map_back_buffer(&mut |pixels, _age| { - render_callback(width, height, skia_safe::ColorType::BGRA8888, pixels.as_mut()) + self.display.map_back_buffer(&mut |pixels, _age, format| { + render_callback( + width, + height, + match format { + drm::buffer::DrmFourcc::Xrgb8888 => skia_safe::ColorType::BGRA8888, + drm::buffer::DrmFourcc::Rgb565 => skia_safe::ColorType::RGB565, + _ => { + return Err(format!( + "Unsupported frame buffer format {format} used with skia software renderer" + ) + .into()) + } + }, + pixels.as_mut(), + ) }) } } diff --git a/internal/backends/linuxkms/renderer/sw.rs b/internal/backends/linuxkms/renderer/sw.rs index 8bf602897c1..63f0f8af459 100644 --- a/internal/backends/linuxkms/renderer/sw.rs +++ b/internal/backends/linuxkms/renderer/sw.rs @@ -20,11 +20,11 @@ pub struct SoftwareRendererAdapter { #[repr(transparent)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct DumbBufferPixel(pub u32); +struct DumbBufferPixelXrgb888(pub u32); -impl From for PremultipliedRgbaColor { +impl From for PremultipliedRgbaColor { #[inline] - fn from(pixel: DumbBufferPixel) -> Self { + fn from(pixel: DumbBufferPixelXrgb888) -> Self { let v = pixel.0; PremultipliedRgbaColor { red: (v >> 16) as u8, @@ -35,7 +35,7 @@ impl From for PremultipliedRgbaColor { } } -impl From for DumbBufferPixel { +impl From for DumbBufferPixelXrgb888 { #[inline] fn from(pixel: PremultipliedRgbaColor) -> Self { Self( @@ -47,7 +47,7 @@ impl From for DumbBufferPixel { } } -impl TargetPixel for DumbBufferPixel { +impl TargetPixel for DumbBufferPixelXrgb888 { fn blend(&mut self, color: PremultipliedRgbaColor) { let mut x = PremultipliedRgbaColor::from(*self); x.blend(color); @@ -100,7 +100,7 @@ impl crate::fullscreenwindowadapter::FullscreenRenderer for SoftwareRendererAdap _draw_mouse_cursor_callback: &dyn Fn(&mut dyn i_slint_core::item_rendering::ItemRenderer), ready_for_next_animation_frame: Box, ) -> Result<(), PlatformError> { - self.display.map_back_buffer(&mut |pixels, age| { + self.display.map_back_buffer(&mut |pixels, age, format| { self.renderer.set_repaint_buffer_type(match age { 1 => RepaintBufferType::ReusedBuffer, 2 => RepaintBufferType::SwappedBuffers, @@ -122,8 +122,24 @@ impl crate::fullscreenwindowadapter::FullscreenRenderer for SoftwareRendererAdap } }); - let buffer: &mut [DumbBufferPixel] = bytemuck::cast_slice_mut(pixels.as_mut()); - self.renderer.render(buffer, self.size.width as usize); + match format { + drm::buffer::DrmFourcc::Xrgb8888 => { + let buffer: &mut [DumbBufferPixelXrgb888] = + bytemuck::cast_slice_mut(pixels.as_mut()); + self.renderer.render(buffer, self.size.width as usize); + } + drm::buffer::DrmFourcc::Rgb565 => { + let buffer: &mut [i_slint_core::software_renderer::Rgb565Pixel] = + bytemuck::cast_slice_mut(pixels.as_mut()); + self.renderer.render(buffer, self.size.width as usize); + } + _ => { + return Err(format!( + "Unsupported frame buffer format {format} used with software renderer" + ) + .into()) + } + } Ok(()) })?;