graphics/virtio-gpud: Move resources from Display to GpuScheme

Resources are global for the entire virtio-gpud device, not local to a
single display. In the future resource creation will become entirely
detached from specific displays.
This commit is contained in:
bjorn3
2024-12-26 17:53:45 +01:00
parent 2344206c21
commit 62f1a80c3e
+87 -89
View File
@@ -1,4 +1,3 @@
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -32,39 +31,18 @@ struct GpuResource {
sgl: sgl::Sgl,
}
pub struct Display<'a> {
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
active_vt: RefCell<usize>,
vts: RefCell<HashMap<usize, GpuResource>>,
#[derive(Debug, Copy, Clone)]
pub struct Display {
width: u32,
height: u32,
id: usize,
}
impl<'a> Display<'a> {
pub fn new(
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
id: usize,
) -> Self {
impl Display {
pub fn new() -> Self {
Self {
control_queue,
cursor_queue,
active_vt: RefCell::new(0),
vts: RefCell::new(HashMap::new()),
// FIXME use the actual screen size
width: 1920,
height: 1080,
transport,
id,
}
}
@@ -75,7 +53,9 @@ impl<'a> Display<'a> {
buffer[..path.len()].copy_from_slice(path.as_bytes());
Ok(path.len())
}
}
impl GpuScheme<'_> {
async fn send_request<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
let header = Dma::new(ControlHeader::default())?;
let command = ChainBuilder::new()
@@ -94,13 +74,18 @@ impl<'a> Display<'a> {
Ok(())
}
async fn res_for_screen(&self, vt: usize) -> Result<(ResourceId, *mut u8), Error> {
if let Some(res) = self.vts.borrow().get(&vt) {
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()));
}
let bpp = 32;
let fb_size = self.width as usize * self.height as usize * bpp / 8;
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 {
@@ -112,8 +97,8 @@ impl<'a> Display<'a> {
// Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`.
let mut request = Dma::new(ResourceCreate2d::default())?;
request.set_width(self.width);
request.set_height(self.height);
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);
@@ -143,40 +128,52 @@ impl<'a> Display<'a> {
self.control_queue.send(command).await;
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
let mut mapped_vts = self.vts.borrow_mut();
let res = mapped_vts
let res = self
.vts
.entry(vt)
.or_default()
.entry(screen)
.or_insert(GpuResource { id: res_id, sgl });
Ok((res.id, res.sgl.as_ptr()))
}
async fn set_scanout(&self, vt: usize) -> Result<(), Error> {
let (res_id, _) = self.res_for_screen(vt).await?;
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(
self.id as u32,
screen as u32,
res_id,
GpuRect::new(0, 0, self.width, self.height),
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, None).await?;
self.flush(vt, screen, None).await?;
Ok(())
}
/// If `damage` is `None`, the entire screen is flushed.
async fn flush(&self, vt: usize, damage: Option<&[Damage]>) -> Result<(), Error> {
let (res_id, _) = self.res_for_screen(vt).await?;
async fn flush(
&mut self,
vt: usize,
screen: usize,
damage: Option<&[Damage]>,
) -> Result<(), Error> {
let (res_id, _) = self.res_for_screen(vt, screen).await?;
let req = Dma::new(XferToHost2d::new(
res_id,
GpuRect {
x: 0,
y: 0,
width: self.width,
height: self.height,
width: self.displays[screen].width,
height: self.displays[screen].height,
},
0,
))?;
@@ -187,7 +184,12 @@ impl<'a> Display<'a> {
for damage in damage {
self.flush_resource(ResourceFlush::new(
res_id,
damage.clip(self.width as i32, self.height as i32).into(),
damage
.clip(
self.displays[screen].width as i32,
self.displays[screen].height as i32,
)
.into(),
))
.await?;
}
@@ -197,8 +199,8 @@ impl<'a> Display<'a> {
GpuRect {
x: 0,
y: 0,
width: self.width,
height: self.height,
width: self.displays[screen].width,
height: self.displays[screen].height,
},
))
.await?;
@@ -207,17 +209,24 @@ impl<'a> Display<'a> {
}
}
struct Handle<'a> {
display: Arc<Display<'a>>,
struct Handle {
screen: usize,
vt: usize,
}
pub struct GpuScheme<'a> {
handles: BTreeMap<usize /* file descriptor */, Handle<'a>>,
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
pub inputd_handle: inputd::DisplayHandle,
handles: BTreeMap<usize /* file descriptor */, Handle>,
/// Counter used for file descriptor allocation.
next_id: AtomicUsize,
displays: Vec<Arc<Display<'a>>>,
pub inputd_handle: inputd::DisplayHandle,
active_vt: usize,
vts: HashMap<usize, HashMap<usize, GpuResource>>,
displays: Vec<Display>,
}
impl<'a> GpuScheme<'a> {
@@ -227,50 +236,42 @@ impl<'a> GpuScheme<'a> {
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
) -> Result<GpuScheme<'a>, Error> {
let displays = Self::probe(
control_queue.clone(),
cursor_queue.clone(),
transport.clone(),
config,
)
.await?;
let displays = Self::probe(control_queue.clone(), config).await?;
let inputd_handle = inputd::DisplayHandle::new("virtio-gpu").unwrap();
Ok(Self {
control_queue,
cursor_queue,
transport,
inputd_handle,
handles: BTreeMap::new(),
next_id: AtomicUsize::new(0),
active_vt: 0,
vts: HashMap::new(),
displays,
inputd_handle,
})
}
async fn probe(
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
config: &GpuConfig,
) -> Result<Vec<Arc<Display<'a>>>, Error> {
) -> Result<Vec<Display>, Error> {
let mut display_info = Self::get_display_info(control_queue.clone()).await?;
let displays = &mut display_info.display_info[..config.num_scanouts() as usize];
let mut result = vec![];
for (id, info) in displays.iter().enumerate() {
for info in displays.iter() {
log::info!(
"virtio-gpu: opening display ({}x{}px)",
info.rect().width,
info.rect().height
);
let display = Display::new(
control_queue.clone(),
cursor_queue.clone(),
transport.clone(),
id,
);
result.push(Arc::new(display));
result.push(Display::new());
}
Ok(result)
@@ -299,13 +300,13 @@ impl<'a> GpuScheme<'a> {
VtEventKind::Activate => {
log::info!("activate {}", vt_event.vt);
for display in &self.displays {
for id in 0..self.displays.len() {
log::warn!("virtio-gpu: activating");
futures::executor::block_on(display.set_scanout(vt_event.vt)).unwrap();
*display.active_vt.borrow_mut() = vt_event.vt;
futures::executor::block_on(self.set_scanout(vt_event.vt, id)).unwrap();
}
self.active_vt = vt_event.vt;
}
VtEventKind::Deactivate => {
@@ -334,33 +335,30 @@ impl<'a> Scheme for GpuScheme<'a> {
dbg!(vt, id);
let display = self.displays.get(id).ok_or(SysError::new(EINVAL))?;
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 {
display: display.clone(),
vt,
},
);
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 bytes_copied = futures::executor::block_on(handle.display.get_fpath(buf)).unwrap();
let bytes_copied =
futures::executor::block_on(self.displays[handle.screen].get_fpath(buf)).unwrap();
Ok(bytes_copied)
}
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
if handle.vt != *handle.display.active_vt.borrow() {
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(handle.display.flush(handle.vt, None)).unwrap();
futures::executor::block_on(self.flush(handle.vt, handle.screen, None)).unwrap();
Ok(0)
}
@@ -385,7 +383,7 @@ impl<'a> Scheme for GpuScheme<'a> {
) -> syscall::Result<usize> {
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
if handle.vt != *handle.display.active_vt.borrow() {
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());
@@ -398,7 +396,7 @@ impl<'a> Scheme for GpuScheme<'a> {
)
};
futures::executor::block_on(handle.display.flush(handle.vt, Some(damage))).unwrap();
futures::executor::block_on(self.flush(handle.vt, handle.screen, Some(damage))).unwrap();
Ok(buf.len())
}
@@ -416,7 +414,7 @@ impl<'a> Scheme for GpuScheme<'a> {
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(handle.display.res_for_screen(handle.vt)).unwrap();
futures::executor::block_on(self.res_for_screen(handle.vt, handle.screen)).unwrap();
Ok(unsafe { ptr.offset(offset as isize) } as usize)
}
}