From d2f1af9ca7e2f99eb504bcc161221d62b0e5b4fc Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:22:32 +0100 Subject: [PATCH] Add driver-graphics crate This unifies the driver interface handling between graphics drivers and makes it easier to change all graphics drivers in lockstep when adding new features. --- Cargo.lock | 16 +- Cargo.toml | 1 + graphics/driver-graphics/Cargo.toml | 13 + graphics/driver-graphics/src/lib.rs | 259 ++++++++++++++++ graphics/vesad/Cargo.toml | 2 +- graphics/vesad/src/display.rs | 4 + graphics/vesad/src/main.rs | 67 ++--- graphics/vesad/src/scheme.rs | 253 ++-------------- graphics/vesad/src/screen.rs | 34 +-- graphics/virtio-gpud/Cargo.toml | 2 +- graphics/virtio-gpud/src/main.rs | 51 +--- graphics/virtio-gpud/src/scheme.rs | 446 ++++++++++------------------ 12 files changed, 523 insertions(+), 625 deletions(-) create mode 100644 graphics/driver-graphics/Cargo.toml create mode 100644 graphics/driver-graphics/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0e99da0d5d..28de7e560d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,6 +396,18 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "driver-graphics" +version = "0.1.0" +dependencies = [ + "common", + "inputd", + "libredox", + "log", + "redox-scheme", + "redox_syscall", +] + [[package]] name = "driver-network" version = "0.1.0" @@ -1706,12 +1718,12 @@ name = "vesad" version = "0.1.0" dependencies = [ "common", + "driver-graphics", "inputd", "libredox", "orbclient", "ransid", "redox-daemon", - "redox-scheme", "redox_event", "redox_syscall", ] @@ -1760,6 +1772,7 @@ version = "0.1.0" dependencies = [ "anyhow", "common", + "driver-graphics", "futures", "inputd", "libredox", @@ -1768,7 +1781,6 @@ dependencies = [ "paste", "pcid", "redox-daemon", - "redox-scheme", "redox_event", "redox_syscall", "spin", diff --git a/Cargo.toml b/Cargo.toml index 2181fb2841..9fe3ecd2be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "graphics/bgad", "graphics/console-draw", "graphics/fbbootlogd", + "graphics/driver-graphics", "graphics/fbcond", "graphics/vesad", "graphics/virtio-gpud", diff --git a/graphics/driver-graphics/Cargo.toml b/graphics/driver-graphics/Cargo.toml new file mode 100644 index 0000000000..c7cd6f9de5 --- /dev/null +++ b/graphics/driver-graphics/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "driver-graphics" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4" +redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } +redox_syscall = "0.5" +libredox = "0.1.3" + +common = { path = "../../common" } +inputd = { path = "../../inputd" } diff --git a/graphics/driver-graphics/src/lib.rs b/graphics/driver-graphics/src/lib.rs new file mode 100644 index 0000000000..33c925206c --- /dev/null +++ b/graphics/driver-graphics/src/lib.rs @@ -0,0 +1,259 @@ +use std::collections::{BTreeMap, HashMap}; +use std::io; + +use inputd::{Damage, VtEvent, VtEventKind}; +use libredox::errno::EOPNOTSUPP; +use libredox::Fd; +use redox_scheme::{RequestKind, Response, Scheme, SignalBehavior, Socket}; +use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL}; + +pub trait GraphicsAdapter { + type Resource: Resource; + + fn displays(&self) -> Vec; + 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 set_scanout(&mut self, display_id: usize, resource: &Self::Resource); + fn flush_resource( + &mut self, + display_id: usize, + resource: &Self::Resource, + damage: Option<&[Damage]>, + ); +} + +pub trait Resource { + fn width(&self) -> u32; + fn height(&self) -> u32; +} + +pub struct GraphicsScheme { + adapter: T, + + scheme_name: String, + socket: Socket, + next_id: usize, + handles: BTreeMap, + + active_vt: usize, + vts_res: HashMap>, +} + +enum Handle { + Screen { vt: usize, screen: usize }, +} + +impl GraphicsScheme { + pub fn new(adapter: T, scheme_name: String) -> Self { + assert!(scheme_name.starts_with("display")); + let socket = Socket::nonblock(&scheme_name).expect("failed to create graphics scheme"); + + GraphicsScheme { + adapter, + scheme_name, + socket, + next_id: 0, + handles: BTreeMap::new(), + active_vt: 0, + vts_res: HashMap::new(), + } + } + + pub fn event_handle(&self) -> &Fd { + self.socket.inner() + } + + pub fn adapter(&self) -> &T { + &self.adapter + } + + pub fn adapter_mut(&mut self) -> &mut T { + &mut self.adapter + } + + pub fn handle_vt_event(&mut self, vt_event: VtEvent) { + match vt_event.kind { + VtEventKind::Activate => { + log::info!("activate {}", vt_event.vt); + + for display_id in self.adapter.displays() { + let resource = self + .vts_res + .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.set_scanout(display_id, resource); + + self.active_vt = vt_event.vt; + } + } + + VtEventKind::Deactivate => { + log::info!("deactivate {}", vt_event.vt); + // nothing to do :) + } + + VtEventKind::Resize => { + log::warn!("driver-graphics: resize is not implemented yet") + } + } + } + + /// Process new scheme requests. + /// + /// This needs to be called each time there is a new event on the scheme + /// file. + pub fn tick(&mut self) -> io::Result<()> { + loop { + let request = match self.socket.next_request(SignalBehavior::Restart) { + Ok(Some(request)) => request, + Ok(None) => { + // Scheme likely got unmounted + std::process::exit(0); + } + Err(err) if err.errno == EAGAIN => break, + Err(err) => panic!("vesad: failed to read display scheme: {err}"), + }; + + match request.kind() { + RequestKind::Call(call_request) => { + let resp = call_request.handle_scheme(self); + self.socket + .write_response(resp, SignalBehavior::Restart) + .expect("vesad: failed to write display scheme"); + } + RequestKind::SendFd(sendfd_request) => { + self.socket.write_response( + Response::for_sendfd(&sendfd_request, Err(syscall::Error::new(EOPNOTSUPP))), + SignalBehavior::Restart, + )?; + } + RequestKind::Cancellation(_cancellation_request) => {} + RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => { + unreachable!() + } + } + } + + Ok(()) + } +} + +impl Scheme for GraphicsScheme { + fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { + if path.is_empty() { + return Err(Error::new(EINVAL)); + } + + let mut parts = path.split('/'); + let mut screen = parts.next().unwrap_or("").split('.'); + + let vt = screen.next().unwrap_or("").parse::().unwrap(); + let id = screen.next().unwrap_or("").parse::().unwrap_or(0); + + dbg!(vt, id); + + if id >= self.adapter.displays().len() { + return Err(Error::new(EINVAL)); + } + + self.vts_res + .entry(vt) + .or_default() + .entry(id) + .or_insert_with(|| { + let (width, height) = self.adapter.display_size(id); + self.adapter.create_resource(width, height) + }); + + self.next_id += 1; + self.handles + .insert(self.next_id, Handle::Screen { vt, screen: id }); + Ok(self.next_id) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result { + let Handle::Screen { vt, screen } = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let resource = &self.vts_res[vt][screen]; + let path = format!( + "{}:{vt}.{screen}/{}/{}", + self.scheme_name, + resource.width(), + resource.height() + ); + buf[..path.len()].copy_from_slice(path.as_bytes()); + Ok(path.len()) + } + + fn fsync(&mut self, id: usize) -> syscall::Result { + 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 + return Ok(0); + } + let resource = &self.vts_res[vt][screen]; + self.adapter.flush_resource(*screen, resource, None); + Ok(0) + } + + fn read( + &mut self, + id: usize, + _buf: &mut [u8], + _offset: u64, + _fcntl_flags: u32, + ) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + Err(Error::new(EINVAL)) + } + + fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result { + 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 + return Ok(buf.len()); + } + + let resource = &self.vts_res[vt][screen]; + + let damage = unsafe { + core::slice::from_raw_parts( + buf.as_ptr() as *const Damage, + buf.len() / core::mem::size_of::(), + ) + }; + + self.adapter.flush_resource(*screen, resource, Some(damage)); + + Ok(buf.len()) + } + + fn close(&mut self, id: usize) -> syscall::Result { + self.handles.remove(&id).ok_or(Error::new(EBADF))?; + Ok(0) + } + fn mmap_prep( + &mut self, + id: usize, + offset: u64, + size: usize, + flags: MapFlags, + ) -> syscall::Result { + 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); + Ok(ptr as usize) + } +} diff --git a/graphics/vesad/Cargo.toml b/graphics/vesad/Cargo.toml index eeb5bc77ed..8106dbca03 100644 --- a/graphics/vesad/Cargo.toml +++ b/graphics/vesad/Cargo.toml @@ -11,9 +11,9 @@ redox-daemon = "0.1" redox_event = "0.4.1" common = { path = "../../common" } +driver-graphics = { path = "../driver-graphics" } inputd = { path = "../../inputd" } libredox = "0.1.3" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } [features] default = [] diff --git a/graphics/vesad/src/display.rs b/graphics/vesad/src/display.rs index f746c5ac14..8aad04d62c 100644 --- a/graphics/vesad/src/display.rs +++ b/graphics/vesad/src/display.rs @@ -21,6 +21,10 @@ impl OffscreenBuffer { let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout)); OffscreenBuffer { ptr } } + + pub fn ptr(&self) -> *mut u8 { + self.ptr.as_mut_ptr().cast::() + } } impl Drop for OffscreenBuffer { fn drop(&mut self) { diff --git a/graphics/vesad/src/main.rs b/graphics/vesad/src/main.rs index 4880e1a67b..457870ba64 100644 --- a/graphics/vesad/src/main.rs +++ b/graphics/vesad/src/main.rs @@ -1,15 +1,16 @@ -#![feature(int_roundings)] +#![feature(int_roundings, slice_ptr_get)] extern crate orbclient; extern crate syscall; +use driver_graphics::GraphicsScheme; use event::{user_data, EventQueue}; -use libredox::errno::{EAGAIN, EOPNOTSUPP}; -use redox_scheme::{RequestKind, Response, SignalBehavior, Socket}; +use inputd::DisplayHandle; use std::env; use std::fs::File; use std::os::fd::AsRawFd; -use crate::{framebuffer::FrameBuffer, scheme::DisplayScheme}; +use crate::framebuffer::FrameBuffer; +use crate::scheme::FbAdapter; mod display; mod framebuffer; @@ -78,12 +79,15 @@ fn main() { .expect("failed to create daemon"); } fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[()]) -> ! { - let socket = Socket::nonblock("display.vesa").expect("vesad: failed to create display scheme"); - - let mut scheme = DisplayScheme::new(framebuffers, &spec); - + let mut inputd_display_handle = DisplayHandle::new_early("vesa").unwrap(); let mut inputd_control_handle = inputd::ControlHandle::new().unwrap(); + for &() in spec.iter() { + inputd_display_handle.register_vt().unwrap(); + } + + let mut scheme = GraphicsScheme::new(FbAdapter { framebuffers }, "display.vesa".to_owned()); + user_data! { enum Source { Input, @@ -95,14 +99,14 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[( EventQueue::new().expect("vesad: failed to create event queue"); event_queue .subscribe( - scheme.inputd_handle.inner().as_raw_fd() as usize, + inputd_display_handle.inner().as_raw_fd() as usize, Source::Input, event::EventFlags::READ, ) .unwrap(); event_queue .subscribe( - socket.inner().raw(), + scheme.event_handle().raw(), Source::Scheme, event::EventFlags::READ, ) @@ -123,8 +127,7 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[( { match event { Source::Input => { - while let Some(vt_event) = scheme - .inputd_handle + while let Some(vt_event) = inputd_display_handle .read_vt_event() .expect("vesad: failed to read display handle") { @@ -132,43 +135,9 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec, spec: &[( } } Source::Scheme => { - loop { - let request = match socket.next_request(SignalBehavior::Restart) { - Ok(Some(request)) => request, - Ok(None) => { - // Scheme likely got unmounted - std::process::exit(0); - } - Err(err) if err.errno == EAGAIN => break, - Err(err) => panic!("vesad: failed to read display scheme: {err}"), - }; - - match request.kind() { - RequestKind::Call(call_request) => { - socket - .write_response( - call_request.handle_scheme(&mut scheme), - SignalBehavior::Restart, - ) - .expect("vesad: failed to write display scheme"); - } - RequestKind::SendFd(sendfd_request) => { - socket - .write_response( - Response::for_sendfd( - &sendfd_request, - Err(syscall::Error::new(EOPNOTSUPP)), - ), - SignalBehavior::Restart, - ) - .expect("vesad: failed to write scheme"); - } - RequestKind::Cancellation(_cancellation_request) => {} - RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => { - unreachable!() - } - } - } + scheme + .tick() + .expect("vesad: failed to handle scheme events"); } } } diff --git a/graphics/vesad/src/scheme.rs b/graphics/vesad/src/scheme.rs index a2cf93a011..495a5a48cf 100644 --- a/graphics/vesad/src/scheme.rs +++ b/graphics/vesad/src/scheme.rs @@ -1,248 +1,47 @@ -use std::collections::BTreeMap; -use std::str; - -use inputd::{VtEvent, VtEventKind}; -use redox_scheme::Scheme; -use syscall::{Error, MapFlags, Result, EBADF, EINVAL, ENOENT}; +use driver_graphics::GraphicsAdapter; use crate::{framebuffer::FrameBuffer, screen::GraphicScreen}; -#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug)] -pub struct VtIndex(usize); - -#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug)] -pub struct ScreenIndex(usize); - -#[derive(Clone)] -pub struct Handle { - pub vt: VtIndex, - pub screen: ScreenIndex, +pub struct FbAdapter { + pub framebuffers: Vec, } -pub struct DisplayScheme { - framebuffers: Vec, - active: VtIndex, - pub vts: BTreeMap>, - next_id: usize, - pub handles: BTreeMap, - pub inputd_handle: inputd::DisplayHandle, -} +impl GraphicsAdapter for FbAdapter { + type Resource = GraphicScreen; -impl DisplayScheme { - pub fn new(framebuffers: Vec, spec: &[()]) -> DisplayScheme { - let mut inputd_handle = inputd::DisplayHandle::new_early("vesa").unwrap(); - - let mut vts = BTreeMap::>::new(); - - for &() in spec.iter() { - let mut screens = BTreeMap::::new(); - for fb_i in 0..framebuffers.len() { - let fb = &framebuffers[fb_i]; - screens.insert(ScreenIndex(fb_i), GraphicScreen::new(fb.width, fb.height)); - } - vts.insert(VtIndex(inputd_handle.register_vt().unwrap()), screens); - } - - DisplayScheme { - framebuffers, - active: VtIndex(1), - vts, - next_id: 0, - handles: BTreeMap::new(), - inputd_handle, - } + fn displays(&self) -> Vec { + (0..self.framebuffers.len()).collect() } - fn resize(&mut self, width: usize, height: usize, stride: usize) { - //TODO: support resizing other outputs? - let fb_i = 0; - println!( - "Resizing framebuffer {} to {}, {} stride {}", - fb_i, width, height, stride - ); - - unsafe { - self.framebuffers[fb_i].resize(width, height, stride); - } - - // Resize screens - for (vt_i, screens) in self.vts.iter_mut() { - for (screen_i, screen) in screens.iter_mut() { - if screen_i.0 == fb_i { - screen.resize(width, height); - if *vt_i == self.active { - screen.redraw(&mut self.framebuffers[screen_i.0]); - } - } - } - } + fn display_size(&self, display_id: usize) -> (u32, u32) { + ( + self.framebuffers[display_id].width as u32, + self.framebuffers[display_id].height as u32, + ) } - pub fn handle_vt_event(&mut self, vt_event: VtEvent) { - match vt_event.kind { - VtEventKind::Activate => { - let vt_i = VtIndex(vt_event.vt); - - if let Some(screens) = self.vts.get_mut(&vt_i) { - for (screen_i, screen) in screens.iter_mut() { - screen.redraw(&mut self.framebuffers[screen_i.0]); - } - } - - self.active = vt_i; - } - VtEventKind::Deactivate => { - // Nothing to do for deactivate :) - } - VtEventKind::Resize => { - self.resize( - vt_event.width as usize, - vt_event.height as usize, - vt_event.stride as usize, - ); - } - } - } -} - -impl Scheme for DisplayScheme { - fn open(&mut self, path_str: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { - let mut parts = path_str.split('/'); - let mut vt_screen = parts.next().unwrap_or("").split('.'); - let vt_i = VtIndex(vt_screen.next().unwrap_or("").parse::().unwrap_or(1)); - let screen_i = ScreenIndex(vt_screen.next().unwrap_or("").parse::().unwrap_or(0)); - if let Some(screens) = self.vts.get_mut(&vt_i) { - if screens.get_mut(&screen_i).is_some() { - let id = self.next_id; - self.next_id += 1; - - self.handles.insert( - id, - Handle { - vt: vt_i, - screen: screen_i, - }, - ); - - Ok(id) - } else { - Err(Error::new(ENOENT)) - } - } else { - Err(Error::new(ENOENT)) - } + fn create_resource(&mut self, width: u32, height: u32) -> Self::Resource { + GraphicScreen::new(width as usize, height as usize) } - fn dup(&mut self, id: usize, buf: &[u8]) -> Result { - if !buf.is_empty() { - return Err(Error::new(EINVAL)); - } - - let handle = self - .handles - .get(&id) - .map(|handle| handle.clone()) - .ok_or(Error::new(EBADF))?; - - let new_id = self.next_id; - self.next_id += 1; - - self.handles.insert(new_id, handle.clone()); - - Ok(new_id) + fn map_resource(&mut self, resource: &Self::Resource) -> *mut u8 { + resource.offscreen.ptr() } - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - let path_str = { - if let Some(screens) = self.vts.get(&handle.vt) { - if let Some(screen) = screens.get(&handle.screen) { - format!( - "display:{}.{}/{}/{}", - handle.vt.0, handle.screen.0, screen.width, screen.height - ) - } else { - return Err(Error::new(EBADF)); - } - } else { - return Err(Error::new(EBADF)); - } - }; - - let path = path_str.as_bytes(); - - let mut i = 0; - while i < buf.len() && i < path.len() { - buf[i] = path[i]; - i += 1; - } - - Ok(i) + fn set_scanout(&mut self, display_id: usize, resource: &Self::Resource) { + resource.redraw(&mut self.framebuffers[display_id]); } - fn fsync(&mut self, id: usize) -> Result { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - if let Some(screens) = self.vts.get_mut(&handle.vt) { - if let Some(screen) = screens.get_mut(&handle.screen) { - if handle.vt == self.active { - screen.redraw(&mut self.framebuffers[handle.screen.0]); - } - return Ok(0); - } - } - - Err(Error::new(EBADF)) - } - - fn read( + fn flush_resource( &mut self, - id: usize, - _buf: &mut [u8], - _offset: u64, - _fcntl_flags: u32, - ) -> Result { - let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - Err(Error::new(EINVAL)) - } - - fn write(&mut self, id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - if let Some(screens) = self.vts.get_mut(&handle.vt) { - if let Some(screen) = screens.get_mut(&handle.screen) { - if handle.vt == self.active { - screen.write(buf, Some(&mut self.framebuffers[handle.screen.0])) - } else { - screen.write(buf, None) - } - } else { - Err(Error::new(EBADF)) - } + display_id: usize, + resource: &Self::Resource, + damage: Option<&[inputd::Damage]>, + ) { + if let Some(damage) = damage { + resource.sync(&mut self.framebuffers[display_id], damage) } else { - Err(Error::new(EBADF)) + resource.redraw(&mut self.framebuffers[display_id]) } } - - fn close(&mut self, id: usize) -> Result { - self.handles.remove(&id).ok_or(Error::new(EBADF))?; - Ok(0) - } - fn mmap_prep(&mut self, id: usize, off: u64, size: usize, _flags: MapFlags) -> Result { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - if let Some(screens) = self.vts.get(&handle.vt) { - if let Some(screen) = screens.get(&handle.screen) { - if off as usize + size <= screen.offscreen.len() * 4 { - return Ok(screen.offscreen.as_ptr() as usize + off as usize); - } else { - return Err(Error::new(EINVAL)); - } - } - } - - Err(Error::new(EBADF)) - } } diff --git a/graphics/vesad/src/screen.rs b/graphics/vesad/src/screen.rs index a50518ae4c..71b99f7b51 100644 --- a/graphics/vesad/src/screen.rs +++ b/graphics/vesad/src/screen.rs @@ -1,8 +1,8 @@ use std::convert::TryInto; -use std::{cmp, mem, ptr, slice}; +use std::{cmp, ptr}; +use driver_graphics::Resource; use inputd::Damage; -use syscall::error::*; use crate::display::OffscreenBuffer; use crate::framebuffer::FrameBuffer; @@ -23,6 +23,16 @@ impl GraphicScreen { } } +impl Resource for GraphicScreen { + fn width(&self) -> u32 { + self.width as u32 + } + + fn height(&self) -> u32 { + self.height as u32 + } +} + impl GraphicScreen { pub fn resize(&mut self, width: usize, height: usize) { //TODO: Fix issue with mapped screens @@ -69,21 +79,7 @@ impl GraphicScreen { }; } - pub fn write(&mut self, buf: &[u8], framebuffer: Option<&mut FrameBuffer>) -> Result { - let sync_rects = unsafe { - slice::from_raw_parts( - buf.as_ptr() as *const Damage, - buf.len() / mem::size_of::(), - ) - }; - - if let Some(framebuffer) = framebuffer { - self.sync(framebuffer, sync_rects); - } - - Ok(sync_rects.len() * mem::size_of::()) - } - pub fn sync(&mut self, framebuffer: &mut FrameBuffer, sync_rects: &[Damage]) { + pub fn sync(&self, framebuffer: &mut FrameBuffer, sync_rects: &[Damage]) { for sync_rect in sync_rects { let sync_rect = sync_rect.clip( self.width.try_into().unwrap(), @@ -99,7 +95,7 @@ impl GraphicScreen { let row_pixel_count = w; - let mut offscreen_ptr = self.offscreen.as_mut_ptr(); + let mut offscreen_ptr = self.offscreen.as_ptr(); let mut onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable unsafe { @@ -117,7 +113,7 @@ impl GraphicScreen { } } - pub fn redraw(&mut self, framebuffer: &mut FrameBuffer) { + pub fn redraw(&self, framebuffer: &mut FrameBuffer) { let width = self.width.try_into().unwrap(); let height = self.height.try_into().unwrap(); self.sync( diff --git a/graphics/virtio-gpud/Cargo.toml b/graphics/virtio-gpud/Cargo.toml index 1362b2d8b3..42da6e798f 100644 --- a/graphics/virtio-gpud/Cargo.toml +++ b/graphics/virtio-gpud/Cargo.toml @@ -12,6 +12,7 @@ anyhow = "1.0.71" paste = "1.0.13" common = { path = "../../common" } +driver-graphics = { path = "../driver-graphics" } virtio-core = { path = "../../virtio-core" } pcid = { path = "../../pcid" } inputd = { path = "../../inputd" } @@ -22,4 +23,3 @@ redox_syscall = "0.5" orbclient = "0.3.27" spin = "0.9.8" libredox = "0.1.3" -redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" } diff --git a/graphics/virtio-gpud/src/main.rs b/graphics/virtio-gpud/src/main.rs index 6543d97ddc..985d03b7c1 100644 --- a/graphics/virtio-gpud/src/main.rs +++ b/graphics/virtio-gpud/src/main.rs @@ -27,10 +27,8 @@ use std::os::fd::AsRawFd; use std::sync::atomic::{AtomicU32, Ordering}; use event::{user_data, EventQueue}; -use libredox::errno::{EAGAIN, EOPNOTSUPP}; use pcid_interface::PciFunctionHandle; -use redox_scheme::{RequestKind, Response, SignalBehavior, Socket}; use virtio_core::utils::VolatileCell; use virtio_core::MSIX_PRIMARY_VECTOR; @@ -439,7 +437,6 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { device.transport.run_device(); deamon.ready().unwrap(); - let socket = Socket::nonblock("display.virtio-gpu")?; let mut scheme = futures::executor::block_on(scheme::GpuScheme::new( config, control_queue.clone(), @@ -465,15 +462,12 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { .unwrap(); event_queue .subscribe( - socket.inner().raw(), + scheme.inner.event_handle().raw(), Source::Scheme, event::EventFlags::READ, ) .unwrap(); - //let mut inputd_control_handle = inputd::ControlHandle::new().unwrap(); - //inputd_control_handle.activate_vt(3).unwrap(); - let all = [Source::Input, Source::Scheme]; for event in all .into_iter() @@ -486,47 +480,14 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> { .read_vt_event() .expect("virtio-gpud: failed to read display handle") { - scheme.handle_vt_event(vt_event); + scheme.inner.handle_vt_event(vt_event); } } Source::Scheme => { - loop { - let request = match socket.next_request(SignalBehavior::Restart) { - Ok(Some(request)) => request, - Ok(None) => { - // Scheme likely got unmounted - std::process::exit(0); - } - Err(err) if err.errno == EAGAIN => break, - Err(err) => return Err(err.into()), - }; - - match request.kind() { - RequestKind::Call(call_request) => { - socket - .write_response( - call_request.handle_scheme(&mut scheme), - SignalBehavior::Restart, - ) - .expect("virtio-gpud: failed to write display scheme"); - } - RequestKind::SendFd(sendfd_request) => { - socket - .write_response( - Response::for_sendfd( - &sendfd_request, - Err(syscall::Error::new(EOPNOTSUPP)), - ), - SignalBehavior::Restart, - ) - .expect("virtio-gpud: failed to write scheme"); - } - RequestKind::Cancellation(_cancellation_request) => {} - RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => { - unreachable!() - } - } - } + scheme + .inner + .tick() + .expect("virtio-gpud: failed to process scheme events"); } } } diff --git a/graphics/virtio-gpud/src/scheme.rs b/graphics/virtio-gpud/src/scheme.rs index 35933fad9d..69d41ab868 100644 --- a/graphics/virtio-gpud/src/scheme.rs +++ b/graphics/virtio-gpud/src/scheme.rs @@ -1,13 +1,10 @@ -use std::collections::{BTreeMap, HashMap}; - -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use common::{dma::Dma, sgl}; -use inputd::{Damage, VtEvent, VtEventKind}; +use driver_graphics::{GraphicsAdapter, GraphicsScheme, Resource}; +use inputd::Damage; -use redox_scheme::Scheme; -use syscall::{Error as SysError, MapFlags, EBADF, EINVAL, PAGE_SIZE}; +use syscall::PAGE_SIZE; use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags}; use virtio_core::transport::{Error, Queue, Transport}; @@ -26,9 +23,21 @@ impl Into for Damage { } } -struct GpuResource { +pub struct VirtGpuResource { id: ResourceId, sgl: sgl::Sgl, + width: u32, + height: u32, +} + +impl Resource for VirtGpuResource { + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } } #[derive(Debug, Copy, Clone)] @@ -37,7 +46,14 @@ pub struct Display { height: u32, } -impl GpuScheme<'_> { +pub struct VirtGpuAdapter<'a> { + control_queue: Arc>, + cursor_queue: Arc>, + transport: Arc, + displays: Vec, +} + +impl VirtGpuAdapter<'_> { async fn send_request(&self, request: Dma) -> Result, Error> { let header = Dma::new(ControlHeader::default())?; let command = ChainBuilder::new() @@ -49,166 +65,161 @@ impl GpuScheme<'_> { Ok(header) } - async fn flush_resource(&self, flush: ResourceFlush) -> Result<(), Error> { + async fn flush_resource_inner(&self, flush: ResourceFlush) -> Result<(), Error> { let header = self.send_request(Dma::new(flush)?).await?; assert_eq!(header.ty.get(), CommandTy::RespOkNodata); Ok(()) } +} - async fn res_for_screen( - &mut self, - vt: usize, - screen: usize, - ) -> Result<(ResourceId, *mut u8), Error> { - if let Some(res) = self.vts.entry(vt).or_default().get(&screen) { - return Ok((res.id, res.sgl.as_ptr())); - } +impl GraphicsAdapter for VirtGpuAdapter<'_> { + type Resource = VirtGpuResource; - let bpp = 32; - let fb_size = - self.displays[screen].width as usize * self.displays[screen].height as usize * bpp / 8; - let sgl = sgl::Sgl::new(fb_size)?; - - unsafe { - core::ptr::write_bytes(sgl.as_ptr() as *mut u8, 255, fb_size); - } - - let res_id = ResourceId::alloc(); - - // Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`. - let mut request = Dma::new(ResourceCreate2d::default())?; - - request.set_width(self.displays[screen].width); - request.set_height(self.displays[screen].height); - request.set_format(ResourceFormat::Bgrx); - request.set_resource_id(res_id); - - let header = self.send_request(request).await?; - assert_eq!(header.ty.get(), CommandTy::RespOkNodata); - - // Use the allocated framebuffer from tthe guest ram, and attach it as backing - // storage to the resource just created, using `VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING`. - - let mut mem_entries = unsafe { Dma::zeroed_slice(sgl.chunks().len())?.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))?; - let header = Dma::new(ControlHeader::default())?; - 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.get(), CommandTy::RespOkNodata); - - let res = self - .vts - .entry(vt) - .or_default() - .entry(screen) - .or_insert(GpuResource { id: res_id, sgl }); - Ok((res.id, res.sgl.as_ptr())) + fn displays(&self) -> Vec { + self.displays.iter().enumerate().map(|(i, _)| i).collect() } - async fn set_scanout(&mut self, vt: usize, screen: usize) -> Result<(), Error> { - let (res_id, _) = self.res_for_screen(vt, screen).await?; - - let scanout_request = Dma::new(SetScanout::new( - screen as u32, - res_id, - GpuRect::new( - 0, - 0, - self.displays[screen].width, - self.displays[screen].height, - ), - ))?; - let header = self.send_request(scanout_request).await?; - assert_eq!(header.ty.get(), CommandTy::RespOkNodata); - - self.flush(vt, screen, None).await?; - - Ok(()) + fn display_size(&self, display_id: usize) -> (u32, u32) { + ( + self.displays[display_id].width, + self.displays[display_id].height, + ) } - /// If `damage` is `None`, the entire screen is flushed. - async fn flush( - &mut self, - vt: usize, - screen: usize, - damage: Option<&[Damage]>, - ) -> Result<(), Error> { - let (res_id, _) = self.res_for_screen(vt, screen).await?; + fn create_resource(&mut self, width: u32, height: u32) -> Self::Resource { + futures::executor::block_on(async { + let bpp = 32; + let fb_size = width as usize * height as usize * bpp / 8; + let sgl = sgl::Sgl::new(fb_size).unwrap(); - let req = Dma::new(XferToHost2d::new( - res_id, - GpuRect { - x: 0, - y: 0, - width: self.displays[screen].width, - height: self.displays[screen].height, - }, - 0, - ))?; - let header = self.send_request(req).await?; - assert_eq!(header.ty.get(), CommandTy::RespOkNodata); - - if let Some(damage) = damage { - for damage in damage { - self.flush_resource(ResourceFlush::new( - res_id, - damage - .clip( - self.displays[screen].width as i32, - self.displays[screen].height as i32, - ) - .into(), - )) - .await?; + unsafe { + core::ptr::write_bytes(sgl.as_ptr() as *mut u8, 255, fb_size); } - } else { - self.flush_resource(ResourceFlush::new( - res_id, + + let res_id = ResourceId::alloc(); + + // Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`. + let mut request = Dma::new(ResourceCreate2d::default()).unwrap(); + + request.set_width(width); + request.set_height(height); + request.set_format(ResourceFormat::Bgrx); + request.set_resource_id(res_id); + + let header = self.send_request(request).await.unwrap(); + assert_eq!(header.ty.get(), CommandTy::RespOkNodata); + + // Use the allocated framebuffer from tthe guest ram, and attach it as backing + // storage to the resource just created, using `VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING`. + + 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 header = Dma::new(ControlHeader::default()).unwrap(); + 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.get(), CommandTy::RespOkNodata); + + VirtGpuResource { + id: res_id, + sgl, + width, + height, + } + }) + } + + fn map_resource(&mut self, resource: &Self::Resource) -> *mut u8 { + resource.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, + self.displays[display_id].width, + self.displays[display_id].height, + ), + )) + .unwrap(); + let header = self.send_request(scanout_request).await.unwrap(); + assert_eq!(header.ty.get(), CommandTy::RespOkNodata); + }); + + self.flush_resource(display_id, resource, None); + } + + fn flush_resource( + &mut self, + _display_id: usize, + resource: &Self::Resource, + damage: Option<&[Damage]>, + ) { + futures::executor::block_on(async { + let req = Dma::new(XferToHost2d::new( + resource.id, GpuRect { x: 0, y: 0, - width: self.displays[screen].width, - height: self.displays[screen].height, + width: resource.width, + height: resource.height, }, + 0, )) - .await?; - } - Ok(()) + .unwrap(); + let header = self.send_request(req).await.unwrap(); + assert_eq!(header.ty.get(), 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, + }, + )) + .await + .unwrap(); + } + }); } } -struct Handle { - screen: usize, - vt: usize, -} - pub struct GpuScheme<'a> { - control_queue: Arc>, - cursor_queue: Arc>, - transport: Arc, + pub inner: GraphicsScheme>, pub inputd_handle: inputd::DisplayHandle, - - handles: BTreeMap, - /// Counter used for file descriptor allocation. - next_id: AtomicUsize, - - active_vt: usize, - vts: HashMap>, - displays: Vec, } impl<'a> GpuScheme<'a> { @@ -223,17 +234,16 @@ impl<'a> GpuScheme<'a> { let inputd_handle = inputd::DisplayHandle::new("virtio-gpu").unwrap(); Ok(Self { - control_queue, - cursor_queue, - transport, + inner: GraphicsScheme::new( + VirtGpuAdapter { + control_queue, + cursor_queue, + transport, + displays, + }, + "display.virtio-gpu".to_owned(), + ), inputd_handle, - - handles: BTreeMap::new(), - next_id: AtomicUsize::new(0), - - active_vt: 0, - vts: HashMap::new(), - displays, }) } @@ -279,130 +289,4 @@ impl<'a> GpuScheme<'a> { Ok(response) } - - pub fn handle_vt_event(&mut self, vt_event: VtEvent) { - match vt_event.kind { - VtEventKind::Activate => { - log::info!("activate {}", vt_event.vt); - - for id in 0..self.displays.len() { - log::warn!("virtio-gpu: activating"); - - futures::executor::block_on(self.set_scanout(vt_event.vt, id)).unwrap(); - } - - self.active_vt = vt_event.vt; - } - - VtEventKind::Deactivate => { - log::info!("deactivate {}", vt_event.vt); - // nothing to do :) - } - - VtEventKind::Resize => { - log::warn!("virtio-gpu: resize is not implemented yet") - } - } - } -} - -impl<'a> Scheme for GpuScheme<'a> { - fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result { - if path.is_empty() { - return Err(SysError::new(EINVAL)); - } - - let mut parts = path.split('/'); - let mut screen = parts.next().unwrap_or("").split('.'); - - let vt = screen.next().unwrap_or("").parse::().unwrap(); - let id = screen.next().unwrap_or("").parse::().unwrap_or(0); - - dbg!(vt, id); - - if id >= self.displays.len() { - return Err(SysError::new(EINVAL)); - }; - - let fd = self.next_id.fetch_add(1, Ordering::SeqCst); - self.handles.insert(fd, Handle { screen: id, vt }); - Ok(fd) - } - - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result { - let handle = self.handles.get(&id).unwrap(); - let path = format!( - "display.virtio-gpu:3.0/{}/{}", - self.displays[handle.screen].width, self.displays[handle.screen].height - ); - buf[..path.len()].copy_from_slice(path.as_bytes()); - Ok(path.len()) - } - - fn fsync(&mut self, id: usize) -> syscall::Result { - let handle = self.handles.get(&id).ok_or(SysError::new(EBADF))?; - if handle.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 scanout anyway - return Ok(0); - } - futures::executor::block_on(self.flush(handle.vt, handle.screen, None)).unwrap(); - Ok(0) - } - - fn read( - &mut self, - id: usize, - _buf: &mut [u8], - _offset: u64, - _fcntl_flags: u32, - ) -> syscall::Result { - let _handle = self.handles.get(&id).ok_or(SysError::new(EBADF))?; - Err(SysError::new(EINVAL)) - } - - fn write( - &mut self, - id: usize, - buf: &[u8], - _offset: u64, - _fcntl_flags: u32, - ) -> syscall::Result { - let handle = self.handles.get(&id).ok_or(SysError::new(EBADF))?; - - if handle.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 scanout anyway - return Ok(buf.len()); - } - - let damage = unsafe { - core::slice::from_raw_parts( - buf.as_ptr() as *const Damage, - buf.len() / core::mem::size_of::(), - ) - }; - - futures::executor::block_on(self.flush(handle.vt, handle.screen, Some(damage))).unwrap(); - - Ok(buf.len()) - } - - fn close(&mut self, id: usize) -> syscall::Result { - self.handles.remove(&id).ok_or(SysError::new(EBADF))?; - Ok(0) - } - fn mmap_prep( - &mut self, - id: usize, - offset: u64, - size: usize, - flags: MapFlags, - ) -> syscall::Result { - log::info!("KSMSG MMAP {} {:?} {} {}", id, flags, offset, size); - let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?; - let (_, ptr) = - futures::executor::block_on(self.res_for_screen(handle.vt, handle.screen)).unwrap(); - Ok(unsafe { ptr.offset(offset as isize) } as usize) - } }