diff --git a/drivers/graphics/console-draw/src/lib.rs b/drivers/graphics/console-draw/src/lib.rs index 9caaef58a8..ec9f0996fa 100644 --- a/drivers/graphics/console-draw/src/lib.rs +++ b/drivers/graphics/console-draw/src/lib.rs @@ -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<'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<'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(()) diff --git a/drivers/graphics/fbbootlogd/src/scheme.rs b/drivers/graphics/fbbootlogd/src/scheme.rs index a7408132e3..4ba933afec 100644 --- a/drivers/graphics/fbbootlogd/src/scheme.rs +++ b/drivers/graphics/fbbootlogd/src/scheme.rs @@ -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(); } } diff --git a/drivers/graphics/fbcond/src/display.rs b/drivers/graphics/fbcond/src/display.rs index ad0f354919..eb09b97e9c 100644 --- a/drivers/graphics/fbcond/src/display.rs +++ b/drivers/graphics/fbcond/src/display.rs @@ -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(); } } diff --git a/drivers/graphics/graphics-ipc/src/lib.rs b/drivers/graphics/graphics-ipc/src/lib.rs index 4dd3d2ffac..fedc5658b2 100644 --- a/drivers/graphics/graphics-ipc/src/lib.rs +++ b/drivers/graphics/graphics-ipc/src/lib.rs @@ -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>, +} + +impl CpuBackedBuffer { + pub fn new( + display_handle: &V2GraphicsHandle, + size: (u32, u32), + format: drm::buffer::DrmFourcc, + bpp: u32, + ) -> io::Result { + 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<'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>) { + 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) + } +} diff --git a/drivers/graphics/ihdgd/src/device/scheme.rs b/drivers/graphics/ihdgd/src/device/scheme.rs index 629486eca3..3258b62c2b 100644 --- a/drivers/graphics/ihdgd/src/device/scheme.rs +++ b/drivers/graphics/ihdgd/src/device/scheme.rs @@ -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 { match cap { DRM_CAP_DUMB_BUFFER => Ok(1), + DRM_CAP_DUMB_PREFER_SHADOW => Ok(0), _ => Err(syscall::Error::new(EINVAL)), } } diff --git a/drivers/graphics/vesad/src/scheme.rs b/drivers/graphics/vesad/src/scheme.rs index d1dd1a99c0..5c13617136 100644 --- a/drivers/graphics/vesad/src/scheme.rs +++ b/drivers/graphics/vesad/src/scheme.rs @@ -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 { match cap { DRM_CAP_DUMB_BUFFER => Ok(1), + DRM_CAP_DUMB_PREFER_SHADOW => Ok(0), _ => Err(syscall::Error::new(EINVAL)), } } diff --git a/drivers/graphics/virtio-gpud/src/scheme.rs b/drivers/graphics/virtio-gpud/src/scheme.rs index 1b6e48d412..fc3a6d2539 100644 --- a/drivers/graphics/virtio-gpud/src/scheme.rs +++ b/drivers/graphics/virtio-gpud/src/scheme.rs @@ -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 { 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)),