summary refs log tree commit diff
path: root/gpu_display
diff options
context:
space:
mode:
authorJason Macnak <natsu@google.com>2020-01-09 14:36:29 -0800
committerCommit Bot <commit-bot@chromium.org>2020-01-14 00:31:00 +0000
commit60eb1fbe89fab558bf781c6c02496a345b5d6a4c (patch)
treeff34abd184c7f2bee3ee0627d51089f59249c501 /gpu_display
parent1617c21918c30479ab705f45d4ef3c21ccc0d4fa (diff)
downloadcrosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar.gz
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar.bz2
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar.lz
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar.xz
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.tar.zst
crosvm-60eb1fbe89fab558bf781c6c02496a345b5d6a4c.zip
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 <gurchetansingh@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Jason Macnak <natsu@google.com>
Diffstat (limited to 'gpu_display')
-rw-r--r--gpu_display/src/gpu_display_stub.rs234
-rw-r--r--gpu_display/src/lib.rs10
2 files changed, 244 insertions, 0 deletions
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<u8>,
+}
+
+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<Buffer>,
+}
+
+impl Surface {
+    fn create(width: u32, height: u32) -> Result<Surface, GpuDisplayError> {
+        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<GpuDisplayFramebuffer> {
+        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<SurfaceId, Surface>,
+}
+
+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<u32, GpuDisplayError> {
+        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<DisplayStub, GpuDisplayError> {
+        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<u32>,
+        width: u32,
+        height: u32,
+    ) -> Result<u32, GpuDisplayError> {
+        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<GpuDisplayFramebuffer> {
+        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<u32, GpuDisplayError> {
+        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<u32, GpuDisplayError> {
+        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<GpuDisplay, GpuDisplayError> {
+        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,