diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs index b410e3e017..143b9efe21 100644 --- a/devices/src/virtio/gpu/mod.rs +++ b/devices/src/virtio/gpu/mod.rs @@ -666,7 +666,7 @@ pub enum DisplayBackend { /// Open a connection to the X server at the given display if given. X(Option), /// Emulate a display without actually displaying it. - Null, + Stub, } impl DisplayBackend { @@ -674,7 +674,7 @@ impl DisplayBackend { match self { DisplayBackend::Wayland(path) => GpuDisplay::open_wayland(path.as_ref()), DisplayBackend::X(display) => GpuDisplay::open_x(display.as_ref()), - DisplayBackend::Null => unimplemented!(), + DisplayBackend::Stub => GpuDisplay::open_stub(), } } diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs new file mode 100644 index 0000000000..9b33a96d61 --- /dev/null +++ b/gpu_display/src/gpu_display_stub.rs @@ -0,0 +1,234 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::collections::BTreeMap; +use std::num::NonZeroU32; +use std::os::unix::io::{AsRawFd, RawFd}; + +use crate::{DisplayT, EventDevice, GpuDisplayError, GpuDisplayFramebuffer}; + +use data_model::VolatileSlice; +use sys_util::EventFd; + +type SurfaceId = NonZeroU32; + +#[allow(unused_variables)] +struct Buffer { + width: u32, + height: u32, + bytes_per_pixel: u32, + bytes_total: u64, + bytes: Vec, +} + +impl Drop for Buffer { + fn drop(&mut self) {} +} + +impl Buffer { + fn as_volatile_slice(&mut self) -> VolatileSlice { + unsafe { VolatileSlice::new(self.bytes.as_mut_ptr(), self.bytes_total) } + } + + fn stride(&self) -> usize { + return (self.bytes_per_pixel as usize) * (self.width as usize); + } + + fn bytes_per_pixel(&self) -> usize { + return self.bytes_per_pixel as usize; + } +} + +struct Surface { + width: u32, + height: u32, + buffer: Option, +} + +impl Surface { + fn create(width: u32, height: u32) -> Result { + Ok(Surface { + width, + height, + buffer: None, + }) + } + + /// Gets the buffer at buffer_index, allocating it if necessary. + fn lazily_allocate_buffer(&mut self) -> Option<&mut Buffer> { + if self.buffer.is_none() { + // XRGB8888 + let bytes_per_pixel = 4; + let bytes_total = (self.width as u64) * (self.height as u64) * (bytes_per_pixel as u64); + + self.buffer = Some(Buffer { + width: self.width, + height: self.height, + bytes_per_pixel, + bytes_total, + bytes: vec![0; bytes_total as usize], + }); + } + + self.buffer.as_mut() + } + + /// Gets the next framebuffer, allocating if necessary. + fn framebuffer(&mut self) -> Option { + let framebuffer = self.lazily_allocate_buffer()?; + let framebuffer_stride = framebuffer.stride() as u32; + let framebuffer_bytes_per_pixel = framebuffer.bytes_per_pixel() as u32; + Some(GpuDisplayFramebuffer::new( + framebuffer.as_volatile_slice(), + framebuffer_stride, + framebuffer_bytes_per_pixel, + )) + } + + fn flip(&mut self) {} +} + +impl Drop for Surface { + fn drop(&mut self) {} +} + +struct SurfacesHelper { + next_surface_id: SurfaceId, + surfaces: BTreeMap, +} + +impl SurfacesHelper { + fn new() -> SurfacesHelper { + SurfacesHelper { + next_surface_id: SurfaceId::new(1).unwrap(), + surfaces: Default::default(), + } + } + + fn create_surface(&mut self, width: u32, height: u32) -> Result { + let new_surface = Surface::create(width, height)?; + let new_surface_id = self.next_surface_id; + + self.surfaces.insert(new_surface_id, new_surface); + self.next_surface_id = SurfaceId::new(self.next_surface_id.get() + 1).unwrap(); + + Ok(new_surface_id.get()) + } + + fn get_surface(&mut self, surface_id: u32) -> Option<&mut Surface> { + SurfaceId::new(surface_id).and_then(move |id| self.surfaces.get_mut(&id)) + } + + fn destroy_surface(&mut self, surface_id: u32) { + SurfaceId::new(surface_id).and_then(|id| self.surfaces.remove(&id)); + } + + fn flip_surface(&mut self, surface_id: u32) { + if let Some(surface) = self.get_surface(surface_id) { + surface.flip(); + } + } +} + +pub struct DisplayStub { + /// This eventfd is never triggered and is used solely to fulfill AsRawFd. + eventfd: EventFd, + surfaces: SurfacesHelper, +} + +impl DisplayStub { + pub fn new() -> Result { + let eventfd = EventFd::new().map_err(|_| GpuDisplayError::CreateEventFd)?; + + Ok(DisplayStub { + eventfd, + surfaces: SurfacesHelper::new(), + }) + } +} + +impl DisplayT for DisplayStub { + fn dispatch_events(&mut self) {} + + fn create_surface( + &mut self, + parent_surface_id: Option, + width: u32, + height: u32, + ) -> Result { + if parent_surface_id.is_some() { + return Err(GpuDisplayError::Unsupported); + } + self.surfaces.create_surface(width, height) + } + + fn release_surface(&mut self, surface_id: u32) { + self.surfaces.destroy_surface(surface_id); + } + + fn framebuffer(&mut self, surface_id: u32) -> Option { + self.surfaces + .get_surface(surface_id) + .and_then(|s| s.framebuffer()) + } + + fn next_buffer_in_use(&self, _surface_id: u32) -> bool { + false + } + + fn flip(&mut self, surface_id: u32) { + self.surfaces.flip_surface(surface_id); + } + + fn close_requested(&self, _surface_id: u32) -> bool { + false + } + + fn import_dmabuf( + &mut self, + _fd: RawFd, + _offset: u32, + _stride: u32, + _modifiers: u64, + _width: u32, + _height: u32, + _fourcc: u32, + ) -> Result { + Err(GpuDisplayError::Unsupported) + } + + fn release_import(&mut self, _import_id: u32) { + // unsupported + } + + fn commit(&mut self, _surface_id: u32) { + // unsupported + } + + fn flip_to(&mut self, _surface_id: u32, _import_id: u32) { + // unsupported + } + + fn set_position(&mut self, _surface_id: u32, _x: u32, _y: u32) { + // unsupported + } + + fn import_event_device(&mut self, _event_device: EventDevice) -> Result { + Err(GpuDisplayError::Unsupported) + } + + fn release_event_device(&mut self, _event_device_id: u32) { + // unsupported + } + + fn attach_event_device(&mut self, _surface_id: u32, _event_device_id: u32) { + // unsupported + } +} + +impl AsRawFd for DisplayStub { + fn as_raw_fd(&self) -> RawFd { + self.eventfd.as_raw_fd() + } +} diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs index 1d9fcdce70..07358a0e86 100644 --- a/gpu_display/src/lib.rs +++ b/gpu_display/src/lib.rs @@ -12,6 +12,7 @@ use data_model::VolatileSlice; use sys_util::Error as SysError; mod event_device; +mod gpu_display_stub; mod gpu_display_wl; #[cfg(feature = "x")] mod gpu_display_x; @@ -26,6 +27,8 @@ pub enum GpuDisplayError { Allocate, /// Connecting to the compositor failed. Connect, + /// Creating event file descriptor failed. + CreateEventFd, /// Creating shared memory failed. CreateShm(SysError), /// Setting the size of shared memory failed. @@ -51,6 +54,7 @@ impl Display for GpuDisplayError { match self { Allocate => write!(f, "internal allocation failed"), Connect => write!(f, "failed to connect to compositor"), + CreateEventFd => write!(f, "failed to create event file descriptor"), CreateShm(e) => write!(f, "failed to create shared memory: {}", e), CreateSurface => write!(f, "failed to crate surface on the compositor"), FailedImport => write!(f, "failed to import a buffer to the compositor"), @@ -197,6 +201,12 @@ impl GpuDisplay { Ok(GpuDisplay { inner }) } + pub fn open_stub() -> Result { + let display = gpu_display_stub::DisplayStub::new()?; + let inner = Box::new(display); + Ok(GpuDisplay { inner }) + } + /// Imports a dmabuf to the compositor for use as a surface buffer and returns a handle to it. pub fn import_dmabuf( &mut self, diff --git a/src/linux.rs b/src/linux.rs index c7e6b0ba51..d6b65191ea 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -597,7 +597,7 @@ fn create_gpu_device( let mut display_backends = vec![ virtio::DisplayBackend::X(x_display), - virtio::DisplayBackend::Null, + virtio::DisplayBackend::Stub, ]; if let Some(socket_path) = wayland_socket_path {