adding support for gpu cursor

This commit is contained in:
Dimitar Gjorgievski
2025-04-03 21:21:32 +00:00
committed by Jeremy Soller
parent a813d93001
commit de4cc6ed4a
5 changed files with 312 additions and 7 deletions
+39 -3
View File
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use std::io;
use graphics_ipc::v1::Damage;
use graphics_ipc::v1::{CursorDamage, Damage};
use inputd::{VtEvent, VtEventKind};
use libredox::Fd;
use redox_scheme::{RequestKind, Scheme, SignalBehavior, Socket};
@@ -9,6 +9,7 @@ use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL};
pub trait GraphicsAdapter {
type Framebuffer: Framebuffer;
type Cursor: Cursor;
fn displays(&self) -> Vec<usize>;
fn display_size(&self, display_id: usize) -> (u32, u32);
@@ -17,6 +18,10 @@ pub trait GraphicsAdapter {
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8;
fn update_plane(&mut self, display_id: usize, framebuffer: &Self::Framebuffer, damage: Damage);
fn supports_hw_cursor(&self) -> bool;
fn create_cursor_framebuffer(&mut self) -> Self::Cursor;
fn handle_cursor(&mut self, cursor_damage: CursorDamage, cursor_resource: &mut Self::Cursor);
}
pub trait Framebuffer {
@@ -24,6 +29,8 @@ pub trait Framebuffer {
fn height(&self) -> u32;
}
pub trait Cursor {}
pub struct GraphicsScheme<T: GraphicsAdapter> {
adapter: T,
@@ -34,6 +41,7 @@ pub struct GraphicsScheme<T: GraphicsAdapter> {
active_vt: usize,
vts_fb: HashMap<usize, HashMap<usize, T::Framebuffer>>,
cursor_resources: HashMap<usize, T::Cursor>,
}
enum Handle {
@@ -53,6 +61,7 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
handles: BTreeMap::new(),
active_vt: 0,
vts_fb: HashMap::new(),
cursor_resources: HashMap::new(),
}
}
@@ -167,6 +176,11 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
self.adapter.create_dumb_framebuffer(width, height)
});
if self.adapter.supports_hw_cursor() {
self.cursor_resources
.insert(vt, self.adapter.create_cursor_framebuffer());
}
self.next_id += 1;
self.handles
.insert(self.next_id, Handle::Screen { vt, screen: id });
@@ -201,12 +215,21 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
fn read(
&mut self,
id: usize,
_buf: &mut [u8],
buf: &mut [u8],
_offset: u64,
_fcntl_flags: u32,
) -> Result<usize> {
let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
Err(Error::new(EINVAL))
//Currently read is only used for Orbital to check GPU cursor support
//and only expects a buf to pass a 0 or 1 flag
if self.adapter.supports_hw_cursor() {
buf[0] = 1;
} else {
buf[0] = 0;
}
Ok(1)
}
fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result<usize> {
@@ -218,6 +241,19 @@ impl<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
return Ok(buf.len());
}
if size_of_val(buf) == std::mem::size_of::<CursorDamage>()
&& self.adapter.supports_hw_cursor()
{
let cursor_damage = unsafe { *buf.as_ptr().cast::<CursorDamage>() };
//There is always expected to be cursor_resource if supports_hw_cursor returns true
if let Some(cursor_resource) = self.cursor_resources.get_mut(vt) {
self.adapter.handle_cursor(cursor_damage, cursor_resource);
}
return Ok(buf.len());
}
let framebuffer = &self.vts_fb[vt][screen];
assert_eq!(buf.len(), std::mem::size_of::<Damage>());
+13
View File
@@ -110,6 +110,19 @@ impl Drop for DisplayMap {
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct CursorDamage {
pub header: u32,
pub x: i32,
pub y: i32,
pub hot_x: i32,
pub hot_y: i32,
pub width: i32,
pub height: i32,
pub cursor_img_bytes: [u32; 4096],
}
// Keep synced with orbital's SyncRect
// Technically orbital uses i32 rather than u32, but values larger than i32::MAX
// would be a bug anyway.
+19 -2
View File
@@ -2,8 +2,8 @@ use std::alloc::{self, Layout};
use std::convert::TryInto;
use std::ptr::{self, NonNull};
use driver_graphics::{Framebuffer, GraphicsAdapter};
use graphics_ipc::v1::Damage;
use driver_graphics::{Cursor, Framebuffer, GraphicsAdapter};
use graphics_ipc::v1::{CursorDamage, Damage};
use syscall::PAGE_SIZE;
use crate::framebuffer::FrameBuffer;
@@ -12,8 +12,13 @@ pub struct FbAdapter {
pub framebuffers: Vec<FrameBuffer>,
}
pub enum VesadCursor {}
impl Cursor for VesadCursor {}
impl GraphicsAdapter for FbAdapter {
type Framebuffer = GraphicScreen;
type Cursor = VesadCursor;
fn displays(&self) -> Vec<usize> {
(0..self.framebuffers.len()).collect()
@@ -37,6 +42,18 @@ impl GraphicsAdapter for FbAdapter {
fn update_plane(&mut self, display_id: usize, framebuffer: &Self::Framebuffer, damage: Damage) {
framebuffer.sync(&mut self.framebuffers[display_id], damage)
}
fn supports_hw_cursor(&self) -> bool {
false
}
fn create_cursor_framebuffer(&mut self) -> VesadCursor {
unimplemented!("Vesad does not support this function");
}
fn handle_cursor(&mut self, _cursor_damage: CursorDamage, _cursor_resource: &mut VesadCursor) {
unimplemented!("Vesad does not support this function");
}
}
pub struct GraphicScreen {
+67
View File
@@ -361,6 +361,73 @@ impl XferToHost2d {
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CursorPos {
pub scanout_id: u32,
pub x: i32,
pub y: i32,
_padding: u32,
}
impl CursorPos {
pub fn new(scanout_id: u32, x: i32, y: i32) -> Self {
Self {
scanout_id,
x,
y,
_padding: 0,
}
}
}
/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
#[derive(Debug)]
#[repr(C)]
pub struct UpdateCursor {
pub header: ControlHeader,
pub pos: CursorPos,
pub resource_id: ResourceId,
pub hot_x: i32,
pub hot_y: i32,
_padding: u32,
}
impl UpdateCursor {
pub fn update_cursor(x: i32, y: i32, hot_x: i32, hot_y: i32, resource_id: ResourceId) -> Self {
Self {
header: ControlHeader::with_ty(CommandTy::UpdateCursor),
pos: CursorPos::new(0, x, y),
resource_id,
hot_x,
hot_y,
_padding: 0,
}
}
}
pub struct MoveCursor {
pub header: ControlHeader,
pub pos: CursorPos,
pub resource_id: ResourceId,
pub hot_x: i32,
pub hot_y: i32,
_padding: u32,
}
impl MoveCursor {
pub fn move_cursor(x: i32, y: i32, hot_x: i32, hot_y: i32, resource_id: ResourceId) -> Self {
Self {
header: ControlHeader::with_ty(CommandTy::MoveCursor),
pos: CursorPos::new(0, x, y),
resource_id,
hot_x,
hot_y,
_padding: 0,
}
}
}
static DEVICE: spin::Once<virtio_core::Device> = spin::Once::new();
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
+174 -2
View File
@@ -1,8 +1,8 @@
use std::sync::Arc;
use common::{dma::Dma, sgl};
use driver_graphics::{Framebuffer, GraphicsAdapter, GraphicsScheme};
use graphics_ipc::v1::Damage;
use driver_graphics::{Cursor, Framebuffer, GraphicsAdapter, GraphicsScheme};
use graphics_ipc::v1::{CursorDamage, Damage};
use inputd::DisplayHandle;
use syscall::PAGE_SIZE;
@@ -40,6 +40,14 @@ impl Framebuffer for VirtGpuFramebuffer {
}
}
pub struct VirtGpuCursor {
resource_id: ResourceId,
sgl: sgl::Sgl,
set: bool,
}
impl Cursor for VirtGpuCursor {}
#[derive(Debug, Copy, Clone)]
pub struct Display {
width: u32,
@@ -92,10 +100,93 @@ impl VirtGpuAdapter<'_> {
Ok(response)
}
fn update_cursor(&mut self, cursor_damage: CursorDamage, cursor: &mut VirtGpuCursor) {
let x = cursor_damage.x;
let y = cursor_damage.y;
let hot_x = cursor_damage.hot_x;
let hot_y = cursor_damage.hot_y;
let w: i32 = cursor_damage.width;
let h: i32 = cursor_damage.height;
let cursor_image = cursor_damage.cursor_img_bytes;
//Clear previous image from backing storage
unsafe {
core::ptr::write_bytes(cursor.sgl.as_ptr() as *mut u8, 0, 64 * 64 * 4);
}
//Write image to backing storage
for row in 0..h {
let start: usize = (w * row) as usize;
let end: usize = (w * row + w) as usize;
unsafe {
core::ptr::copy_nonoverlapping(
cursor_image[start..end].as_ptr(),
(cursor.sgl.as_ptr() as *mut u32).offset(64 * row as isize),
w as usize,
);
}
}
//Transfering cursor resource to host
futures::executor::block_on(async {
let transfer_request = Dma::new(XferToHost2d::new(
cursor.resource_id,
GpuRect {
x: 0,
y: 0,
width: 64,
height: 64,
},
0,
))
.unwrap();
let header = self.send_request_fenced(transfer_request).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
});
//Update the cursor position
let request = Dma::new(UpdateCursor::update_cursor(
x,
y,
hot_x,
hot_y,
cursor.resource_id,
))
.unwrap();
futures::executor::block_on(async {
let command = ChainBuilder::new().chain(Buffer::new(&request)).build();
self.cursor_queue.send(command).await;
});
}
fn move_cursor(&self, cursor_damage: CursorDamage, cursor: &mut VirtGpuCursor) {
let x = cursor_damage.x;
let y = cursor_damage.y;
let hot_x = cursor_damage.hot_x;
let hot_y = cursor_damage.hot_y;
let request = Dma::new(MoveCursor::move_cursor(
x,
y,
hot_x,
hot_y,
cursor.resource_id,
))
.unwrap();
futures::executor::block_on(async {
let command = ChainBuilder::new().chain(Buffer::new(&request)).build();
self.cursor_queue.send(command).await;
});
}
}
impl GraphicsAdapter for VirtGpuAdapter<'_> {
type Framebuffer = VirtGpuFramebuffer;
type Cursor = VirtGpuCursor;
fn displays(&self) -> Vec<usize> {
self.displays.iter().enumerate().map(|(i, _)| i).collect()
@@ -207,6 +298,87 @@ impl GraphicsAdapter for VirtGpuAdapter<'_> {
assert_eq!(header.ty, CommandTy::RespOkNodata);
});
}
fn supports_hw_cursor(&self) -> bool {
true
}
fn create_cursor_framebuffer(&mut self) -> VirtGpuCursor {
//Creating a new resource for the cursor
let fb_size = 64 * 64 * 4;
let sgl = sgl::Sgl::new(fb_size).unwrap();
let res_id = ResourceId::alloc();
futures::executor::block_on(async {
unsafe {
core::ptr::write_bytes(sgl.as_ptr() as *mut u8, 0, fb_size);
}
let resource_request =
Dma::new(ResourceCreate2d::new(res_id, ResourceFormat::Bgrx, 64, 64)).unwrap();
let header = self.send_request_fenced(resource_request).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
//Attaching cursor resource as backing storage
let mut mem_entries =
unsafe { Dma::zeroed_slice(sgl.chunks().len()).unwrap().assume_init() };
for (entry, chunk) in mem_entries.iter_mut().zip(sgl.chunks().iter()) {
*entry = MemEntry {
address: chunk.phys as u64,
length: chunk.length.next_multiple_of(PAGE_SIZE) as u32,
padding: 0,
};
}
let attach_request =
Dma::new(AttachBacking::new(res_id, mem_entries.len() as u32)).unwrap();
let mut header = Dma::new(ControlHeader::default()).unwrap();
header.flags |= VIRTIO_GPU_FLAG_FENCE;
let command = ChainBuilder::new()
.chain(Buffer::new(&attach_request))
.chain(Buffer::new_unsized(&mem_entries))
.chain(Buffer::new(&header).flags(DescriptorFlags::WRITE_ONLY))
.build();
self.control_queue.send(command).await;
assert_eq!(header.ty, CommandTy::RespOkNodata);
//Transfering cursor resource to host
let transfer_request = Dma::new(XferToHost2d::new(
res_id,
GpuRect {
x: 0,
y: 0,
width: 64,
height: 64,
},
0,
))
.unwrap();
let header = self.send_request_fenced(transfer_request).await.unwrap();
assert_eq!(header.ty, CommandTy::RespOkNodata);
});
VirtGpuCursor {
resource_id: res_id,
sgl: sgl,
set: false,
}
}
fn handle_cursor(&mut self, cursor_damage: CursorDamage, cursor: &mut VirtGpuCursor) {
if !cursor.set {
cursor.set = true;
self.update_cursor(cursor_damage, cursor);
}
if cursor_damage.header == 0 {
self.move_cursor(cursor_damage, cursor);
} else {
self.update_cursor(cursor_damage, cursor);
}
}
}
pub struct GpuScheme {}