Merge branch 'driver_graphics_crate' into 'master'
Add driver-graphics crate See merge request redox-os/drivers!223
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