Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/gpu_display_win/mod.rs
5394 views
1
// Copyright 2022 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
mod keyboard_input_manager;
6
mod math_util;
7
mod mouse_input_manager;
8
pub mod surface;
9
mod virtual_display_manager;
10
mod window;
11
mod window_manager;
12
mod window_message_dispatcher;
13
mod window_message_processor;
14
pub mod window_procedure_thread;
15
16
use std::collections::HashMap;
17
use std::num::NonZeroU32;
18
use std::rc::Rc;
19
use std::sync::mpsc::channel;
20
use std::sync::Arc;
21
use std::sync::Weak;
22
use std::time::Duration;
23
24
use anyhow::bail;
25
use anyhow::format_err;
26
#[cfg(feature = "vulkan_display")]
27
use anyhow::Context;
28
use anyhow::Result;
29
use base::error;
30
use base::info;
31
use base::warn;
32
use base::AsRawDescriptor;
33
use base::Descriptor;
34
use base::Event;
35
use base::EventWaitResult;
36
use base::RawDescriptor;
37
use base::ReadNotifier;
38
use base::SendTube;
39
use metrics::sys::windows::Metrics;
40
pub use surface::Surface;
41
use sync::Mutex;
42
use sync::Waitable;
43
use vm_control::gpu::DisplayParameters;
44
use vm_control::ModifyWaitContext;
45
use window_message_processor::DisplaySendToWndProc;
46
pub use window_procedure_thread::WindowProcedureThread;
47
pub use window_procedure_thread::WindowProcedureThreadBuilder;
48
49
#[cfg(feature = "vulkan_display")]
50
use crate::gpu_display_win::window::BasicWindow;
51
#[cfg(feature = "vulkan_display")]
52
use crate::vulkan::HostDisplay;
53
use crate::DisplayExternalResourceImport;
54
use crate::DisplayT;
55
use crate::EventDevice;
56
use crate::FlipToExtraInfo;
57
use crate::GpuDisplayError;
58
use crate::GpuDisplayResult;
59
use crate::GpuDisplaySurface;
60
use crate::MouseMode;
61
use crate::SemaphoreTimepoint;
62
use crate::SurfaceType;
63
use crate::SysDisplayT;
64
use crate::VulkanCreateParams;
65
66
pub(crate) type ObjectId = NonZeroU32;
67
68
pub struct VirtualDisplaySpace;
69
pub struct HostWindowSpace;
70
71
pub enum HostDisplayWrapper {
72
Uninitialized,
73
#[cfg(feature = "vulkan_display")]
74
Initialized(Box<HostDisplay>),
75
}
76
77
pub struct DisplayWin {
78
wndproc_thread: Rc<WindowProcedureThread>,
79
close_requested_event: Event,
80
win_metrics: Option<Weak<Metrics>>,
81
is_surface_created: bool,
82
#[allow(dead_code)]
83
gpu_display_wait_descriptor_ctrl: SendTube,
84
event_device_wait_descriptor_requests: Vec<ModifyWaitContext>,
85
host_displays: HashMap<u32, Arc<Mutex<HostDisplayWrapper>>>,
86
#[allow(dead_code)]
87
vulkan_display_create_params: Option<VulkanCreateParams>,
88
}
89
90
impl DisplayWin {
91
pub fn new(
92
wndproc_thread: WindowProcedureThread,
93
win_metrics: Option<Weak<Metrics>>,
94
gpu_display_wait_descriptor_ctrl: SendTube,
95
vulkan_display_create_params: Option<VulkanCreateParams>,
96
) -> Result<DisplayWin, GpuDisplayError> {
97
let close_requested_event =
98
wndproc_thread
99
.try_clone_close_requested_event()
100
.map_err(|e| {
101
error!("Failed to create DisplayWin: {:?}", e);
102
GpuDisplayError::Allocate
103
})?;
104
Ok(Self {
105
wndproc_thread: Rc::new(wndproc_thread),
106
close_requested_event,
107
win_metrics,
108
is_surface_created: false,
109
gpu_display_wait_descriptor_ctrl,
110
event_device_wait_descriptor_requests: Vec::new(),
111
host_displays: HashMap::new(),
112
vulkan_display_create_params,
113
})
114
}
115
116
/// Posts a create surface command to the WndProc thread and waits until the creation finishes
117
/// to check the result.
118
fn create_surface_internal(
119
&mut self,
120
surface_id: u32,
121
scanout_id: u32,
122
display_params: &DisplayParameters,
123
) -> Result<Arc<Mutex<HostDisplayWrapper>>> {
124
let display_params_clone = display_params.clone();
125
let metrics = self.win_metrics.clone();
126
#[cfg(feature = "vulkan_display")]
127
let vulkan_create_params = self.vulkan_display_create_params.clone();
128
// This function should not return until surface creation finishes. Besides, we would like
129
// to know if the creation succeeds. Hence, we use channels to wait to see the result.
130
let (result_sender, result_receiver) = channel();
131
#[allow(unused_variables)]
132
let (host_display_sender, host_display_receiver) = channel();
133
134
// Post a message to the WndProc thread to create the surface.
135
self.wndproc_thread
136
.post_display_command(DisplaySendToWndProc::CreateSurface {
137
scanout_id,
138
function: Box::new(move |window, display_event_dispatcher| {
139
#[cfg(feature = "vulkan_display")]
140
let host_display = {
141
let create_display_closure =
142
|VulkanCreateParams {
143
vulkan_library,
144
device_uuid,
145
driver_uuid,
146
}| {
147
// SAFETY: Safe because vulkan display lives longer than window
148
// (because for Windows, we keep the
149
// windows alive for the entire life of the
150
// emulator).
151
unsafe {
152
let initial_host_viewport_size = window
153
.get_client_rect()
154
.with_context(|| "retrieve window client area size")?
155
.size;
156
HostDisplay::new(
157
vulkan_library,
158
window.handle() as _,
159
&initial_host_viewport_size.cast_unit(),
160
device_uuid,
161
driver_uuid,
162
)
163
.with_context(|| "create vulkan display")
164
}
165
};
166
let host_display = vulkan_create_params
167
.map(create_display_closure)
168
.transpose()?;
169
let host_display = match host_display {
170
Some(host_display) => {
171
HostDisplayWrapper::Initialized(Box::new(host_display))
172
}
173
None => HostDisplayWrapper::Uninitialized,
174
};
175
let host_display = Arc::new(Mutex::new(host_display));
176
host_display_sender
177
.send(Arc::clone(&host_display))
178
.map_err(|_| {
179
format_err!("Failed to send vulkan display back to caller.")
180
})?;
181
host_display
182
};
183
184
#[cfg(not(feature = "vulkan_display"))]
185
let host_display = Arc::new(Mutex::new(HostDisplayWrapper::Uninitialized));
186
187
Surface::new(
188
surface_id,
189
window,
190
metrics,
191
&display_params_clone,
192
display_event_dispatcher,
193
host_display,
194
)
195
}),
196
callback: Box::new(move |success| {
197
if let Err(e) = result_sender.send(success) {
198
error!("Failed to send surface creation result: {}", e);
199
}
200
}),
201
})?;
202
203
// Block until the surface creation finishes and check the result.
204
match result_receiver.recv() {
205
Ok(true) => host_display_receiver.recv().map_err(|_| {
206
format_err!(
207
"Failed to receive the vulkan display from the surface creation routine."
208
)
209
}),
210
Ok(false) => bail!("WndProc thread failed to create surface!"),
211
Err(e) => bail!("Failed to receive surface creation result: {}", e),
212
}
213
}
214
215
fn import_event_device_internal(
216
&mut self,
217
event_device_id: u32,
218
event_device: EventDevice,
219
) -> Result<()> {
220
match ObjectId::new(event_device_id) {
221
Some(event_device_id) => {
222
// This is safe because the winproc thread (which owns the event device after we
223
// send it there below) will be dropped before the GPU worker thread (which is
224
// where we're sending this descriptor).
225
let req = ModifyWaitContext::Add(Descriptor(
226
event_device.get_read_notifier().as_raw_descriptor(),
227
));
228
229
if let Err(e) = self.wndproc_thread.post_display_command(
230
DisplaySendToWndProc::ImportEventDevice {
231
event_device_id,
232
event_device,
233
},
234
) {
235
bail!("Failed to post ImportEventDevice message: {:?}", e);
236
}
237
238
if self.is_surface_created {
239
if let Err(e) = self.gpu_display_wait_descriptor_ctrl.send(&req) {
240
bail!(
241
"failed to send event device descriptor to \
242
GPU worker's wait context: {:?}",
243
e
244
)
245
}
246
} else {
247
self.event_device_wait_descriptor_requests.push(req);
248
}
249
250
Ok(())
251
}
252
None => bail!("{} cannot be converted to ObjectId", event_device_id),
253
}
254
}
255
}
256
257
impl AsRawDescriptor for DisplayWin {
258
/// Event handling is done on the GPU worker thread on other platforms. However, due to the way
259
/// Windows GUI system works, we have to do that on the WndProc thread instead, and we only
260
/// notify the event loop in the GPU worker thread of the display closure event.
261
fn as_raw_descriptor(&self) -> RawDescriptor {
262
self.close_requested_event.as_raw_descriptor()
263
}
264
}
265
266
impl DisplayT for DisplayWin {
267
fn create_surface(
268
&mut self,
269
parent_surface_id: Option<u32>,
270
surface_id: u32,
271
scanout_id: Option<u32>,
272
display_params: &DisplayParameters,
273
surface_type: SurfaceType,
274
) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
275
if parent_surface_id.is_some() {
276
return Err(GpuDisplayError::Unsupported);
277
}
278
279
if !matches!(surface_type, SurfaceType::Scanout) {
280
return Err(GpuDisplayError::Unsupported);
281
}
282
283
// Gfxstream allows for attaching a window only once along the initialization, so we only
284
// create the surface once. See details in b/179319775.
285
let host_display = match self.create_surface_internal(
286
surface_id,
287
scanout_id.expect("scanout id is required"),
288
display_params,
289
) {
290
Err(e) => {
291
error!("Failed to create surface: {:?}", e);
292
return Err(GpuDisplayError::Allocate);
293
}
294
Ok(display) => display,
295
};
296
self.is_surface_created = true;
297
self.host_displays
298
.insert(surface_id, Arc::clone(&host_display));
299
300
// Now that the window is ready, we can start listening for inbound (guest -> host) events
301
// on our event devices.
302
for req in self.event_device_wait_descriptor_requests.drain(..) {
303
if let Err(e) = self.gpu_display_wait_descriptor_ctrl.send(&req) {
304
error!(
305
"failed to send event device descriptor to GPU worker's wait context: {:?}",
306
e
307
);
308
return Err(GpuDisplayError::FailedEventDeviceListen(e));
309
}
310
}
311
312
Ok(Box::new(SurfaceWin {
313
surface_id,
314
wndproc_thread: Rc::downgrade(&self.wndproc_thread),
315
close_requested_event: self.close_requested_event.try_clone().map_err(|e| {
316
error!("Failed to clone close_requested_event: {}", e);
317
GpuDisplayError::Allocate
318
})?,
319
host_display,
320
}))
321
}
322
323
fn import_resource(
324
&mut self,
325
#[allow(unused_variables)] import_id: u32,
326
surface_id: u32,
327
#[allow(unused_variables)] external_display_resource: DisplayExternalResourceImport,
328
) -> Result<()> {
329
match self.host_displays.get(&surface_id) {
330
Some(host_display) => match *host_display.lock() {
331
#[cfg(feature = "vulkan_display")]
332
HostDisplayWrapper::Initialized(ref mut host_display) => {
333
match external_display_resource {
334
DisplayExternalResourceImport::VulkanImage {
335
descriptor,
336
metadata,
337
} => {
338
host_display.import_image(import_id, descriptor, metadata)?;
339
}
340
DisplayExternalResourceImport::VulkanTimelineSemaphore { descriptor } => {
341
host_display.import_semaphore(import_id, descriptor)?;
342
}
343
DisplayExternalResourceImport::Dmabuf { .. } => {
344
bail!("gpu_display_win does not support importing dmabufs")
345
}
346
DisplayExternalResourceImport::AHardwareBuffer { .. } => {
347
bail!("gpu_display_win does not support importing AHardwareBuffers")
348
}
349
}
350
Ok(())
351
}
352
HostDisplayWrapper::Uninitialized => {
353
bail!("HostDisplay is not initialized for this surface")
354
}
355
},
356
None => {
357
bail!("No HostDisplay for surface id {}", surface_id)
358
}
359
}
360
}
361
362
#[allow(unused_variables)]
363
fn release_import(&mut self, surface_id: u32, import_id: u32) {
364
#[cfg(feature = "vulkan_display")]
365
if let Some(host_display) = self.host_displays.get(&surface_id) {
366
if let HostDisplayWrapper::Initialized(ref mut host_display) = *host_display.lock() {
367
host_display.delete_imported_image_or_semaphore(import_id);
368
}
369
}
370
}
371
}
372
373
impl SysDisplayT for DisplayWin {
374
fn import_event_device(
375
&mut self,
376
event_device_id: u32,
377
event_device: EventDevice,
378
) -> GpuDisplayResult<()> {
379
self.import_event_device_internal(event_device_id, event_device)
380
.map_err(|e| {
381
GpuDisplayError::FailedEventDeviceImport(format!(
382
"Failed to import event device (ID: {event_device_id}): {e:?}"
383
))
384
})
385
}
386
387
fn handle_event_device(&mut self, event_device_id: u32) {
388
match ObjectId::new(event_device_id) {
389
Some(event_device_id) => {
390
if let Err(e) = self
391
.wndproc_thread
392
.post_display_command(DisplaySendToWndProc::HandleEventDevice(event_device_id))
393
{
394
error!(
395
"Failed to route guest -> host input_event; event device (ID: {:?}): {:?}",
396
event_device_id, e
397
);
398
}
399
}
400
None => error!(
401
"Failed to route guest -> host input_event; {} cannot be converted to ObjectId",
402
event_device_id
403
),
404
}
405
}
406
}
407
408
/// The display logic for Windows is quite different from other platforms since display events are
409
/// not handled on the GPU worker thread, but handled by `Surface` class that lives in the WndProc
410
/// thread. `SurfaceWin` will live in the GPU worker thread and provide limited functionalities.
411
pub(crate) struct SurfaceWin {
412
surface_id: u32,
413
wndproc_thread: std::rc::Weak<WindowProcedureThread>,
414
close_requested_event: Event,
415
#[allow(dead_code)]
416
host_display: Arc<Mutex<HostDisplayWrapper>>,
417
}
418
419
impl GpuDisplaySurface for SurfaceWin {
420
/// The entire VM will be shut down when this function returns true. We don't want that happen
421
/// until we know our display is expected to be closed.
422
fn close_requested(&self) -> bool {
423
match self
424
.close_requested_event
425
.wait_timeout(Duration::from_secs(0))
426
{
427
Ok(EventWaitResult::Signaled) => true,
428
Ok(EventWaitResult::TimedOut) => false,
429
Err(e) => {
430
error!("Failed to read whether display is closed: {}", e);
431
false
432
}
433
}
434
}
435
436
fn set_mouse_mode(&mut self, mouse_mode: MouseMode) {
437
if let Some(wndproc_thread) = self.wndproc_thread.upgrade() {
438
if let Err(e) =
439
wndproc_thread.post_display_command(DisplaySendToWndProc::SetMouseMode {
440
surface_id: self.surface_id,
441
mouse_mode,
442
})
443
{
444
warn!("Failed to post SetMouseMode message: {:?}", e);
445
}
446
}
447
}
448
449
#[cfg(not(feature = "vulkan_display"))]
450
fn flip_to(
451
&mut self,
452
_import_id: u32,
453
_acquire_timepoint: Option<SemaphoreTimepoint>,
454
_release_timepoint: Option<SemaphoreTimepoint>,
455
_extra_info: Option<FlipToExtraInfo>,
456
) -> Result<Waitable> {
457
bail!("host_display feature is not enabled")
458
}
459
460
#[cfg(feature = "vulkan_display")]
461
fn flip_to(
462
&mut self,
463
import_id: u32,
464
acquire_timepoint: Option<SemaphoreTimepoint>,
465
release_timepoint: Option<SemaphoreTimepoint>,
466
extra_info: Option<FlipToExtraInfo>,
467
) -> Result<Waitable> {
468
let last_layout_transition = match extra_info {
469
Some(FlipToExtraInfo::Vulkan {
470
old_layout,
471
new_layout,
472
}) => (old_layout, new_layout),
473
None => {
474
bail!("vulkan display flip_to requires old and new layout in extra_info")
475
}
476
};
477
478
let release_timepoint =
479
release_timepoint.ok_or(anyhow::anyhow!("release timepoint must be non-None"))?;
480
481
match *self.host_display.lock() {
482
HostDisplayWrapper::Initialized(ref mut host_display) => host_display.post(
483
import_id,
484
last_layout_transition,
485
acquire_timepoint,
486
release_timepoint,
487
),
488
HostDisplayWrapper::Uninitialized => {
489
bail!("HostDisplay is not initialized for this surface")
490
}
491
}
492
}
493
}
494
495
impl Drop for SurfaceWin {
496
fn drop(&mut self) {
497
info!("Dropping surface {}", self.surface_id);
498
// Let the WndProc thread release `Surface` and return the associated window to the pool.
499
// If the WndProc thread has already done so and has shut down, it is benign to hit an error
500
// below since we can no longer deliver this notification.
501
if let Some(wndproc_thread) = self.wndproc_thread.upgrade() {
502
if let Err(e) =
503
wndproc_thread.post_display_command(DisplaySendToWndProc::ReleaseSurface {
504
surface_id: self.surface_id,
505
})
506
{
507
warn!(
508
"Failed to post ReleaseSurface message (benign if message loop has \
509
shut down): {:?}",
510
e
511
);
512
}
513
}
514
}
515
}
516
517
#[cfg(test)]
518
mod tests {
519
use base::Tube;
520
521
use super::*;
522
523
#[test]
524
fn can_create_2_window_proc_threads() {
525
let threads = (0..2)
526
.map(|_| {
527
let (main_ime_tube, _device_ime_tube) =
528
Tube::pair().expect("failed to create IME tube");
529
let wndproc_thread_builder = WindowProcedureThread::builder();
530
#[cfg(feature = "kiwi")]
531
let wndproc_thread_builder = {
532
let mut wndproc_thread_builder = wndproc_thread_builder;
533
wndproc_thread_builder
534
.set_max_num_windows(1)
535
.set_display_tube(None)
536
.set_ime_tube(Some(_device_ime_tube));
537
wndproc_thread_builder
538
};
539
(
540
wndproc_thread_builder.start_thread().unwrap(),
541
main_ime_tube,
542
)
543
})
544
.collect::<Vec<_>>();
545
drop(threads);
546
}
547
}
548
549