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:
@@ -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(())
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user