From 60eb1fbe89fab558bf781c6c02496a345b5d6a4c Mon Sep 17 00:00:00 2001 From: Jason Macnak Date: Thu, 9 Jan 2020 14:36:29 -0800 Subject: gpu_display: implement stub display Adds a stub display that emulates a display without actually displaying contents anywhere. This is needed for transitioning Cuttlefish to always using minigbm as its gralloc implementation. Cuttlefish currently uses a custom gralloc and hwcomposer implementation when running without hardware acceleration. The Cuttlefish team would like to start with removing our custom gralloc implementation and use minigbm. For this, we need to add a virtio 2D backend to crosvm. Our hwcomposer implementation currenlly sends framebuffers from the guest to the host via sockets. The gpu backend still requires a display so we need a stub display to use with the 2D backend for the period of time while we are either still using our hwcomposer implementation or until our hwcomposer implementation is updated to use the virtio backend for display. BUG=b:123764798 BUG=chromium:1033787 TEST=built and launched with Cuttlefish locally Change-Id: I1a7e259d914a53252200c59589c4142e76c6b96b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1993947 Reviewed-by: Gurchetan Singh Tested-by: kokoro Commit-Queue: Jason Macnak --- gpu_display/src/gpu_display_stub.rs | 234 ++++++++++++++++++++++++++++++++++++ gpu_display/src/lib.rs | 10 ++ 2 files changed, 244 insertions(+) create mode 100644 gpu_display/src/gpu_display_stub.rs (limited to 'gpu_display') diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs new file mode 100644 index 0000000..9b33a96 --- /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 1d9fcdc..07358a0 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, -- cgit 1.4.1