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.
This commit is contained in:
Generated
+14
-2
@@ -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",
|
||||
|
||||
@@ -19,6 +19,7 @@ members = [
|
||||
"graphics/bgad",
|
||||
"graphics/console-draw",
|
||||
"graphics/fbbootlogd",
|
||||
"graphics/driver-graphics",
|
||||
"graphics/fbcond",
|
||||
"graphics/vesad",
|
||||
"graphics/virtio-gpud",
|
||||
|
||||
@@ -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" }
|
||||
@@ -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<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 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<T: GraphicsAdapter> {
|
||||
adapter: T,
|
||||
|
||||
scheme_name: String,
|
||||
socket: Socket,
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
|
||||
active_vt: usize,
|
||||
vts_res: HashMap<usize, HashMap<usize, T::Resource>>,
|
||||
}
|
||||
|
||||
enum Handle {
|
||||
Screen { vt: usize, screen: usize },
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> GraphicsScheme<T> {
|
||||
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<T: GraphicsAdapter> Scheme for GraphicsScheme<T> {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
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::<usize>().unwrap();
|
||||
let id = screen.next().unwrap_or("").parse::<usize>().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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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::<Damage>(),
|
||||
)
|
||||
};
|
||||
|
||||
self.adapter.flush_resource(*screen, resource, Some(damage));
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> syscall::Result<usize> {
|
||||
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<usize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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::<u8>()
|
||||
}
|
||||
}
|
||||
impl Drop for OffscreenBuffer {
|
||||
fn drop(&mut self) {
|
||||
|
||||
+18
-49
@@ -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<FrameBuffer>, 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<FrameBuffer>, 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<FrameBuffer>, 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<FrameBuffer>, 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-227
@@ -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<FrameBuffer>,
|
||||
}
|
||||
|
||||
pub struct DisplayScheme {
|
||||
framebuffers: Vec<FrameBuffer>,
|
||||
active: VtIndex,
|
||||
pub vts: BTreeMap<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>,
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, Handle>,
|
||||
pub inputd_handle: inputd::DisplayHandle,
|
||||
}
|
||||
impl GraphicsAdapter for FbAdapter {
|
||||
type Resource = GraphicScreen;
|
||||
|
||||
impl DisplayScheme {
|
||||
pub fn new(framebuffers: Vec<FrameBuffer>, spec: &[()]) -> DisplayScheme {
|
||||
let mut inputd_handle = inputd::DisplayHandle::new_early("vesa").unwrap();
|
||||
|
||||
let mut vts = BTreeMap::<VtIndex, BTreeMap<ScreenIndex, GraphicScreen>>::new();
|
||||
|
||||
for &() in spec.iter() {
|
||||
let mut screens = BTreeMap::<ScreenIndex, GraphicScreen>::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<usize> {
|
||||
(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<usize> {
|
||||
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::<usize>().unwrap_or(1));
|
||||
let screen_i = ScreenIndex(vt_screen.next().unwrap_or("").parse::<usize>().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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<usize> {
|
||||
let sync_rects = unsafe {
|
||||
slice::from_raw_parts(
|
||||
buf.as_ptr() as *const Damage,
|
||||
buf.len() / mem::size_of::<Damage>(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(framebuffer) = framebuffer {
|
||||
self.sync(framebuffer, sync_rects);
|
||||
}
|
||||
|
||||
Ok(sync_rects.len() * mem::size_of::<Damage>())
|
||||
}
|
||||
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(
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+165
-281
@@ -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<GpuRect> 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<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
transport: Arc<dyn Transport>,
|
||||
displays: Vec<Display>,
|
||||
}
|
||||
|
||||
impl VirtGpuAdapter<'_> {
|
||||
async fn send_request<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, 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<usize> {
|
||||
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<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
transport: Arc<dyn Transport>,
|
||||
pub inner: GraphicsScheme<VirtGpuAdapter<'a>>,
|
||||
pub inputd_handle: inputd::DisplayHandle,
|
||||
|
||||
handles: BTreeMap<usize /* file descriptor */, Handle>,
|
||||
/// Counter used for file descriptor allocation.
|
||||
next_id: AtomicUsize,
|
||||
|
||||
active_vt: usize,
|
||||
vts: HashMap<usize, HashMap<usize, GpuResource>>,
|
||||
displays: Vec<Display>,
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
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::<usize>().unwrap();
|
||||
let id = screen.next().unwrap_or("").parse::<usize>().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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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::<Damage>(),
|
||||
)
|
||||
};
|
||||
|
||||
futures::executor::block_on(self.flush(handle.vt, handle.screen, Some(damage))).unwrap();
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> syscall::Result<usize> {
|
||||
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<usize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user