Merge branch 'graphics_driver_api_refactor' into 'master'

A bunch of improvements to the graphics subsystem

See merge request redox-os/drivers!246
This commit is contained in:
Jeremy Soller
2025-03-07 01:50:35 +00:00
9 changed files with 135 additions and 147 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ use std::collections::{BTreeSet, VecDeque};
use std::convert::{TryFrom, TryInto};
use std::{cmp, ptr};
use graphics_ipc::legacy::Damage;
use graphics_ipc::v1::Damage;
use orbclient::FONT;
pub struct DisplayMap {
@@ -221,7 +221,7 @@ impl TextScreen {
} else {
damage.push(Damage {
x: 0,
y: i32::try_from(change).unwrap() * 16,
y: u32::try_from(change).unwrap() * 16,
width,
height: 16,
});
+40 -28
View File
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use std::io;
use graphics_ipc::legacy::Damage;
use graphics_ipc::v1::Damage;
use inputd::{VtEvent, VtEventKind};
use libredox::errno::EOPNOTSUPP;
use libredox::Fd;
@@ -9,24 +9,23 @@ use redox_scheme::{RequestKind, Response, Scheme, SignalBehavior, Socket};
use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL};
pub trait GraphicsAdapter {
type Resource: Resource;
type Framebuffer: Framebuffer;
fn displays(&self) -> Vec<usize>;
fn display_size(&self, display_id: usize) -> (u32, u32);
fn create_resource(&mut self, width: u32, height: u32) -> Self::Resource;
fn map_resource(&mut self, resource: &Self::Resource) -> *mut u8;
fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer;
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8;
fn set_scanout(&mut self, display_id: usize, resource: &Self::Resource);
fn flush_resource(
fn update_plane(
&mut self,
display_id: usize,
resource: &Self::Resource,
damage: Option<&[Damage]>,
framebuffer: &Self::Framebuffer,
damage: &[Damage],
);
}
pub trait Resource {
pub trait Framebuffer {
fn width(&self) -> u32;
fn height(&self) -> u32;
}
@@ -40,7 +39,7 @@ pub struct GraphicsScheme<T: GraphicsAdapter> {
handles: BTreeMap<usize, Handle>,
active_vt: usize,
vts_res: HashMap<usize, HashMap<usize, T::Resource>>,
vts_fb: HashMap<usize, HashMap<usize, T::Framebuffer>>,
}
enum Handle {
@@ -59,7 +58,7 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
next_id: 0,
handles: BTreeMap::new(),
active_vt: 0,
vts_res: HashMap::new(),
vts_fb: HashMap::new(),
}
}
@@ -81,16 +80,16 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
log::info!("activate {}", vt_event.vt);
for display_id in self.adapter.displays() {
let resource = self
.vts_res
let framebuffer = self
.vts_fb
.entry(vt_event.vt)
.or_default()
.entry(display_id)
.or_insert_with(|| {
let (width, height) = self.adapter.display_size(display_id);
self.adapter.create_resource(width, height)
self.adapter.create_dumb_framebuffer(width, height)
});
self.adapter.set_scanout(display_id, resource);
Self::update_whole_screen(&mut self.adapter, display_id, framebuffer);
self.active_vt = vt_event.vt;
}
@@ -140,6 +139,19 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
Ok(())
}
fn update_whole_screen(adapter: &mut T, screen: usize, framebuffer: &T::Framebuffer) {
adapter.update_plane(
screen,
framebuffer,
&[Damage {
x: 0,
y: 0,
width: framebuffer.width(),
height: framebuffer.height(),
}],
);
}
}
impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
@@ -160,13 +172,13 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
return Err(Error::new(EINVAL));
}
self.vts_res
self.vts_fb
.entry(vt)
.or_default()
.entry(id)
.or_insert_with(|| {
let (width, height) = self.adapter.display_size(id);
self.adapter.create_resource(width, height)
self.adapter.create_dumb_framebuffer(width, height)
});
self.next_id += 1;
@@ -177,12 +189,12 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
let Handle::Screen { vt, screen } = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let resource = &self.vts_res[vt][screen];
let framebuffer = &self.vts_fb[vt][screen];
let path = format!(
"{}:{vt}.{screen}/{}/{}",
self.scheme_name,
resource.width(),
resource.height()
framebuffer.width(),
framebuffer.height()
);
buf[..path.len()].copy_from_slice(path.as_bytes());
Ok(path.len())
@@ -192,11 +204,11 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
let Handle::Screen { vt, screen } = self.handles.get(&id).ok_or(Error::new(EBADF))?;
if *vt != self.active_vt {
// This is a protection against background VT's spamming us with flush requests. We will
// flush the resource on the next VT switch anyway
// flush the framebuffer on the next VT switch anyway
return Ok(0);
}
let resource = &self.vts_res[vt][screen];
self.adapter.flush_resource(*screen, resource, None);
let framebuffer = &self.vts_fb[vt][screen];
Self::update_whole_screen(&mut self.adapter, *screen, framebuffer);
Ok(0)
}
@@ -216,11 +228,11 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
if *vt != self.active_vt {
// This is a protection against background VT's spamming us with flush requests. We will
// flush the resource on the next VT switch anyway
// flush the framebuffer on the next VT switch anyway
return Ok(buf.len());
}
let resource = &self.vts_res[vt][screen];
let framebuffer = &self.vts_fb[vt][screen];
let damage = unsafe {
core::slice::from_raw_parts(
@@ -229,7 +241,7 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
)
};
self.adapter.flush_resource(*screen, resource, Some(damage));
self.adapter.update_plane(*screen, framebuffer, damage);
Ok(buf.len())
}
@@ -248,8 +260,8 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
log::info!("KSMSG MMAP {} {:?} {} {}", id, flags, offset, size);
let handle = self.handles.get(&id).ok_or(Error::new(EINVAL))?;
let Handle::Screen { vt, screen } = handle;
let resource = &self.vts_res[vt][screen];
let ptr = T::map_resource(&mut self.adapter, resource);
let framebuffer = &self.vts_fb[vt][screen];
let ptr = T::map_dumb_framebuffer(&mut self.adapter, framebuffer);
Ok(ptr as usize)
}
}
+6 -6
View File
@@ -1,5 +1,5 @@
use event::{user_data, EventQueue};
use graphics_ipc::legacy::{Damage, LegacyGraphicsHandle};
use graphics_ipc::v1::{Damage, V1GraphicsHandle};
use inputd::ConsumerHandle;
use libredox::errno::ESTALE;
use orbclient::Event;
@@ -22,7 +22,7 @@ fn read_to_slice<T: Copy>(
}
}
fn display_fd_map(display_handle: LegacyGraphicsHandle) -> io::Result<DisplayMap> {
fn display_fd_map(display_handle: V1GraphicsHandle) -> io::Result<DisplayMap> {
let display_map = display_handle.map_display()?;
Ok(DisplayMap {
display_handle: Arc::new(display_handle),
@@ -31,8 +31,8 @@ fn display_fd_map(display_handle: LegacyGraphicsHandle) -> io::Result<DisplayMap
}
pub struct DisplayMap {
display_handle: Arc<LegacyGraphicsHandle>,
pub inner: graphics_ipc::legacy::DisplayMap,
display_handle: Arc<V1GraphicsHandle>,
pub inner: graphics_ipc::v1::DisplayMap,
}
enum DisplayCommand {
@@ -50,7 +50,7 @@ impl Display {
let map = match input_handle.open_display() {
Ok(display) => {
let display_handle = LegacyGraphicsHandle::from_file(display)?;
let display_handle = V1GraphicsHandle::from_file(display)?;
Arc::new(Mutex::new(Some(
display_fd_map(display_handle)
.unwrap_or_else(|e| panic!("failed to map display: {e}")),
@@ -102,7 +102,7 @@ impl Display {
eprintln!("fbbootlogd: handoff requested");
let new_display_handle = match input_handle.open_display() {
Ok(display) => LegacyGraphicsHandle::from_file(display).unwrap(),
Ok(display) => V1GraphicsHandle::from_file(display).unwrap(),
Err(err) => {
println!("fbbootlogd: No display present yet: {err}");
continue;
+5 -5
View File
@@ -1,4 +1,4 @@
use graphics_ipc::legacy::{Damage, LegacyGraphicsHandle};
use graphics_ipc::v1::{Damage, V1GraphicsHandle};
use inputd::ConsumerHandle;
use std::io;
@@ -8,8 +8,8 @@ pub struct Display {
}
pub struct DisplayMap {
display_handle: LegacyGraphicsHandle,
pub inner: graphics_ipc::legacy::DisplayMap,
display_handle: V1GraphicsHandle,
pub inner: graphics_ipc::v1::DisplayMap,
}
impl Display {
@@ -63,10 +63,10 @@ impl Display {
}
}
fn open_display(input_handle: &ConsumerHandle) -> io::Result<LegacyGraphicsHandle> {
fn open_display(input_handle: &ConsumerHandle) -> io::Result<V1GraphicsHandle> {
let display_file = input_handle.open_display()?;
LegacyGraphicsHandle::from_file(display_file)
V1GraphicsHandle::from_file(display_file)
}
pub fn sync_rects(&mut self, sync_rects: Vec<Damage>) {
+1 -1
View File
@@ -1 +1 @@
pub mod legacy;
pub mod v1;
@@ -4,17 +4,17 @@ use std::{cmp, io, mem, ptr, slice};
use libredox::flag;
/// A graphics handle using the legacy graphics API.
/// A graphics handle using the v1 graphics API.
///
/// The legacy graphics API only allows a single framebuffer for each VT and supports neither page
/// The v1 graphics API only allows a single framebuffer for each VT and supports neither page
/// flipping nor cursor planes.
pub struct LegacyGraphicsHandle {
pub struct V1GraphicsHandle {
file: File,
}
impl LegacyGraphicsHandle {
impl V1GraphicsHandle {
pub fn from_file(file: File) -> io::Result<Self> {
Ok(LegacyGraphicsHandle { file })
Ok(V1GraphicsHandle { file })
}
pub fn map_display(&self) -> io::Result<DisplayMap> {
@@ -111,18 +111,20 @@ impl Drop for DisplayMap {
}
// Keep synced with orbital's SyncRect
// Technically orbital uses i32 rather than u32, but values larger than i32::MAX
// would be a bug anyway.
#[derive(Debug, Copy, Clone)]
#[repr(packed)]
pub struct Damage {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
impl Damage {
#[must_use]
pub fn clip(mut self, width: i32, height: i32) -> Self {
pub fn clip(mut self, width: u32, height: u32) -> Self {
// Clip damage
self.x = cmp::min(self.x, width);
if self.x + self.width > width {
+15 -34
View File
@@ -2,8 +2,8 @@ use std::alloc::{self, Layout};
use std::convert::TryInto;
use std::ptr::{self, NonNull};
use driver_graphics::{GraphicsAdapter, Resource};
use graphics_ipc::legacy::Damage;
use driver_graphics::{Framebuffer, GraphicsAdapter};
use graphics_ipc::v1::Damage;
use syscall::PAGE_SIZE;
use crate::framebuffer::FrameBuffer;
@@ -13,7 +13,7 @@ pub struct FbAdapter {
}
impl GraphicsAdapter for FbAdapter {
type Resource = GraphicScreen;
type Framebuffer = GraphicScreen;
fn displays(&self) -> Vec<usize> {
(0..self.framebuffers.len()).collect()
@@ -26,40 +26,21 @@ impl GraphicsAdapter for FbAdapter {
)
}
fn create_resource(&mut self, width: u32, height: u32) -> Self::Resource {
fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer {
GraphicScreen::new(width as usize, height as usize)
}
fn map_resource(&mut self, resource: &Self::Resource) -> *mut u8 {
resource.ptr.as_ptr().cast::<u8>()
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8 {
framebuffer.ptr.as_ptr().cast::<u8>()
}
fn set_scanout(&mut self, display_id: usize, resource: &Self::Resource) {
self.flush_resource(display_id, resource, None);
}
fn flush_resource(
fn update_plane(
&mut self,
display_id: usize,
resource: &Self::Resource,
damage: Option<&[Damage]>,
framebuffer: &Self::Framebuffer,
damage: &[Damage],
) {
if let Some(damage) = damage {
resource.sync(&mut self.framebuffers[display_id], damage)
} else {
let framebuffer: &mut FrameBuffer = &mut self.framebuffers[display_id];
let width = resource.width.try_into().unwrap();
let height = resource.height.try_into().unwrap();
resource.sync(
framebuffer,
&[Damage {
x: 0,
y: 0,
width,
height,
}],
);
}
framebuffer.sync(&mut self.framebuffers[display_id], damage)
}
}
@@ -97,7 +78,7 @@ impl Drop for GraphicScreen {
}
}
impl Resource for GraphicScreen {
impl Framebuffer for GraphicScreen {
fn width(&self) -> u32 {
self.width as u32
}
@@ -115,10 +96,10 @@ impl GraphicScreen {
self.height.try_into().unwrap(),
);
let start_x: usize = sync_rect.x.try_into().unwrap_or(0);
let start_y: usize = sync_rect.y.try_into().unwrap_or(0);
let w: usize = sync_rect.width.try_into().unwrap_or(0);
let h: usize = sync_rect.height.try_into().unwrap_or(0);
let start_x: usize = sync_rect.x.try_into().unwrap();
let start_y: usize = sync_rect.y.try_into().unwrap();
let w: usize = sync_rect.width.try_into().unwrap();
let h: usize = sync_rect.height.try_into().unwrap();
let offscreen_ptr = self.ptr.as_ptr() as *mut u32;
let onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable
+4 -1
View File
@@ -109,6 +109,9 @@ pub enum CommandTy {
static_assertions::const_assert_eq!(core::mem::size_of::<CommandTy>(), 4);
const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0;
//const VIRTIO_GPU_FLAG_INFO_RING_IDX: u32 = 1 << 1;
#[derive(Debug)]
#[repr(C)]
pub struct ControlHeader {
@@ -192,7 +195,7 @@ impl Default for GetDisplayInfo {
static RESOURCE_ALLOC: AtomicU32 = AtomicU32::new(1); // XXX: 0 is reserved for whatever that takes `resource_id`.
#[derive(Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[repr(C)]
pub struct ResourceId(u32);
+50 -60
View File
@@ -1,8 +1,8 @@
use std::sync::Arc;
use common::{dma::Dma, sgl};
use driver_graphics::{GraphicsAdapter, GraphicsScheme, Resource};
use graphics_ipc::legacy::Damage;
use driver_graphics::{Framebuffer, GraphicsAdapter, GraphicsScheme};
use graphics_ipc::v1::Damage;
use inputd::DisplayHandle;
use syscall::PAGE_SIZE;
@@ -15,22 +15,22 @@ use crate::*;
impl Into<GpuRect> for Damage {
fn into(self) -> GpuRect {
GpuRect {
x: self.x as u32,
y: self.y as u32,
width: self.width as u32,
height: self.height as u32,
x: self.x,
y: self.y,
width: self.width,
height: self.height,
}
}
}
pub struct VirtGpuResource {
pub struct VirtGpuFramebuffer {
id: ResourceId,
sgl: sgl::Sgl,
width: u32,
height: u32,
}
impl Resource for VirtGpuResource {
impl Framebuffer for VirtGpuFramebuffer {
fn width(&self) -> u32 {
self.width
}
@@ -44,6 +44,7 @@ impl Resource for VirtGpuResource {
pub struct Display {
width: u32,
height: u32,
active_resource: Option<ResourceId>,
}
pub struct VirtGpuAdapter<'a> {
@@ -65,11 +66,16 @@ impl VirtGpuAdapter<'_> {
Ok(header)
}
async fn flush_resource_inner(&self, flush: ResourceFlush) -> Result<(), Error> {
let header = self.send_request(Dma::new(flush)?).await?;
assert_eq!(header.ty, CommandTy::RespOkNodata);
async fn send_request_fenced<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
let mut header = Dma::new(ControlHeader::default())?;
header.flags |= VIRTIO_GPU_FLAG_FENCE;
let command = ChainBuilder::new()
.chain(Buffer::new(&request))
.chain(Buffer::new(&header).flags(DescriptorFlags::WRITE_ONLY))
.build();
Ok(())
self.control_queue.send(command).await;
Ok(header)
}
async fn get_display_info(&self) -> Result<Dma<GetDisplayInfo>, Error> {
@@ -89,7 +95,7 @@ impl VirtGpuAdapter<'_> {
}
impl GraphicsAdapter for VirtGpuAdapter<'_> {
type Resource = VirtGpuResource;
type Framebuffer = VirtGpuFramebuffer;
fn displays(&self) -> Vec<usize> {
self.displays.iter().enumerate().map(|(i, _)| i).collect()
@@ -102,7 +108,7 @@ impl GraphicsAdapter for VirtGpuAdapter<'_> {
)
}
fn create_resource(&mut self, width: u32, height: u32) -> Self::Resource {
fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer {
futures::executor::block_on(async {
let bpp = 32;
let fb_size = width as usize * height as usize * bpp / 8;
@@ -151,7 +157,7 @@ impl GraphicsAdapter for VirtGpuAdapter<'_> {
self.control_queue.send(command).await;
assert_eq!(header.ty, CommandTy::RespOkNodata);
VirtGpuResource {
VirtGpuFramebuffer {
id: res_id,
sgl,
width,
@@ -160,39 +166,24 @@ impl GraphicsAdapter for VirtGpuAdapter<'_> {
})
}
fn map_resource(&mut self, resource: &Self::Resource) -> *mut u8 {
resource.sgl.as_ptr()
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8 {
framebuffer.sgl.as_ptr()
}
fn set_scanout(&mut self, display_id: usize, resource: &Self::Resource) {
futures::executor::block_on(async {
let scanout_request = Dma::new(SetScanout::new(
display_id as u32,
resource.id,
GpuRect::new(0, 0, resource.width, resource.height),
))
.unwrap();
let header = self.send_request(scanout_request).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
});
self.flush_resource(display_id, resource, None);
}
fn flush_resource(
fn update_plane(
&mut self,
_display_id: usize,
resource: &Self::Resource,
damage: Option<&[Damage]>,
display_id: usize,
framebuffer: &Self::Framebuffer,
damage: &[Damage],
) {
futures::executor::block_on(async {
let req = Dma::new(XferToHost2d::new(
resource.id,
framebuffer.id,
GpuRect {
x: 0,
y: 0,
width: resource.width,
height: resource.height,
width: framebuffer.width,
height: framebuffer.height,
},
0,
))
@@ -200,29 +191,26 @@ impl GraphicsAdapter for VirtGpuAdapter<'_> {
let header = self.send_request(req).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
if let Some(damage) = damage {
for damage in damage {
self.flush_resource_inner(ResourceFlush::new(
resource.id,
damage
.clip(resource.width as i32, resource.height as i32)
.into(),
))
.await
.unwrap();
}
} else {
self.flush_resource_inner(ResourceFlush::new(
resource.id,
GpuRect {
x: 0,
y: 0,
width: resource.width,
height: resource.height,
},
// FIXME once we support resizing we also need to check that the current and target size match
if self.displays[display_id].active_resource != Some(framebuffer.id) {
let scanout_request = Dma::new(SetScanout::new(
display_id as u32,
framebuffer.id,
GpuRect::new(0, 0, framebuffer.width, framebuffer.height),
))
.await
.unwrap();
let header = self.send_request(scanout_request).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
self.displays[display_id].active_resource = Some(framebuffer.id);
}
for damage in damage {
let flush = ResourceFlush::new(
framebuffer.id,
damage.clip(framebuffer.width, framebuffer.height).into(),
);
let header = self.send_request(Dma::new(flush).unwrap()).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
}
});
}
@@ -261,11 +249,13 @@ impl<'a> GpuScheme {
adapter.displays.push(Display {
width: 640,
height: 480,
active_resource: None,
});
} else {
adapter.displays.push(Display {
width: info.rect.width,
height: info.rect.height,
active_resource: None,
});
}
}