Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/lib.rs
5392 views
1
// Copyright 2018 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
//! Crate for displaying simple surfaces and GPU buffers over a low-level display backend such as
6
//! Wayland or X.
7
8
use std::collections::BTreeMap;
9
use std::io::Error as IoError;
10
use std::time::Duration;
11
12
use anyhow::anyhow;
13
use anyhow::Context;
14
use base::AsRawDescriptor;
15
use base::Error as BaseError;
16
use base::EventToken;
17
use base::EventType;
18
use base::VolatileSlice;
19
use base::WaitContext;
20
use remain::sorted;
21
use rutabaga_gfx::AhbInfo;
22
use serde::Deserialize;
23
use serde::Serialize;
24
use sync::Waitable;
25
use thiserror::Error;
26
use vm_control::gpu::DisplayParameters;
27
use vm_control::gpu::MouseMode;
28
#[cfg(feature = "vulkan_display")]
29
use vulkano::VulkanLibrary;
30
31
mod event_device;
32
#[cfg(feature = "android_display")]
33
mod gpu_display_android;
34
#[cfg(feature = "android_display_stub")]
35
mod gpu_display_android_stub;
36
mod gpu_display_stub;
37
#[cfg(windows)]
38
mod gpu_display_win;
39
#[cfg(any(target_os = "android", target_os = "linux"))]
40
mod gpu_display_wl;
41
#[cfg(feature = "x")]
42
mod gpu_display_x;
43
#[cfg(any(windows, feature = "x"))]
44
mod keycode_converter;
45
mod sys;
46
#[cfg(feature = "vulkan_display")]
47
pub mod vulkan;
48
49
pub use event_device::EventDevice;
50
pub use event_device::EventDeviceKind;
51
#[cfg(windows)]
52
pub use gpu_display_win::WindowProcedureThread;
53
#[cfg(windows)]
54
pub use gpu_display_win::WindowProcedureThreadBuilder;
55
use linux_input_sys::virtio_input_event;
56
use sys::SysDisplayT;
57
pub use sys::SysGpuDisplayExt;
58
59
// The number of bytes in a vulkan UUID.
60
#[cfg(feature = "vulkan_display")]
61
const VK_UUID_BYTES: usize = 16;
62
63
#[derive(Clone)]
64
pub struct VulkanCreateParams {
65
#[cfg(feature = "vulkan_display")]
66
pub vulkan_library: std::sync::Arc<VulkanLibrary>,
67
#[cfg(feature = "vulkan_display")]
68
pub device_uuid: [u8; VK_UUID_BYTES],
69
#[cfg(feature = "vulkan_display")]
70
pub driver_uuid: [u8; VK_UUID_BYTES],
71
}
72
73
/// An error generated by `GpuDisplay`.
74
#[sorted]
75
#[derive(Error, Debug)]
76
pub enum GpuDisplayError {
77
/// An internal allocation failed.
78
#[error("internal allocation failed")]
79
Allocate,
80
/// A base error occurred.
81
#[error("received a base error: {0}")]
82
BaseError(BaseError),
83
/// Connecting to the compositor failed.
84
#[error("failed to connect to compositor")]
85
Connect,
86
/// Connection to compositor has been broken.
87
#[error("connection to compositor has been broken")]
88
ConnectionBroken,
89
/// Creating event file descriptor failed.
90
#[error("failed to create event file descriptor")]
91
CreateEvent,
92
/// Failed to create a surface on the compositor.
93
#[error("failed to crate surface on the compositor")]
94
CreateSurface,
95
/// Failed to import an event device.
96
#[error("failed to import an event device: {0}")]
97
FailedEventDeviceImport(String),
98
#[error("failed to register an event device to listen for guest events: {0}")]
99
FailedEventDeviceListen(base::TubeError),
100
/// Failed to import a buffer to the compositor.
101
#[error("failed to import a buffer to the compositor")]
102
FailedImport,
103
/// Android display service name is invalid.
104
#[error("invalid Android display service name: {0}")]
105
InvalidAndroidDisplayServiceName(String),
106
/// The import ID is invalid.
107
#[error("invalid import ID")]
108
InvalidImportId,
109
/// The path is invalid.
110
#[error("invalid path")]
111
InvalidPath,
112
/// The surface ID is invalid.
113
#[error("invalid surface ID")]
114
InvalidSurfaceId,
115
/// An input/output error occured.
116
#[error("an input/output error occur: {0}")]
117
IoError(IoError),
118
/// A required feature was missing.
119
#[error("required feature was missing: {0}")]
120
RequiredFeature(&'static str),
121
/// The method is unsupported by the implementation.
122
#[error("unsupported by the implementation")]
123
Unsupported,
124
}
125
126
pub type GpuDisplayResult<T> = std::result::Result<T, GpuDisplayError>;
127
128
impl From<BaseError> for GpuDisplayError {
129
fn from(e: BaseError) -> GpuDisplayError {
130
GpuDisplayError::BaseError(e)
131
}
132
}
133
134
impl From<IoError> for GpuDisplayError {
135
fn from(e: IoError) -> GpuDisplayError {
136
GpuDisplayError::IoError(e)
137
}
138
}
139
140
/// A surface type
141
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
142
pub enum SurfaceType {
143
/// Scanout surface
144
Scanout,
145
/// Mouse cursor surface
146
Cursor,
147
}
148
149
/// Event token for display instances
150
#[derive(EventToken, Debug)]
151
pub enum DisplayEventToken {
152
Display,
153
EventDevice { event_device_id: u32 },
154
}
155
156
#[derive(Clone)]
157
pub struct GpuDisplayFramebuffer<'a> {
158
framebuffer: VolatileSlice<'a>,
159
slice: VolatileSlice<'a>,
160
stride: u32,
161
bytes_per_pixel: u32,
162
}
163
164
impl<'a> GpuDisplayFramebuffer<'a> {
165
fn new(
166
framebuffer: VolatileSlice<'a>,
167
stride: u32,
168
bytes_per_pixel: u32,
169
) -> GpuDisplayFramebuffer<'a> {
170
GpuDisplayFramebuffer {
171
framebuffer,
172
slice: framebuffer,
173
stride,
174
bytes_per_pixel,
175
}
176
}
177
178
fn sub_region(
179
&self,
180
x: u32,
181
y: u32,
182
width: u32,
183
height: u32,
184
) -> Option<GpuDisplayFramebuffer<'a>> {
185
let x_byte_offset = x.checked_mul(self.bytes_per_pixel)?;
186
let y_byte_offset = y.checked_mul(self.stride)?;
187
let byte_offset = x_byte_offset.checked_add(y_byte_offset)?;
188
189
let width_bytes = width.checked_mul(self.bytes_per_pixel)?;
190
let count = height
191
.checked_mul(self.stride)?
192
.checked_sub(self.stride)?
193
.checked_add(width_bytes)?;
194
let slice = self
195
.framebuffer
196
.sub_slice(byte_offset as usize, count as usize)
197
.unwrap();
198
199
Some(GpuDisplayFramebuffer { slice, ..*self })
200
}
201
202
pub fn as_volatile_slice(&self) -> VolatileSlice<'a> {
203
self.slice
204
}
205
206
pub fn stride(&self) -> u32 {
207
self.stride
208
}
209
}
210
211
trait GpuDisplaySurface {
212
/// Returns an unique ID associated with the surface. This is typically generated by the
213
/// compositor or cast of a raw pointer.
214
fn surface_descriptor(&self) -> u64 {
215
0
216
}
217
218
/// Returns the next framebuffer, allocating if necessary.
219
fn framebuffer(&mut self) -> Option<GpuDisplayFramebuffer> {
220
None
221
}
222
223
/// Returns true if the next buffer in the swapchain is already in use.
224
fn next_buffer_in_use(&self) -> bool {
225
false
226
}
227
228
/// Returns true if the surface should be closed.
229
fn close_requested(&self) -> bool {
230
false
231
}
232
233
/// Puts the next buffer on the screen, making it the current buffer.
234
fn flip(&mut self) {
235
// no-op
236
}
237
238
/// Puts the specified import_id on the screen.
239
fn flip_to(
240
&mut self,
241
_import_id: u32,
242
_acquire_timepoint: Option<SemaphoreTimepoint>,
243
_release_timepoint: Option<SemaphoreTimepoint>,
244
_extra_info: Option<FlipToExtraInfo>,
245
) -> anyhow::Result<Waitable> {
246
// no-op
247
Ok(Waitable::signaled())
248
}
249
250
/// Commits the surface to the compositor.
251
fn commit(&mut self) -> GpuDisplayResult<()> {
252
Ok(())
253
}
254
255
/// Sets the mouse mode used on this surface.
256
fn set_mouse_mode(&mut self, _mouse_mode: MouseMode) {
257
// no-op
258
}
259
260
/// Sets the position of the identified subsurface relative to its parent.
261
fn set_position(&mut self, _x: u32, _y: u32) {
262
// no-op
263
}
264
265
/// Returns the type of the completed buffer.
266
#[allow(dead_code)]
267
fn buffer_completion_type(&self) -> u32 {
268
0
269
}
270
271
/// Draws the current buffer on the screen.
272
#[allow(dead_code)]
273
fn draw_current_buffer(&mut self) {
274
// no-op
275
}
276
277
/// Handles a compositor-specific client event.
278
#[allow(dead_code)]
279
fn on_client_message(&mut self, _client_data: u64) {
280
// no-op
281
}
282
283
/// Handles a compositor-specific shared memory completion event.
284
#[allow(dead_code)]
285
fn on_shm_completion(&mut self, _shm_complete: u64) {
286
// no-op
287
}
288
}
289
290
struct GpuDisplayEvents {
291
events: Vec<virtio_input_event>,
292
device_type: EventDeviceKind,
293
}
294
295
trait DisplayT: AsRawDescriptor {
296
/// Returns true if there are events that are on the queue.
297
fn pending_events(&self) -> bool {
298
false
299
}
300
301
/// Sends any pending commands to the compositor.
302
fn flush(&self) {
303
// no-op
304
}
305
306
/// Returns the surface descirptor associated with the current event
307
fn next_event(&mut self) -> GpuDisplayResult<u64> {
308
Ok(0)
309
}
310
311
/// Handles the event from the compositor, and returns an list of events
312
fn handle_next_event(
313
&mut self,
314
_surface: &mut Box<dyn GpuDisplaySurface>,
315
) -> Option<GpuDisplayEvents> {
316
None
317
}
318
319
/// Creates a surface with the given parameters. The display backend is given a non-zero
320
/// `surface_id` as a handle for subsequent operations.
321
fn create_surface(
322
&mut self,
323
parent_surface_id: Option<u32>,
324
surface_id: u32,
325
scanout_id: Option<u32>,
326
display_params: &DisplayParameters,
327
surf_type: SurfaceType,
328
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>;
329
330
/// Imports a resource into the display backend. The display backend is given a non-zero
331
/// `import_id` as a handle for subsequent operations.
332
fn import_resource(
333
&mut self,
334
_import_id: u32,
335
_surface_id: u32,
336
_external_display_resource: DisplayExternalResourceImport,
337
) -> anyhow::Result<()> {
338
Err(anyhow!("import_resource is unsupported"))
339
}
340
341
/// Frees a previously imported resource.
342
fn release_import(&mut self, _import_id: u32, _surface_id: u32) {}
343
344
/// Releases a previously created surface.
345
fn release_surface(&mut self, _surface_id: u32) {}
346
}
347
348
pub trait GpuDisplayExt {
349
/// Imports the given `event_device` into the display, returning an event device id on success.
350
/// This device may be used to dispatch input events to the guest.
351
fn import_event_device(&mut self, event_device: EventDevice) -> GpuDisplayResult<u32>;
352
353
/// Called when an event device is readable.
354
fn handle_event_device(&mut self, event_device_id: u32);
355
}
356
357
pub enum DisplayExternalResourceImport<'a> {
358
Dmabuf {
359
descriptor: &'a dyn AsRawDescriptor,
360
offset: u32,
361
stride: u32,
362
modifiers: u64,
363
width: u32,
364
height: u32,
365
fourcc: u32,
366
},
367
VulkanImage {
368
descriptor: &'a dyn AsRawDescriptor,
369
metadata: VulkanDisplayImageImportMetadata,
370
},
371
VulkanTimelineSemaphore {
372
descriptor: &'a dyn AsRawDescriptor,
373
},
374
AHardwareBuffer {
375
info: AhbInfo,
376
},
377
}
378
379
pub struct VkExtent3D {
380
pub width: u32,
381
pub height: u32,
382
pub depth: u32,
383
}
384
385
pub struct VulkanDisplayImageImportMetadata {
386
// These fields go into a VkImageCreateInfo
387
pub flags: u32,
388
pub image_type: i32,
389
pub format: i32,
390
pub extent: VkExtent3D,
391
pub mip_levels: u32,
392
pub array_layers: u32,
393
pub samples: u32,
394
pub tiling: i32,
395
pub usage: u32,
396
pub sharing_mode: i32,
397
pub queue_family_indices: Vec<u32>,
398
pub initial_layout: i32,
399
400
// These fields go into a VkMemoryAllocateInfo
401
pub allocation_size: u64,
402
pub memory_type_index: u32,
403
404
// Additional information
405
pub dedicated_allocation: bool,
406
}
407
408
pub struct SemaphoreTimepoint {
409
pub import_id: u32,
410
pub value: u64,
411
}
412
413
pub enum FlipToExtraInfo {
414
#[cfg(feature = "vulkan_display")]
415
Vulkan { old_layout: i32, new_layout: i32 },
416
}
417
418
/// A connection to the compositor and associated collection of state.
419
///
420
/// The user of `GpuDisplay` can use `AsRawDescriptor` to poll on the compositor connection's file
421
/// descriptor. When the connection is readable, `dispatch_events` can be called to process it.
422
pub struct GpuDisplay {
423
next_id: u32,
424
event_devices: BTreeMap<u32, EventDevice>,
425
surfaces: BTreeMap<u32, Box<dyn GpuDisplaySurface>>,
426
wait_ctx: WaitContext<DisplayEventToken>,
427
// `inner` must be after `surfaces` to ensure those objects are dropped before
428
// the display context. The drop order for fields inside a struct is the order in which they
429
// are declared [Rust RFC 1857].
430
//
431
// We also don't want to drop inner before wait_ctx because it contains references to the event
432
// devices owned by inner.display_event_dispatcher.
433
inner: Box<dyn SysDisplayT>,
434
}
435
436
impl GpuDisplay {
437
/// Opens a connection to X server
438
pub fn open_x(display_name: Option<&str>) -> GpuDisplayResult<GpuDisplay> {
439
let _ = display_name;
440
#[cfg(feature = "x")]
441
{
442
let display = gpu_display_x::DisplayX::open_display(display_name)?;
443
444
let wait_ctx = WaitContext::new()?;
445
wait_ctx.add(&display, DisplayEventToken::Display)?;
446
447
Ok(GpuDisplay {
448
inner: Box::new(display),
449
next_id: 1,
450
event_devices: Default::default(),
451
surfaces: Default::default(),
452
wait_ctx,
453
})
454
}
455
#[cfg(not(feature = "x"))]
456
Err(GpuDisplayError::Unsupported)
457
}
458
459
pub fn open_android(service_name: &str) -> GpuDisplayResult<GpuDisplay> {
460
let _ = service_name;
461
#[cfg(feature = "android_display")]
462
{
463
let display = gpu_display_android::DisplayAndroid::new(service_name)?;
464
465
let wait_ctx = WaitContext::new()?;
466
wait_ctx.add(&display, DisplayEventToken::Display)?;
467
468
Ok(GpuDisplay {
469
inner: Box::new(display),
470
next_id: 1,
471
event_devices: Default::default(),
472
surfaces: Default::default(),
473
wait_ctx,
474
})
475
}
476
#[cfg(not(feature = "android_display"))]
477
Err(GpuDisplayError::Unsupported)
478
}
479
480
pub fn open_stub() -> GpuDisplayResult<GpuDisplay> {
481
let display = gpu_display_stub::DisplayStub::new()?;
482
let wait_ctx = WaitContext::new()?;
483
wait_ctx.add(&display, DisplayEventToken::Display)?;
484
485
Ok(GpuDisplay {
486
inner: Box::new(display),
487
next_id: 1,
488
event_devices: Default::default(),
489
surfaces: Default::default(),
490
wait_ctx,
491
})
492
}
493
494
// Leaves the `GpuDisplay` in a undefined state.
495
//
496
// TODO: Would be nice to change receiver from `&mut self` to `self`. Requires some refactoring
497
// elsewhere.
498
pub fn take_event_devices(&mut self) -> Vec<EventDevice> {
499
std::mem::take(&mut self.event_devices)
500
.into_values()
501
.collect()
502
}
503
504
fn dispatch_display_events(&mut self) -> GpuDisplayResult<()> {
505
self.inner.flush();
506
while self.inner.pending_events() {
507
let surface_descriptor = self.inner.next_event()?;
508
509
for surface in self.surfaces.values_mut() {
510
if surface_descriptor != surface.surface_descriptor() {
511
continue;
512
}
513
514
if let Some(gpu_display_events) = self.inner.handle_next_event(surface) {
515
for event_device in self.event_devices.values_mut() {
516
if event_device.kind() != gpu_display_events.device_type {
517
continue;
518
}
519
520
event_device.send_report(gpu_display_events.events.iter().cloned())?;
521
}
522
}
523
}
524
}
525
526
Ok(())
527
}
528
529
/// Dispatches internal events that were received from the compositor since the last call to
530
/// `dispatch_events`.
531
pub fn dispatch_events(&mut self) -> GpuDisplayResult<()> {
532
let wait_events = self.wait_ctx.wait_timeout(Duration::default())?;
533
534
if let Some(wait_event) = wait_events.iter().find(|e| e.is_hungup) {
535
base::error!(
536
"Display signaled with a hungup event for token {:?}",
537
wait_event.token
538
);
539
self.wait_ctx = WaitContext::new().unwrap();
540
return GpuDisplayResult::Err(GpuDisplayError::ConnectionBroken);
541
}
542
543
for wait_event in wait_events.iter().filter(|e| e.is_writable) {
544
if let DisplayEventToken::EventDevice { event_device_id } = wait_event.token {
545
if let Some(event_device) = self.event_devices.get_mut(&event_device_id) {
546
if !event_device.flush_buffered_events()? {
547
continue;
548
}
549
self.wait_ctx.modify(
550
event_device,
551
EventType::Read,
552
DisplayEventToken::EventDevice { event_device_id },
553
)?;
554
}
555
}
556
}
557
558
for wait_event in wait_events.iter().filter(|e| e.is_readable) {
559
match wait_event.token {
560
DisplayEventToken::Display => self.dispatch_display_events()?,
561
DisplayEventToken::EventDevice { event_device_id } => {
562
self.handle_event_device(event_device_id)
563
}
564
}
565
}
566
567
Ok(())
568
}
569
570
/// Creates a surface on the the compositor as either a top level window, or child of another
571
/// surface, returning a handle to the new surface.
572
pub fn create_surface(
573
&mut self,
574
parent_surface_id: Option<u32>,
575
scanout_id: Option<u32>,
576
display_params: &DisplayParameters,
577
surf_type: SurfaceType,
578
) -> GpuDisplayResult<u32> {
579
if let Some(parent_id) = parent_surface_id {
580
if !self.surfaces.contains_key(&parent_id) {
581
return Err(GpuDisplayError::InvalidSurfaceId);
582
}
583
}
584
585
let new_surface_id = self.next_id;
586
let new_surface = self.inner.create_surface(
587
parent_surface_id,
588
new_surface_id,
589
scanout_id,
590
display_params,
591
surf_type,
592
)?;
593
594
self.next_id += 1;
595
self.surfaces.insert(new_surface_id, new_surface);
596
Ok(new_surface_id)
597
}
598
599
/// Releases a previously created surface identified by the given handle.
600
pub fn release_surface(&mut self, surface_id: u32) {
601
self.surfaces.remove(&surface_id);
602
self.inner.release_surface(surface_id);
603
}
604
605
/// Gets a reference to an unused framebuffer for the identified surface.
606
pub fn framebuffer(&mut self, surface_id: u32) -> Option<GpuDisplayFramebuffer> {
607
let surface = self.surfaces.get_mut(&surface_id)?;
608
surface.framebuffer()
609
}
610
611
/// Gets a reference to an unused framebuffer for the identified surface.
612
pub fn framebuffer_region(
613
&mut self,
614
surface_id: u32,
615
x: u32,
616
y: u32,
617
width: u32,
618
height: u32,
619
) -> Option<GpuDisplayFramebuffer> {
620
let framebuffer = self.framebuffer(surface_id)?;
621
framebuffer.sub_region(x, y, width, height)
622
}
623
624
/// Returns true if the next buffer in the buffer queue for the given surface is currently in
625
/// use.
626
///
627
/// If the next buffer is in use, the memory returned from `framebuffer_memory` should not be
628
/// written to.
629
pub fn next_buffer_in_use(&self, surface_id: u32) -> bool {
630
self.surfaces
631
.get(&surface_id)
632
.map(|s| s.next_buffer_in_use())
633
.unwrap_or(false)
634
}
635
636
/// Changes the visible contents of the identified surface to the contents of the framebuffer
637
/// last returned by `framebuffer_memory` for this surface.
638
pub fn flip(&mut self, surface_id: u32) {
639
if let Some(surface) = self.surfaces.get_mut(&surface_id) {
640
surface.flip()
641
}
642
}
643
644
/// Returns true if the identified top level surface has been told to close by the compositor,
645
/// and by extension the user.
646
pub fn close_requested(&self, surface_id: u32) -> bool {
647
self.surfaces
648
.get(&surface_id)
649
.map(|s| s.close_requested())
650
.unwrap_or(true)
651
}
652
653
/// Imports a resource to the display backend. This resource may be an image for the compositor
654
/// or a synchronization object.
655
pub fn import_resource(
656
&mut self,
657
surface_id: u32,
658
external_display_resource: DisplayExternalResourceImport,
659
) -> anyhow::Result<u32> {
660
let import_id = self.next_id;
661
662
self.inner
663
.import_resource(import_id, surface_id, external_display_resource)?;
664
665
self.next_id += 1;
666
Ok(import_id)
667
}
668
669
/// Releases a previously imported resource identified by the given handle.
670
pub fn release_import(&mut self, import_id: u32, surface_id: u32) {
671
self.inner.release_import(import_id, surface_id);
672
}
673
674
/// Commits any pending state for the identified surface.
675
pub fn commit(&mut self, surface_id: u32) -> GpuDisplayResult<()> {
676
let surface = self
677
.surfaces
678
.get_mut(&surface_id)
679
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
680
681
surface.commit()
682
}
683
684
/// Changes the visible contents of the identified surface to that of the identified imported
685
/// buffer.
686
pub fn flip_to(
687
&mut self,
688
surface_id: u32,
689
import_id: u32,
690
acquire_timepoint: Option<SemaphoreTimepoint>,
691
release_timepoint: Option<SemaphoreTimepoint>,
692
extra_info: Option<FlipToExtraInfo>,
693
) -> anyhow::Result<Waitable> {
694
let surface = self
695
.surfaces
696
.get_mut(&surface_id)
697
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
698
699
surface
700
.flip_to(import_id, acquire_timepoint, release_timepoint, extra_info)
701
.context("failed in flip on GpuDisplaySurface")
702
}
703
704
/// Sets the mouse mode used on this surface.
705
pub fn set_mouse_mode(
706
&mut self,
707
surface_id: u32,
708
mouse_mode: MouseMode,
709
) -> GpuDisplayResult<()> {
710
let surface = self
711
.surfaces
712
.get_mut(&surface_id)
713
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
714
715
surface.set_mouse_mode(mouse_mode);
716
Ok(())
717
}
718
719
/// Sets the position of the identified subsurface relative to its parent.
720
///
721
/// The change in position will not be visible until `commit` is called for the parent surface.
722
pub fn set_position(&mut self, surface_id: u32, x: u32, y: u32) -> GpuDisplayResult<()> {
723
let surface = self
724
.surfaces
725
.get_mut(&surface_id)
726
.ok_or(GpuDisplayError::InvalidSurfaceId)?;
727
728
surface.set_position(x, y);
729
Ok(())
730
}
731
}
732
733