From de4cc6ed4acd1f993ddc4a6be833592192d89c1f Mon Sep 17 00:00:00 2001 From: Dimitar Gjorgievski Date: Thu, 3 Apr 2025 21:21:32 +0000 Subject: [PATCH] adding support for gpu cursor --- graphics/driver-graphics/src/lib.rs | 42 ++++++- graphics/graphics-ipc/src/v1.rs | 13 ++ graphics/vesad/src/scheme.rs | 21 +++- graphics/virtio-gpud/src/main.rs | 67 +++++++++++ graphics/virtio-gpud/src/scheme.rs | 176 +++++++++++++++++++++++++++- 5 files changed, 312 insertions(+), 7 deletions(-) diff --git a/graphics/driver-graphics/src/lib.rs b/graphics/driver-graphics/src/lib.rs index f06dbf59b4..f79d2c2d35 100644 --- a/graphics/driver-graphics/src/lib.rs +++ b/graphics/driver-graphics/src/lib.rs @@ -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; 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 { adapter: T, @@ -34,6 +41,7 @@ pub struct GraphicsScheme { active_vt: usize, vts_fb: HashMap>, + cursor_resources: HashMap, } enum Handle { @@ -53,6 +61,7 @@ impl GraphicsScheme { handles: BTreeMap::new(), active_vt: 0, vts_fb: HashMap::new(), + cursor_resources: HashMap::new(), } } @@ -167,6 +176,11 @@ impl Scheme for GraphicsScheme { 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 Scheme for GraphicsScheme { fn read( &mut self, id: usize, - _buf: &mut [u8], + buf: &mut [u8], _offset: u64, _fcntl_flags: u32, ) -> Result { 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 { @@ -218,6 +241,19 @@ impl Scheme for GraphicsScheme { return Ok(buf.len()); } + if size_of_val(buf) == std::mem::size_of::() + && self.adapter.supports_hw_cursor() + { + let cursor_damage = unsafe { *buf.as_ptr().cast::() }; + + //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::()); diff --git a/graphics/graphics-ipc/src/v1.rs b/graphics/graphics-ipc/src/v1.rs index 2ff0ddf900..14fb1e10b4 100644 --- a/graphics/graphics-ipc/src/v1.rs +++ b/graphics/graphics-ipc/src/v1.rs @@ -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. diff --git a/graphics/vesad/src/scheme.rs b/graphics/vesad/src/scheme.rs index d89b5e600a..4b23b27893 100644 --- a/graphics/vesad/src/scheme.rs +++ b/graphics/vesad/src/scheme.rs @@ -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, } +pub enum VesadCursor {} + +impl Cursor for VesadCursor {} + impl GraphicsAdapter for FbAdapter { type Framebuffer = GraphicScreen; + type Cursor = VesadCursor; fn displays(&self) -> Vec { (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 { diff --git a/graphics/virtio-gpud/src/main.rs b/graphics/virtio-gpud/src/main.rs index fa2a5da6f5..ea2b67eafe 100644 --- a/graphics/virtio-gpud/src/main.rs +++ b/graphics/virtio-gpud/src/main.rs @@ -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 = spin::Once::new(); fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { diff --git a/graphics/virtio-gpud/src/scheme.rs b/graphics/virtio-gpud/src/scheme.rs index 41ee3c31ca..459e29c367 100644 --- a/graphics/virtio-gpud/src/scheme.rs +++ b/graphics/virtio-gpud/src/scheme.rs @@ -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 { 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 {}