// Copyright 2018 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Crate for displaying simple surfaces and GPU buffers over a low-level display backend such as //! Wayland or X. use std::collections::BTreeMap; use std::io::Error as IoError; use std::time::Duration; use anyhow::anyhow; use anyhow::Context; use base::AsRawDescriptor; use base::Error as BaseError; use base::EventToken; use base::EventType; use base::VolatileSlice; use base::WaitContext; use remain::sorted; use serde::Deserialize; use serde::Serialize; use sync::Waitable; use thiserror::Error; use vm_control::gpu::DisplayParameters; use vm_control::gpu::MouseMode; #[cfg(feature = "vulkan_display")] use vulkano::VulkanLibrary; mod event_device; #[cfg(feature = "android_display")] mod gpu_display_android; #[cfg(feature = "android_display_stub")] mod gpu_display_android_stub; mod gpu_display_stub; #[cfg(windows)] mod gpu_display_win; #[cfg(any(target_os = "android", target_os = "linux"))] mod gpu_display_wl; #[cfg(feature = "x")] mod gpu_display_x; #[cfg(any(windows, feature = "x"))] mod keycode_converter; mod sys; #[cfg(feature = "vulkan_display")] pub mod vulkan; pub use event_device::EventDevice; pub use event_device::EventDeviceKind; #[cfg(windows)] pub use gpu_display_win::WindowProcedureThread; #[cfg(windows)] pub use gpu_display_win::WindowProcedureThreadBuilder; use linux_input_sys::virtio_input_event; use sys::SysDisplayT; pub use sys::SysGpuDisplayExt; // The number of bytes in a vulkan UUID. #[cfg(feature = "vulkan_display")] const VK_UUID_BYTES: usize = 16; #[derive(Clone)] pub struct VulkanCreateParams { #[cfg(feature = "vulkan_display")] pub vulkan_library: std::sync::Arc, #[cfg(feature = "vulkan_display")] pub device_uuid: [u8; VK_UUID_BYTES], #[cfg(feature = "vulkan_display")] pub driver_uuid: [u8; VK_UUID_BYTES], } /// An error generated by `GpuDisplay`. #[sorted] #[derive(Error, Debug)] pub enum GpuDisplayError { /// An internal allocation failed. #[error("internal allocation failed")] Allocate, /// A base error occurred. #[error("received a base error: {0}")] BaseError(BaseError), /// Connecting to the compositor failed. #[error("failed to connect to compositor")] Connect, /// Connection to compositor has been broken. #[error("connection to compositor has been broken")] ConnectionBroken, /// Creating event file descriptor failed. #[error("failed to create event file descriptor")] CreateEvent, /// Failed to create a surface on the compositor. #[error("failed to crate surface on the compositor")] CreateSurface, /// Failed to import an event device. #[error("failed to import an event device: {0}")] FailedEventDeviceImport(String), #[error("failed to register an event device to listen for guest events: {0}")] FailedEventDeviceListen(base::TubeError), /// Failed to import a buffer to the compositor. #[error("failed to import a buffer to the compositor")] FailedImport, /// Android display service name is invalid. #[error("invalid Android display service name: {0}")] InvalidAndroidDisplayServiceName(String), /// The import ID is invalid. #[error("invalid import ID")] InvalidImportId, /// The path is invalid. #[error("invalid path")] InvalidPath, /// The surface ID is invalid. #[error("invalid surface ID")] InvalidSurfaceId, /// An input/output error occured. #[error("an input/output error occur: {0}")] IoError(IoError), /// A required feature was missing. #[error("required feature was missing: {0}")] RequiredFeature(&'static str), /// The method is unsupported by the implementation. #[error("unsupported by the implementation")] Unsupported, } pub type GpuDisplayResult = std::result::Result; impl From for GpuDisplayError { fn from(e: BaseError) -> GpuDisplayError { GpuDisplayError::BaseError(e) } } impl From for GpuDisplayError { fn from(e: IoError) -> GpuDisplayError { GpuDisplayError::IoError(e) } } /// A surface type #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SurfaceType { /// Scanout surface Scanout, /// Mouse cursor surface Cursor, } /// Event token for display instances #[derive(EventToken, Debug)] pub enum DisplayEventToken { Display, EventDevice { event_device_id: u32 }, } #[derive(Clone)] pub struct GpuDisplayFramebuffer<'a> { framebuffer: VolatileSlice<'a>, slice: VolatileSlice<'a>, stride: u32, bytes_per_pixel: u32, } impl<'a> GpuDisplayFramebuffer<'a> { fn new( framebuffer: VolatileSlice<'a>, stride: u32, bytes_per_pixel: u32, ) -> GpuDisplayFramebuffer { GpuDisplayFramebuffer { framebuffer, slice: framebuffer, stride, bytes_per_pixel, } } fn sub_region( &self, x: u32, y: u32, width: u32, height: u32, ) -> Option> { 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 usize, count as usize) .unwrap(); Some(GpuDisplayFramebuffer { slice, ..*self }) } pub fn as_volatile_slice(&self) -> VolatileSlice<'a> { self.slice } pub fn stride(&self) -> u32 { self.stride } } trait GpuDisplaySurface { /// Returns an unique ID associated with the surface. This is typically generated by the /// compositor or cast of a raw pointer. fn surface_descriptor(&self) -> u64 { 0 } /// Returns the next framebuffer, allocating if necessary. fn framebuffer(&mut self) -> Option { None } /// Returns true if the next buffer in the swapchain is already in use. fn next_buffer_in_use(&self) -> bool { false } /// Returns true if the surface should be closed. fn close_requested(&self) -> bool { false } /// Puts the next buffer on the screen, making it the current buffer. fn flip(&mut self) { // no-op } /// Puts the specified import_id on the screen. fn flip_to( &mut self, _import_id: u32, _acquire_timepoint: Option, _release_timepoint: Option, _extra_info: Option, ) -> anyhow::Result { // no-op Ok(Waitable::signaled()) } /// Commits the surface to the compositor. fn commit(&mut self) -> GpuDisplayResult<()> { Ok(()) } /// Sets the mouse mode used on this surface. fn set_mouse_mode(&mut self, _mouse_mode: MouseMode) { // no-op } /// Sets the position of the identified subsurface relative to its parent. fn set_position(&mut self, _x: u32, _y: u32) { // no-op } /// Returns the type of the completed buffer. #[allow(dead_code)] fn buffer_completion_type(&self) -> u32 { 0 } /// Draws the current buffer on the screen. #[allow(dead_code)] fn draw_current_buffer(&mut self) { // no-op } /// Handles a compositor-specific client event. #[allow(dead_code)] fn on_client_message(&mut self, _client_data: u64) { // no-op } /// Handles a compositor-specific shared memory completion event. #[allow(dead_code)] fn on_shm_completion(&mut self, _shm_complete: u64) { // no-op } } struct GpuDisplayEvents { events: Vec, device_type: EventDeviceKind, } trait DisplayT: AsRawDescriptor { /// Returns true if there are events that are on the queue. fn pending_events(&self) -> bool { false } /// Sends any pending commands to the compositor. fn flush(&self) { // no-op } /// Returns the surface descirptor associated with the current event fn next_event(&mut self) -> GpuDisplayResult { Ok(0) } /// Handles the event from the compositor, and returns an list of events fn handle_next_event( &mut self, _surface: &mut Box, ) -> Option { None } /// Creates a surface with the given parameters. The display backend is given a non-zero /// `surface_id` as a handle for subsequent operations. fn create_surface( &mut self, parent_surface_id: Option, surface_id: u32, scanout_id: Option, display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult>; /// Imports a resource into the display backend. The display backend is given a non-zero /// `import_id` as a handle for subsequent operations. fn import_resource( &mut self, _import_id: u32, _surface_id: u32, _external_display_resource: DisplayExternalResourceImport, ) -> anyhow::Result<()> { Err(anyhow!("import_resource is unsupported")) } /// Frees a previously imported resource. fn release_import(&mut self, _import_id: u32, _surface_id: u32) {} } pub trait GpuDisplayExt { /// Imports the given `event_device` into the display, returning an event device id on success. /// This device may be used to dispatch input events to the guest. fn import_event_device(&mut self, event_device: EventDevice) -> GpuDisplayResult; /// Called when an event device is readable. fn handle_event_device(&mut self, event_device_id: u32); } pub enum DisplayExternalResourceImport<'a> { Dmabuf { descriptor: &'a dyn AsRawDescriptor, offset: u32, stride: u32, modifiers: u64, width: u32, height: u32, fourcc: u32, }, VulkanImage { descriptor: &'a dyn AsRawDescriptor, metadata: VulkanDisplayImageImportMetadata, }, VulkanTimelineSemaphore { descriptor: &'a dyn AsRawDescriptor, }, } pub struct VkExtent3D { pub width: u32, pub height: u32, pub depth: u32, } pub struct VulkanDisplayImageImportMetadata { // These fields go into a VkImageCreateInfo pub flags: u32, pub image_type: i32, pub format: i32, pub extent: VkExtent3D, pub mip_levels: u32, pub array_layers: u32, pub samples: u32, pub tiling: i32, pub usage: u32, pub sharing_mode: i32, pub queue_family_indices: Vec, pub initial_layout: i32, // These fields go into a VkMemoryAllocateInfo pub allocation_size: u64, pub memory_type_index: u32, // Additional information pub dedicated_allocation: bool, } pub struct SemaphoreTimepoint { pub import_id: u32, pub value: u64, } pub enum FlipToExtraInfo { #[cfg(feature = "vulkan_display")] Vulkan { old_layout: i32, new_layout: i32 }, } /// A connection to the compositor and associated collection of state. /// /// The user of `GpuDisplay` can use `AsRawDescriptor` 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 { next_id: u32, event_devices: BTreeMap, surfaces: BTreeMap>, wait_ctx: WaitContext, // `inner` must be after `surfaces` to ensure those objects are dropped before // the display context. The drop order for fields inside a struct is the order in which they // are declared [Rust RFC 1857]. // // We also don't want to drop inner before wait_ctx because it contains references to the event // devices owned by inner.display_event_dispatcher. inner: Box, } impl GpuDisplay { /// Opens a connection to X server pub fn open_x(display_name: Option<&str>) -> GpuDisplayResult { let _ = display_name; #[cfg(feature = "x")] { let display = gpu_display_x::DisplayX::open_display(display_name)?; let wait_ctx = WaitContext::new()?; wait_ctx.add(&display, DisplayEventToken::Display)?; Ok(GpuDisplay { inner: Box::new(display), next_id: 1, event_devices: Default::default(), surfaces: Default::default(), wait_ctx, }) } #[cfg(not(feature = "x"))] Err(GpuDisplayError::Unsupported) } pub fn open_android(service_name: &str) -> GpuDisplayResult { let _ = service_name; #[cfg(feature = "android_display")] { let display = gpu_display_android::DisplayAndroid::new(service_name)?; let wait_ctx = WaitContext::new()?; wait_ctx.add(&display, DisplayEventToken::Display)?; Ok(GpuDisplay { inner: Box::new(display), next_id: 1, event_devices: Default::default(), surfaces: Default::default(), wait_ctx, }) } #[cfg(not(feature = "android_display"))] Err(GpuDisplayError::Unsupported) } pub fn open_stub() -> GpuDisplayResult { let display = gpu_display_stub::DisplayStub::new()?; let wait_ctx = WaitContext::new()?; wait_ctx.add(&display, DisplayEventToken::Display)?; Ok(GpuDisplay { inner: Box::new(display), next_id: 1, event_devices: Default::default(), surfaces: Default::default(), wait_ctx, }) } // Leaves the `GpuDisplay` in a undefined state. // // TODO: Would be nice to change receiver from `&mut self` to `self`. Requires some refactoring // elsewhere. pub fn take_event_devices(&mut self) -> Vec { std::mem::take(&mut self.event_devices) .into_values() .collect() } fn dispatch_display_events(&mut self) -> GpuDisplayResult<()> { self.inner.flush(); while self.inner.pending_events() { let surface_descriptor = self.inner.next_event()?; for surface in self.surfaces.values_mut() { if surface_descriptor != surface.surface_descriptor() { continue; } if let Some(gpu_display_events) = self.inner.handle_next_event(surface) { for event_device in self.event_devices.values_mut() { if event_device.kind() != gpu_display_events.device_type { continue; } event_device.send_report(gpu_display_events.events.iter().cloned())?; } } } } Ok(()) } /// Dispatches internal events that were received from the compositor since the last call to /// `dispatch_events`. pub fn dispatch_events(&mut self) -> GpuDisplayResult<()> { let wait_events = self.wait_ctx.wait_timeout(Duration::default())?; if let Some(wait_event) = wait_events.iter().find(|e| e.is_hungup) { base::error!( "Display signaled with a hungup event for token {:?}", wait_event.token ); self.wait_ctx = WaitContext::new().unwrap(); return GpuDisplayResult::Err(GpuDisplayError::ConnectionBroken); } for wait_event in wait_events.iter().filter(|e| e.is_writable) { if let DisplayEventToken::EventDevice { event_device_id } = wait_event.token { if let Some(event_device) = self.event_devices.get_mut(&event_device_id) { if !event_device.flush_buffered_events()? { continue; } self.wait_ctx.modify( event_device, EventType::Read, DisplayEventToken::EventDevice { event_device_id }, )?; } } } for wait_event in wait_events.iter().filter(|e| e.is_readable) { match wait_event.token { DisplayEventToken::Display => self.dispatch_display_events()?, DisplayEventToken::EventDevice { event_device_id } => { self.handle_event_device(event_device_id) } } } Ok(()) } /// Creates a surface on the the compositor as either a top level window, or child of another /// surface, returning a handle to the new surface. pub fn create_surface( &mut self, parent_surface_id: Option, scanout_id: Option, display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult { if let Some(parent_id) = parent_surface_id { if !self.surfaces.contains_key(&parent_id) { return Err(GpuDisplayError::InvalidSurfaceId); } } let new_surface_id = self.next_id; let new_surface = self.inner.create_surface( parent_surface_id, new_surface_id, scanout_id, display_params, surf_type, )?; self.next_id += 1; self.surfaces.insert(new_surface_id, new_surface); Ok(new_surface_id) } /// Releases a previously created surface identified by the given handle. pub fn release_surface(&mut self, surface_id: u32) { self.surfaces.remove(&surface_id); } /// Gets a reference to an unused framebuffer for the identified surface. pub fn framebuffer(&mut self, surface_id: u32) -> Option { let surface = self.surfaces.get_mut(&surface_id)?; surface.framebuffer() } /// 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 { let framebuffer = self.framebuffer(surface_id)?; framebuffer.sub_region(x, y, width, height) } /// Returns true if the next buffer in the buffer queue for the given surface is currently in /// use. /// /// 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 { self.surfaces .get(&surface_id) .map(|s| s.next_buffer_in_use()) .unwrap_or(false) } /// 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(&mut self, surface_id: u32) { if let Some(surface) = self.surfaces.get_mut(&surface_id) { surface.flip() } } /// 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 { self.surfaces .get(&surface_id) .map(|s| s.close_requested()) .unwrap_or(true) } /// Imports a resource to the display backend. This resource may be an image for the compositor /// or a synchronization object. pub fn import_resource( &mut self, surface_id: u32, external_display_resource: DisplayExternalResourceImport, ) -> anyhow::Result { let import_id = self.next_id; self.inner .import_resource(import_id, surface_id, external_display_resource)?; self.next_id += 1; Ok(import_id) } /// Releases a previously imported resource identified by the given handle. pub fn release_import(&mut self, import_id: u32, surface_id: u32) { self.inner.release_import(import_id, surface_id); } /// Commits any pending state for the identified surface. pub fn commit(&mut self, surface_id: u32) -> GpuDisplayResult<()> { let surface = self .surfaces .get_mut(&surface_id) .ok_or(GpuDisplayError::InvalidSurfaceId)?; surface.commit() } /// Changes the visible contents of the identified surface to that of the identified imported /// buffer. pub fn flip_to( &mut self, surface_id: u32, import_id: u32, acquire_timepoint: Option, release_timepoint: Option, extra_info: Option, ) -> anyhow::Result { let surface = self .surfaces .get_mut(&surface_id) .ok_or(GpuDisplayError::InvalidSurfaceId)?; surface .flip_to(import_id, acquire_timepoint, release_timepoint, extra_info) .context("failed in flip on GpuDisplaySurface") } /// Sets the mouse mode used on this surface. pub fn set_mouse_mode( &mut self, surface_id: u32, mouse_mode: MouseMode, ) -> GpuDisplayResult<()> { let surface = self .surfaces .get_mut(&surface_id) .ok_or(GpuDisplayError::InvalidSurfaceId)?; surface.set_mouse_mode(mouse_mode); Ok(()) } /// 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(&mut self, surface_id: u32, x: u32, y: u32) -> GpuDisplayResult<()> { let surface = self .surfaces .get_mut(&surface_id) .ok_or(GpuDisplayError::InvalidSurfaceId)?; surface.set_position(x, y); Ok(()) } }