Merge branch 'gpu_drm34' into 'main'

drivers/graphics: Implement shadow buffer for dumb buffers

See merge request redox-os/base!190
This commit is contained in:
Jeremy Soller
2026-03-21 12:06:35 -06:00
7 changed files with 129 additions and 44 deletions
+35 -30
View File
@@ -5,9 +5,8 @@ use std::convert::{TryFrom, TryInto};
use std::{cmp, io, mem, ptr};
use drm::buffer::{Buffer, DrmFourcc};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::{connector, crtc, framebuffer, ClipRect, Device, Mode};
use graphics_ipc::V2GraphicsHandle;
use graphics_ipc::{CpuBackedBuffer, V2GraphicsHandle};
use orbclient::FONT;
#[derive(Debug, Copy, Clone)]
@@ -55,8 +54,7 @@ pub struct V2DisplayMap {
connector: connector::Handle,
crtc: crtc::Handle,
fb: framebuffer::Handle,
pub buffer: DumbBuffer,
mapping: DumbMapping<'static>,
pub buffer: CpuBackedBuffer,
}
impl V2DisplayMap {
@@ -75,40 +73,47 @@ impl V2DisplayMap {
.possible_crtcs(),
)[0];
let mut buffer = display_handle.create_dumb_buffer(
let buffer = CpuBackedBuffer::new(
&display_handle,
(width.into(), height.into()),
DrmFourcc::Argb8888,
32,
)?;
let fb = display_handle.add_framebuffer(&buffer, 32, 32)?;
let fb = display_handle.add_framebuffer(buffer.buffer(), 32, 32)?;
display_handle.set_crtc(crtc, Some(fb), (0, 0), &[connector], Some(mode))?;
let map = display_handle.map_dumb_buffer(&mut buffer)?;
let map = unsafe { mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(map) };
Ok(Self {
display_handle,
connector,
crtc,
fb,
buffer,
mapping: map,
})
}
unsafe fn console_map(&mut self) -> DisplayMap {
let size = self.buffer.buffer().size();
let shadow_buf = self.buffer.shadow_buf();
DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
self.mapping.as_mut_ptr() as *mut u32,
self.mapping.len() / 4,
shadow_buf.as_mut_ptr() as *mut u32,
shadow_buf.len() / 4,
),
width: self.buffer.size().0 as usize,
height: self.buffer.size().1 as usize,
width: size.0 as usize,
height: size.1 as usize,
}
}
pub fn dirty_fb(&self, damage: Damage) -> io::Result<()> {
pub fn dirty_fb(&mut self, damage: Damage) -> io::Result<()> {
let pitch = self.buffer.buffer().pitch();
self.buffer
.sync_range((damage.y..damage.y + damage.height).map(|row| {
let start = (row * pitch + damage.x * 4) as usize;
start..start + damage.width as usize * 4
}));
self.display_handle.dirty_framebuffer(
self.fb,
&[ClipRect::new(
@@ -365,28 +370,30 @@ impl TextScreen {
}
}
let mut new_buffer = map.display_handle.create_dumb_buffer(
let mut new_buffer = CpuBackedBuffer::new(
&map.display_handle,
(u32::from(mode.size().0), u32::from(mode.size().1)),
DrmFourcc::Argb8888,
32,
)?;
let new_fb = map.display_handle.add_framebuffer(&new_buffer, 24, 32)?;
let new_fb = map
.display_handle
.add_framebuffer(new_buffer.buffer(), 24, 32)?;
let new_mapping = map.display_handle.map_dumb_buffer(&mut new_buffer)?;
let mut new_mapping =
unsafe { mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(new_mapping) };
new_mapping.fill(0);
new_buffer.shadow_buf().fill(0);
{
let old_map = unsafe { &mut map.console_map() };
let new_size = new_buffer.buffer().size();
let new_shadow_buf = new_buffer.shadow_buf();
let new_map = &mut DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
new_mapping.as_mut_ptr() as *mut u32,
new_mapping.len() / 4,
new_shadow_buf.as_mut_ptr() as *mut u32,
new_shadow_buf.len() / 4,
),
width: new_buffer.size().0 as usize,
height: new_buffer.size().1 as usize,
width: new_size.0 as usize,
height: new_size.1 as usize,
};
if new_map.height >= old_map.height {
@@ -406,9 +413,9 @@ impl TextScreen {
}
let old_buffer = mem::replace(&mut map.buffer, new_buffer);
let old_fb = mem::replace(&mut map.fb, new_fb);
map.mapping = new_mapping;
old_buffer.destroy(&map.display_handle)?;
let old_fb = mem::replace(&mut map.fb, new_fb);
map.display_handle.set_crtc(
map.crtc,
Some(map.fb),
@@ -416,8 +423,6 @@ impl TextScreen {
&[map.connector],
Some(mode),
)?;
let _ = map.display_handle.destroy_dumb_buffer(old_buffer);
let _ = map.display_handle.destroy_framebuffer(old_fb);
Ok(())
+5 -5
View File
@@ -116,7 +116,7 @@ impl FbbootlogScheme {
self.is_scrollback = true;
self.scrollback_offset = cmp::min(
self.scrollback_offset,
buffer_len - map.buffer.size().1 as usize / 16 + spare_lines,
buffer_len - map.buffer.buffer().size().1 as usize / 16 + spare_lines,
);
let mut i = self.scrollback_offset;
self.text_screen
@@ -129,9 +129,9 @@ impl FbbootlogScheme {
.write(map, &self.text_buffer.lines[i][..], &mut VecDeque::new());
i += 1;
let yd = (damage.y + damage.height) as usize;
if i == buffer_len || yd + spare_lines * 16 > map.buffer.size().1 as usize {
if i == buffer_len || yd + spare_lines * 16 > map.buffer.buffer().size().1 as usize {
// render until end of screen
damage.height = map.buffer.size().1 - damage.y;
damage.height = map.buffer.buffer().size().1 - damage.y;
total_damage = total_damage.merge(damage);
self.is_scrollback = i < buffer_len;
break;
@@ -155,7 +155,7 @@ impl FbbootlogScheme {
}
};
if (u32::from(mode.size().0), u32::from(mode.size().1)) != map.buffer.size() {
if (u32::from(mode.size().0), u32::from(mode.size().1)) != map.buffer.buffer().size() {
match text_screen.resize(map, mode) {
Ok(()) => eprintln!("fbbootlogd: mapped display"),
Err(err) => {
@@ -240,7 +240,7 @@ impl SchemeSync for FbbootlogScheme {
if !self.is_scrollback {
let damage = self.text_screen.write(map, buf, &mut VecDeque::new());
if let Some(map) = &self.display_map {
if let Some(map) = &mut self.display_map {
map.dirty_fb(damage).unwrap();
}
}
+5 -5
View File
@@ -39,8 +39,8 @@ impl Display {
Ok(map) => {
log::debug!(
"fbcond: Mapped new display with size {}x{}",
map.buffer.size().0,
map.buffer.size().1,
map.buffer.buffer().size().0,
map.buffer.buffer().size().1,
);
self.map = Some(map)
}
@@ -59,12 +59,12 @@ impl Display {
{
Ok(mode) => mode,
Err(err) => {
eprintln!("fbbootlogd: failed to get display size: {}", err);
eprintln!("fbcond: failed to get display size: {}", err);
return;
}
};
if (u32::from(mode.size().0), u32::from(mode.size().1)) != map.buffer.size() {
if (u32::from(mode.size().0), u32::from(mode.size().1)) != map.buffer.buffer().size() {
match text_screen.resize(map, mode) {
Ok(()) => eprintln!("fbcond: mapped display"),
Err(err) => {
@@ -76,7 +76,7 @@ impl Display {
}
pub fn sync_rect(&mut self, damage: Damage) {
if let Some(map) = &self.map {
if let Some(map) = &mut self.map {
map.dirty_fb(damage).unwrap();
}
}
+74 -1
View File
@@ -1,8 +1,10 @@
use std::fs::File;
use std::io;
use std::ops::Range;
use std::os::fd::{AsFd, BorrowedFd};
use std::{io, mem, ptr};
use drm::control::connector::{self, State};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::Device as _;
use drm::{Device as _, DriverCapability};
@@ -40,3 +42,74 @@ impl V2GraphicsHandle {
Err(io::Error::other("no connected display"))
}
}
pub struct CpuBackedBuffer {
buffer: DumbBuffer,
map: DumbMapping<'static>,
shadow: Option<Box<[u8]>>,
}
impl CpuBackedBuffer {
pub fn new(
display_handle: &V2GraphicsHandle,
size: (u32, u32),
format: drm::buffer::DrmFourcc,
bpp: u32,
) -> io::Result<CpuBackedBuffer> {
let mut buffer = display_handle.create_dumb_buffer(size, format, bpp)?;
let map = display_handle.map_dumb_buffer(&mut buffer)?;
let map = unsafe { mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(map) };
let shadow = if display_handle
.get_driver_capability(DriverCapability::DumbPreferShadow)
.unwrap_or(1)
== 0
{
None
} else {
Some(vec![0; map.len()].into_boxed_slice())
};
Ok(CpuBackedBuffer {
buffer,
map,
shadow,
})
}
pub fn buffer(&self) -> &DumbBuffer {
&self.buffer
}
pub fn shadow_buf(&mut self) -> &mut [u8] {
self.shadow.as_deref_mut().unwrap_or(&mut *self.map)
}
pub fn sync_range(&mut self, ranges: impl Iterator<Item = Range<usize>>) {
let Some(shadow) = &self.shadow else {
return; // No shadow buffer; all writes are already propagated to the GPU.
};
for range in ranges {
assert!(range.start <= range.end);
assert!(range.end <= self.map.len());
unsafe {
ptr::copy_nonoverlapping(
shadow.as_ptr().add(range.start),
self.map.as_mut_ptr().add(range.start),
range.end - range.start,
);
}
}
// No need for a wbinvd to flush the write combining writes as they are
// already flushed on the next syscall anyway. And the user will need
// to do a DRM ioctl to actually present the changes on the display.
}
pub fn destroy(self, display_handle: &V2GraphicsHandle) -> io::Result<()> {
display_handle.destroy_dumb_buffer(self.buffer)
}
}
+4 -1
View File
@@ -8,7 +8,9 @@ use std::sync::Mutex;
use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus};
use driver_graphics::kms::objects::{KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects};
use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter};
use drm_sys::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
use drm_sys::{
DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
};
use syscall::{error::EINVAL, PAGE_SIZE};
use super::pipe::DeviceFb;
@@ -52,6 +54,7 @@ impl GraphicsAdapter for Device {
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
DRM_CAP_DUMB_PREFER_SHADOW => Ok(0),
_ => Err(syscall::Error::new(EINVAL)),
}
}
+4 -1
View File
@@ -6,7 +6,9 @@ use std::sync::Mutex;
use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus};
use driver_graphics::kms::objects::{KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects};
use driver_graphics::{Buffer, CursorPlane, Damage, GraphicsAdapter};
use drm_sys::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
use drm_sys::{
DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFER_SHADOW, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
};
use syscall::{EINVAL, PAGE_SIZE};
#[derive(Debug)]
@@ -59,6 +61,7 @@ impl GraphicsAdapter for FbAdapter {
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
DRM_CAP_DUMB_PREFER_SHADOW => Ok(0),
_ => Err(syscall::Error::new(EINVAL)),
}
}
+2 -1
View File
@@ -6,7 +6,7 @@ use driver_graphics::kms::connector::{KmsConnectorDriver, KmsConnectorStatus};
use driver_graphics::kms::objects::{KmsCrtc, KmsCrtcState, KmsObjectId, KmsObjects};
use driver_graphics::{Buffer as DrmBuffer, CursorPlane, Damage, GraphicsAdapter, GraphicsScheme};
use drm_sys::{
DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, DRM_CAP_DUMB_BUFFER,
DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, DRM_CAP_DUMB_BUFFER, DRM_CAP_DUMB_PREFER_SHADOW,
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT,
};
@@ -297,6 +297,7 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
match cap {
DRM_CAP_DUMB_BUFFER => Ok(1),
DRM_CAP_DUMB_PREFER_SHADOW => Ok(0),
DRM_CAP_CURSOR_WIDTH => Ok(64),
DRM_CAP_CURSOR_HEIGHT => Ok(64),
_ => Err(syscall::Error::new(EINVAL)),