graphics/virtio-gpud: Handle VIRTIO_GPU_EVENT_DISPLAY

This doesn't yet provide a way for individual consumers to resize their
own framebuffer in response to the display changing size.
This commit is contained in:
bjorn3
2025-06-29 18:04:42 +02:00
parent 181f0ea4ec
commit 27ca1c3296
3 changed files with 78 additions and 30 deletions
+28 -2
View File
@@ -31,7 +31,7 @@ use virtio_core::MSIX_PRIMARY_VECTOR;
mod scheme;
// const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
#[repr(C)]
@@ -457,6 +457,8 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
.transport
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
device.transport.setup_config_notify(MSIX_PRIMARY_VECTOR);
device.transport.run_device();
deamon.ready().unwrap();
@@ -471,6 +473,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
enum Source {
Input,
Scheme,
Interrupt,
}
}
@@ -490,8 +493,15 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
event::EventFlags::READ,
)
.unwrap();
event_queue
.subscribe(
device.irq_handle.as_raw_fd() as usize,
Source::Interrupt,
event::EventFlags::READ,
)
.unwrap();
let all = [Source::Input, Source::Scheme];
let all = [Source::Input, Source::Scheme, Source::Interrupt];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("virtio-gpud: failed to get next event").user_data))
@@ -510,6 +520,22 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
.tick()
.expect("virtio-gpud: failed to process scheme events");
}
Source::Interrupt => loop {
let before_gen = device.transport.config_generation();
let events = config.events_read.get();
if events & VIRTIO_GPU_EVENT_DISPLAY != 0 {
futures::executor::block_on(scheme.adapter_mut().update_displays(config))
.unwrap();
config.events_clear.set(VIRTIO_GPU_EVENT_DISPLAY);
}
let after_gen = device.transport.config_generation();
if before_gen == after_gen {
break;
}
},
}
}
+36 -28
View File
@@ -64,6 +64,40 @@ pub struct VirtGpuAdapter<'a> {
}
impl VirtGpuAdapter<'_> {
pub async fn update_displays(&mut self, config: &mut GpuConfig) -> Result<(), Error> {
let mut display_info = self.get_display_info().await?;
let raw_displays = &mut display_info.display_info[..config.num_scanouts() as usize];
self.displays.resize(
raw_displays.len(),
Display {
width: 0,
height: 0,
active_resource: None,
},
);
for (i, info) in raw_displays.iter().enumerate() {
log::info!(
"virtio-gpu: display {i} ({}x{}px)",
info.rect.width,
info.rect.height
);
if info.rect.width == 0 || info.rect.height == 0 {
// QEMU gives all displays other than the first a zero width and height, but trying
// to attach a zero sized framebuffer to the display will result an error, so
// default to 640x480px.
self.displays[i].width = 640;
self.displays[i].height = 480;
} else {
self.displays[i].width = info.rect.width;
self.displays[i].height = info.rect.height;
}
}
Ok(())
}
async fn send_request<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
let header = Dma::new(ControlHeader::default())?;
let command = ChainBuilder::new()
@@ -350,7 +384,7 @@ pub struct GpuScheme {}
impl<'a> GpuScheme {
pub async fn new(
config: &'a mut GpuConfig,
config: &mut GpuConfig,
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
@@ -362,33 +396,7 @@ impl<'a> GpuScheme {
displays: vec![],
};
let mut display_info = adapter.get_display_info().await?;
let raw_displays = &mut display_info.display_info[..config.num_scanouts() as usize];
for info in raw_displays.iter() {
log::info!(
"virtio-gpu: opening display ({}x{}px)",
info.rect.width,
info.rect.height
);
if info.rect.width == 0 || info.rect.height == 0 {
// QEMU gives all displays other than the first a zero width and height, but trying
// to attach a zero sized framebuffer to the display will result an error, so
// default to 640x480px.
adapter.displays.push(Display {
width: 640,
height: 480,
active_resource: None,
});
} else {
adapter.displays.push(Display {
width: info.rect.width,
height: info.rect.height,
active_resource: None,
});
}
}
adapter.update_displays(config).await?;
let scheme = GraphicsScheme::new(adapter, "display.virtio-gpu".to_owned());
let handle = DisplayHandle::new("virtio-gpu").unwrap();
+14
View File
@@ -492,6 +492,12 @@ pub trait Transport: Sync + Send {
self.insert_status(DeviceStatusFlags::DRIVER_OK);
}
/// Request to be notified on configuration changes on the given MSI-X vector.
fn setup_config_notify(&self, vector: u16);
/// Each time the device configuration changes this number will be updated.
fn config_generation(&self) -> u32;
/// Creates a new queue.
///
/// ## Panics
@@ -601,6 +607,14 @@ impl Transport for StandardTransport<'_> {
assert!((confirm & DeviceStatusFlags::FEATURES_OK) == DeviceStatusFlags::FEATURES_OK);
}
fn setup_config_notify(&self, vector: u16) {
self.common.lock().unwrap().config_msix_vector.set(vector);
}
fn config_generation(&self) -> u32 {
u32::from(self.common.lock().unwrap().config_generation.get())
}
fn setup_queue(&self, vector: u16, irq_handle: &File) -> Result<Arc<Queue>, Error> {
let mut common = self.common.lock().unwrap();