diff options
author | Zach Reizner <zachr@google.com> | 2019-04-16 15:09:20 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-07-25 22:15:48 +0000 |
commit | f5285c647acacb4f25ef8cf9334254b976e71686 (patch) | |
tree | fcc238ec97736727a9c18b3b9de29be3dce3983e /gpu_display/src/lib.rs | |
parent | b2110bef59d72529d99c722df9b3e9a1d705e6f4 (diff) | |
download | crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.gz crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.bz2 crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.lz crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.xz crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.tar.zst crosvm-f5285c647acacb4f25ef8cf9334254b976e71686.zip |
gpu_display: add X11 backend
This change adds an X11 backend to the gpu_display crate. With this addition, the virtio-gpu device can display to traditional linux desktops that only have X11 output. Change-Id: I86c80cac91ca5bdc97588194a44040273ae69385 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1591572 Reviewed-by: Stéphane Marchesin <marcheu@chromium.org> Commit-Queue: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Auto-Submit: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'gpu_display/src/lib.rs')
-rw-r--r-- | gpu_display/src/lib.rs | 404 |
1 files changed, 151 insertions, 253 deletions
diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs index 11a6703..1bf9578 100644 --- a/gpu_display/src/lib.rs +++ b/gpu_display/src/lib.rs @@ -4,23 +4,16 @@ //! Crate for displaying simple surfaces and GPU buffers over wayland. -mod dwl; - -use std::cell::Cell; -use std::collections::HashMap; -use std::ffi::{CStr, CString}; use std::fmt::{self, Display}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; -use std::ptr::null_mut; - -use data_model::{VolatileMemory, VolatileSlice}; -use sys_util::{round_up_to_page_size, Error as SysError, MemoryMapping, SharedMemory}; -use crate::dwl::*; +use data_model::VolatileSlice; +use sys_util::Error as SysError; -const BUFFER_COUNT: usize = 2; -const BYTES_PER_PIXEL: u32 = 4; +mod gpu_display_wl; +#[cfg(feature = "x")] +mod gpu_display_x; /// An error generated by `GpuDisplay`. #[derive(Debug)] @@ -39,8 +32,12 @@ pub enum GpuDisplayError { FailedImport, /// The surface ID is invalid. InvalidSurfaceId, + /// A required feature was missing. + RequiredFeature(&'static str), /// The path is invalid. InvalidPath, + /// The method is unsupported by the implementation. + Unsupported, } impl Display for GpuDisplayError { @@ -51,65 +48,110 @@ impl Display for GpuDisplayError { Allocate => write!(f, "internal allocation failed"), Connect => write!(f, "failed to connect to compositor"), CreateShm(e) => write!(f, "failed to create shared memory: {}", e), - SetSize(e) => write!(f, "failed to set size of shared memory: {}", e), CreateSurface => write!(f, "failed to crate surface on the compositor"), FailedImport => write!(f, "failed to import a buffer to the compositor"), - InvalidSurfaceId => write!(f, "invalid surface ID"), InvalidPath => write!(f, "invalid path"), + InvalidSurfaceId => write!(f, "invalid surface ID"), + RequiredFeature(feature) => write!(f, "required feature was missing: {}", feature), + SetSize(e) => write!(f, "failed to set size of shared memory: {}", e), + Unsupported => write!(f, "unsupported by the implementation"), } } } -struct DwlContext(*mut dwl_context); -impl Drop for DwlContext { - fn drop(&mut self) { - if !self.0.is_null() { - // Safe given that we checked the pointer for non-null and it should always be of the - // correct type. - unsafe { - dwl_context_destroy(&mut self.0); - } - } - } +#[derive(Clone)] +pub struct GpuDisplayFramebuffer<'a> { + framebuffer: VolatileSlice<'a>, + slice: VolatileSlice<'a>, + stride: u32, + bytes_per_pixel: u32, } -struct DwlDmabuf(*mut dwl_dmabuf); -impl Drop for DwlDmabuf { - fn drop(&mut self) { - if !self.0.is_null() { - // Safe given that we checked the pointer for non-null and it should always be of the - // correct type. - unsafe { - dwl_dmabuf_destroy(&mut self.0); - } +impl<'a> GpuDisplayFramebuffer<'a> { + fn new( + framebuffer: VolatileSlice<'a>, + stride: u32, + bytes_per_pixel: u32, + ) -> GpuDisplayFramebuffer { + GpuDisplayFramebuffer { + framebuffer, + slice: framebuffer, + stride, + bytes_per_pixel, } } -} -struct DwlSurface(*mut dwl_surface); -impl Drop for DwlSurface { - fn drop(&mut self) { - if !self.0.is_null() { - // Safe given that we checked the pointer for non-null and it should always be of the - // correct type. - unsafe { - dwl_surface_destroy(&mut self.0); - } - } + fn sub_region( + &self, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Option<GpuDisplayFramebuffer<'a>> { + let x_byte_offset = x.checked_mul(self.bytes_per_pixel)?; + let y_byte_offset = y.checked_mul(self.stride)?; + let byte_offset = x_byte_offset.checked_add(y_byte_offset)?; + + let width_bytes = width.checked_mul(self.bytes_per_pixel)?; + let count = height + .checked_mul(self.stride)? + .checked_sub(self.stride)? + .checked_add(width_bytes)?; + let slice = self + .framebuffer + .sub_slice(byte_offset as u64, count as u64) + .unwrap(); + + Some(GpuDisplayFramebuffer { slice, ..*self }) + } + + pub fn as_volatile_slice(&self) -> VolatileSlice<'a> { + self.slice } -} -struct GpuDisplaySurface { - surface: DwlSurface, - buffer_size: usize, - buffer_index: Cell<usize>, - buffer_mem: MemoryMapping, + pub fn stride(&self) -> u32 { + self.stride + } } -impl GpuDisplaySurface { - fn surface(&self) -> *mut dwl_surface { - self.surface.0 +trait DisplayT: AsRawFd { + fn import_dmabuf( + &mut self, + fd: RawFd, + offset: u32, + stride: u32, + modifiers: u64, + width: u32, + height: u32, + fourcc: u32, + ) -> Result<u32, GpuDisplayError>; + fn release_import(&mut self, import_id: u32); + fn dispatch_events(&mut self); + fn create_surface( + &mut self, + parent_surface_id: Option<u32>, + width: u32, + height: u32, + ) -> Result<u32, GpuDisplayError>; + fn release_surface(&mut self, surface_id: u32); + fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer>; + fn framebuffer_region( + &mut self, + surface_id: u32, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Option<GpuDisplayFramebuffer> { + let framebuffer = self.framebuffer(surface_id)?; + framebuffer.sub_region(x, y, width, height) } + fn commit(&mut self, surface_id: u32); + fn next_buffer_in_use(&self, surface_id: u32) -> bool; + fn flip(&mut self, surface_id: u32); + fn flip_to(&mut self, surface_id: u32, import_id: u32); + fn close_requested(&self, surface_id: u32) -> bool; + fn set_position(&mut self, surface_id: u32, x: u32, y: u32); } /// A connection to the compositor and associated collection of state. @@ -117,51 +159,35 @@ impl GpuDisplaySurface { /// The user of `GpuDisplay` can use `AsRawFd` to poll on the compositor connection's file /// descriptor. When the connection is readable, `dispatch_events` can be called to process it. pub struct GpuDisplay { - ctx: DwlContext, - dmabufs: HashMap<u32, DwlDmabuf>, - dmabuf_next_id: u32, - surfaces: HashMap<u32, GpuDisplaySurface>, - surface_next_id: u32, + inner: Box<DisplayT>, } impl GpuDisplay { - /// Opens a fresh connection to the compositor. - pub fn new<P: AsRef<Path>>(wayland_path: P) -> Result<GpuDisplay, GpuDisplayError> { - // The dwl_context_new call should always be safe to call, and we check its result. - let ctx = DwlContext(unsafe { dwl_context_new() }); - if ctx.0.is_null() { - return Err(GpuDisplayError::Allocate); - } - - // The dwl_context_setup call is always safe to call given that the supplied context is - // valid. and we check its result. - let cstr_path = match wayland_path.as_ref().as_os_str().to_str() { - Some(str) => match CString::new(str) { - Ok(cstr) => cstr, - Err(_) => return Err(GpuDisplayError::InvalidPath), - }, - None => return Err(GpuDisplayError::InvalidPath), - }; - let setup_success = unsafe { dwl_context_setup(ctx.0, cstr_path.as_ptr()) }; - if !setup_success { - return Err(GpuDisplayError::Connect); + pub fn open_x<S: AsRef<str>>(display_name: Option<S>) -> Result<GpuDisplay, GpuDisplayError> { + let _ = display_name; + #[cfg(feature = "x")] + { + let display = match display_name { + Some(s) => gpu_display_x::DisplayX::open_display(Some(s.as_ref()))?, + None => gpu_display_x::DisplayX::open_display(None)?, + }; + let inner = Box::new(display); + Ok(GpuDisplay { inner }) } - - Ok(GpuDisplay { - ctx, - dmabufs: Default::default(), - dmabuf_next_id: 0, - surfaces: Default::default(), - surface_next_id: 0, - }) - } - - fn ctx(&self) -> *mut dwl_context { - self.ctx.0 + #[cfg(not(feature = "x"))] + Err(GpuDisplayError::Unsupported) } - fn get_surface(&self, surface_id: u32) -> Option<&GpuDisplaySurface> { - self.surfaces.get(&surface_id) + /// Opens a fresh connection to the compositor. + pub fn open_wayland<P: AsRef<Path>>( + wayland_path: Option<P>, + ) -> Result<GpuDisplay, GpuDisplayError> { + let display = match wayland_path { + Some(s) => gpu_display_wl::DisplayWl::new(Some(s.as_ref()))?, + None => gpu_display_wl::DisplayWl::new(None)?, + }; + 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. @@ -175,43 +201,19 @@ impl GpuDisplay { height: u32, fourcc: u32, ) -> Result<u32, GpuDisplayError> { - // Safe given that the context pointer is valid. Any other invalid parameters would be - // rejected by dwl_context_dmabuf_new safely. We check that the resulting dmabuf is valid - // before filing it away. - let dmabuf = DwlDmabuf(unsafe { - dwl_context_dmabuf_new( - self.ctx(), - fd, - offset, - stride, - modifiers, - width, - height, - fourcc, - ) - }); - if dmabuf.0.is_null() { - return Err(GpuDisplayError::FailedImport); - } - - let next_id = self.dmabuf_next_id; - self.dmabufs.insert(next_id, dmabuf); - self.dmabuf_next_id += 1; - Ok(next_id) + self.inner + .import_dmabuf(fd, offset, stride, modifiers, width, height, fourcc) } /// Releases a previously imported dmabuf identified by the given handle. pub fn release_import(&mut self, import_id: u32) { - self.dmabufs.remove(&import_id); + self.inner.release_import(import_id); } /// Dispatches internal events that were received from the compositor since the last call to /// `dispatch_events`. pub fn dispatch_events(&mut self) { - // Safe given that the context pointer is valid. - unsafe { - dwl_context_dispatch(self.ctx()); - } + self.inner.dispatch_events() } /// Creates a surface on the the compositor as either a top level window, or child of another @@ -222,88 +224,35 @@ impl GpuDisplay { width: u32, height: u32, ) -> Result<u32, GpuDisplayError> { - let parent_ptr = match parent_surface_id { - Some(id) => match self.get_surface(id).map(|p| p.surface()) { - Some(ptr) => ptr, - None => return Err(GpuDisplayError::InvalidSurfaceId), - }, - None => null_mut(), - }; - let row_size = width * BYTES_PER_PIXEL; - let fb_size = row_size * height; - let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT); - let mut buffer_shm = SharedMemory::new(Some( - CStr::from_bytes_with_nul(b"GpuDisplaySurface\0").unwrap(), - )) - .map_err(GpuDisplayError::CreateShm)?; - buffer_shm - .set_size(buffer_size as u64) - .map_err(GpuDisplayError::SetSize)?; - let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap(); - - // Safe because only a valid context, parent pointer (if not None), and buffer FD are used. - // The returned surface is checked for validity before being filed away. - let surface = DwlSurface(unsafe { - dwl_context_surface_new( - self.ctx(), - parent_ptr, - buffer_shm.as_raw_fd(), - buffer_size, - fb_size as usize, - width, - height, - row_size, - ) - }); - - if surface.0.is_null() { - return Err(GpuDisplayError::CreateSurface); - } - - let next_id = self.surface_next_id; - self.surfaces.insert( - next_id, - GpuDisplaySurface { - surface, - buffer_size: fb_size as usize, - buffer_index: Cell::new(0), - buffer_mem, - }, - ); - - self.surface_next_id += 1; - Ok(next_id) + self.inner.create_surface(parent_surface_id, width, height) } /// Releases a previously created surface identified by the given handle. pub fn release_surface(&mut self, surface_id: u32) { - self.surfaces.remove(&surface_id); + self.inner.release_surface(surface_id) } /// Gets a reference to an unused framebuffer for the identified surface. - pub fn framebuffer_memory(&self, surface_id: u32) -> Option<VolatileSlice> { - let surface = self.get_surface(surface_id)?; - let buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT; - surface - .buffer_mem - .get_slice( - (buffer_index * surface.buffer_size) as u64, - surface.buffer_size as u64, - ) - .ok() + pub fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> { + self.inner.framebuffer(surface_id) + } + + /// Gets a reference to an unused framebuffer for the identified surface. + pub fn framebuffer_region( + &mut self, + surface_id: u32, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Option<GpuDisplayFramebuffer> { + self.inner + .framebuffer_region(surface_id, x, y, width, height) } /// Commits any pending state for the identified surface. - pub fn commit(&self, surface_id: u32) { - match self.get_surface(surface_id) { - Some(surface) => { - // Safe because only a valid surface is used. - unsafe { - dwl_surface_commit(surface.surface()); - } - } - None => debug_assert!(false, "invalid surface_id {}", surface_id), - } + pub fn commit(&mut self, surface_id: u32) { + self.inner.commit(surface_id) } /// Returns true if the next buffer in the buffer queue for the given surface is currently in @@ -312,88 +261,37 @@ impl GpuDisplay { /// If the next buffer is in use, the memory returned from `framebuffer_memory` should not be /// written to. pub fn next_buffer_in_use(&self, surface_id: u32) -> bool { - match self.get_surface(surface_id) { - Some(surface) => { - let next_buffer_index = (surface.buffer_index.get() + 1) % BUFFER_COUNT; - // Safe because only a valid surface and buffer index is used. - unsafe { dwl_surface_buffer_in_use(surface.surface(), next_buffer_index) } - } - None => { - debug_assert!(false, "invalid surface_id {}", surface_id); - false - } - } + self.inner.next_buffer_in_use(surface_id) } /// Changes the visible contents of the identified surface to the contents of the framebuffer /// last returned by `framebuffer_memory` for this surface. - pub fn flip(&self, surface_id: u32) { - match self.get_surface(surface_id) { - Some(surface) => { - surface - .buffer_index - .set((surface.buffer_index.get() + 1) % BUFFER_COUNT); - // Safe because only a valid surface and buffer index is used. - unsafe { - dwl_surface_flip(surface.surface(), surface.buffer_index.get()); - } - } - None => debug_assert!(false, "invalid surface_id {}", surface_id), - } + pub fn flip(&mut self, surface_id: u32) { + self.inner.flip(surface_id) } /// Changes the visible contents of the identified surface to that of the identified imported /// buffer. - pub fn flip_to(&self, surface_id: u32, import_id: u32) { - match self.get_surface(surface_id) { - Some(surface) => { - match self.dmabufs.get(&import_id) { - // Safe because only a valid surface and dmabuf is used. - Some(dmabuf) => unsafe { dwl_surface_flip_to(surface.surface(), dmabuf.0) }, - None => debug_assert!(false, "invalid import_id {}", import_id), - } - } - None => debug_assert!(false, "invalid surface_id {}", surface_id), - } + pub fn flip_to(&mut self, surface_id: u32, import_id: u32) { + self.inner.flip_to(surface_id, import_id) } /// Returns true if the identified top level surface has been told to close by the compositor, /// and by extension the user. pub fn close_requested(&self, surface_id: u32) -> bool { - match self.get_surface(surface_id) { - Some(surface) => - // Safe because only a valid surface is used. - unsafe { dwl_surface_close_requested(surface.surface()) } - None => false, - } + self.inner.close_requested(surface_id) } /// Sets the position of the identified subsurface relative to its parent. /// /// The change in position will not be visible until `commit` is called for the parent surface. - pub fn set_position(&self, surface_id: u32, x: u32, y: u32) { - match self.get_surface(surface_id) { - Some(surface) => { - // Safe because only a valid surface is used. - unsafe { - dwl_surface_set_position(surface.surface(), x, y); - } - } - None => debug_assert!(false, "invalid surface_id {}", surface_id), - } - } -} - -impl Drop for GpuDisplay { - fn drop(&mut self) { - // Safe given that the context pointer is valid. - unsafe { dwl_context_destroy(&mut self.ctx.0) } + pub fn set_position(&mut self, surface_id: u32, x: u32, y: u32) { + self.inner.set_position(surface_id, x, y) } } impl AsRawFd for GpuDisplay { fn as_raw_fd(&self) -> RawFd { - // Safe given that the context pointer is valid. - unsafe { dwl_context_fd(self.ctx.0) } + self.inner.as_raw_fd() } } |