mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 10:10:41 +00:00
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>
This commit is contained in:
parent
1617c21918
commit
60eb1fbe89
4 changed files with 247 additions and 3 deletions
|
@ -666,7 +666,7 @@ pub enum DisplayBackend {
|
|||
/// Open a connection to the X server at the given display if given.
|
||||
X(Option<String>),
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
234
gpu_display/src/gpu_display_stub.rs
Normal file
234
gpu_display/src/gpu_display_stub.rs
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue