Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RGB565 linux framebuffer rendering #5567

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion internal/backends/linuxkms/display/swdisplay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self>) -> Rc<dyn super::Presenter>;
}
Expand Down
64 changes: 52 additions & 12 deletions internal/backends/linuxkms/display/swdisplay/dumbbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> =
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 }))
}
Expand All @@ -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<Self>) -> Rc<dyn crate::display::Presenter> {
Expand Down Expand Up @@ -90,20 +103,47 @@ 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 {
fn allocate(
device: &impl drm::control::Device,
(width, height): (u32, u32),
) -> Result<Self, PlatformError> {
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<Self, PlatformError> {
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 })
}
}
35 changes: 30 additions & 5 deletions internal/backends/linuxkms/display/swdisplay/linuxfb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct LinuxFBDisplay {
height: u32,
presenter: Rc<crate::display::timeranimations::TimerBasedAnimationDriver>,
first_frame: Cell<bool>,
format: drm::buffer::DrmFourcc,
}

impl LinuxFBDisplay {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -89,6 +100,7 @@ impl LinuxFBDisplay {
height,
presenter: crate::display::timeranimations::TimerBasedAnimationDriver::new(),
first_frame: Cell::new(true),
format,
}))
}
}
Expand All @@ -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());
Expand All @@ -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 {
Expand Down
18 changes: 16 additions & 2 deletions internal/backends/linuxkms/renderer/skia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
})
}
}
32 changes: 24 additions & 8 deletions internal/backends/linuxkms/renderer/sw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DumbBufferPixel> for PremultipliedRgbaColor {
impl From<DumbBufferPixelXrgb888> for PremultipliedRgbaColor {
#[inline]
fn from(pixel: DumbBufferPixel) -> Self {
fn from(pixel: DumbBufferPixelXrgb888) -> Self {
let v = pixel.0;
PremultipliedRgbaColor {
red: (v >> 16) as u8,
Expand All @@ -35,7 +35,7 @@ impl From<DumbBufferPixel> for PremultipliedRgbaColor {
}
}

impl From<PremultipliedRgbaColor> for DumbBufferPixel {
impl From<PremultipliedRgbaColor> for DumbBufferPixelXrgb888 {
#[inline]
fn from(pixel: PremultipliedRgbaColor) -> Self {
Self(
Expand All @@ -47,7 +47,7 @@ impl From<PremultipliedRgbaColor> 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);
Expand Down Expand Up @@ -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<dyn FnOnce()>,
) -> 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,
Expand All @@ -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(())
})?;
Expand Down
Loading