Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/vulkan.rs
5392 views
1
// Copyright 2023 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
#![cfg_attr(unix, allow(dead_code))]
6
7
use std::cell::RefCell;
8
use std::collections::HashMap;
9
use std::sync::mpsc::channel;
10
use std::sync::mpsc::Receiver;
11
use std::sync::mpsc::RecvTimeoutError;
12
use std::sync::mpsc::Sender;
13
use std::sync::Arc;
14
use std::time::Duration;
15
16
use anyhow::anyhow;
17
use anyhow::ensure;
18
use anyhow::format_err;
19
use anyhow::Context;
20
use anyhow::Result;
21
use ash::vk::UUID_SIZE;
22
use ash::vk::{self};
23
use base::error;
24
use base::warn;
25
use base::AsRawDescriptor;
26
use euclid::Box2D;
27
use euclid::Size2D;
28
use euclid::UnknownUnit;
29
use smallvec::SmallVec;
30
31
mod external_image;
32
mod post_worker;
33
mod sys;
34
35
pub use external_image::AcquireImageMemoryBarrier;
36
pub use external_image::ExternalImage;
37
pub use external_image::ExternalImageAccess;
38
pub use external_image::ReleaseImageMemoryBarrier;
39
use post_worker::Timepoint;
40
use post_worker::VulkanPostWorker;
41
use sync::create_promise_and_waitable;
42
use sync::Promise;
43
use sync::Waitable;
44
use vulkano::device::Device;
45
use vulkano::image::sys::UnsafeImageCreateInfo;
46
use vulkano::image::ImageCreateFlags;
47
use vulkano::image::ImageDimensions;
48
use vulkano::image::ImageLayout;
49
use vulkano::image::ImageUsage;
50
use vulkano::memory::ExternalMemoryHandleTypes;
51
use vulkano::memory::MemoryAllocateInfo;
52
use vulkano::sync::ExternalSemaphoreHandleTypes;
53
use vulkano::sync::Semaphore;
54
use vulkano::sync::SemaphoreCreateInfo;
55
use vulkano::sync::Sharing;
56
use vulkano::VulkanLibrary;
57
use vulkano::VulkanObject;
58
59
use self::sys::platform::create_post_image_external_memory_handle_types;
60
use self::sys::platform::create_post_image_memory_import_info;
61
use self::sys::platform::import_semaphore_from_descriptor;
62
use self::sys::platform::NativeWindowType;
63
use self::sys::ApplicationState;
64
use self::sys::ApplicationStateBuilder;
65
pub(crate) use self::sys::PlatformWindowEventLoop;
66
use self::sys::Window;
67
use self::sys::WindowEvent;
68
use self::sys::WindowEventLoop;
69
use crate::SemaphoreTimepoint;
70
use crate::VulkanDisplayImageImportMetadata;
71
72
// Vulkan Safety Notes:
73
// Most vulkan APIs are unsafe, but even the wrapper APIs like ash and vulkano will mark their
74
// APIs as unsafe when they cannot ensure that they are 100% obeying the vulkan spec. For the
75
// purposes of HostDisplay, however, we do not consider disobeying the vulkan spec to be unsafe
76
// in terms of memory safety. Safety comments in these cases will say:
77
// "Safe irrespective of vulkan spec conformance"
78
//
79
// If the function is unsafe for any other reason we will still note why it's safe.
80
81
pub type SemaphoreId = u32;
82
pub type ImageId = u32;
83
84
pub enum UserEvent {
85
GetVulkanDevice(Sender<Arc<Device>>),
86
PostCommand {
87
image: ExternalImage,
88
last_layout_transition: (ImageLayout, ImageLayout),
89
acquire_timepoint: Option<Timepoint>,
90
release_timepoint: Timepoint,
91
image_return: Sender<ExternalImage>,
92
promise: Promise,
93
},
94
}
95
96
pub struct HostState {
97
// Post worker submits renders and posts to vulkan. It needs to be in a RefCell because
98
// process_event cannot take a mutable reference to ApplicationState.
99
post_worker: RefCell<VulkanPostWorker>,
100
}
101
102
impl ApplicationState for HostState {
103
type UserEvent = UserEvent;
104
105
/// Process events coming from the Window.
106
fn process_event(&self, event: WindowEvent<Self::UserEvent>) {
107
match event {
108
WindowEvent::User(UserEvent::GetVulkanDevice(sender)) => {
109
sender
110
.send(self.post_worker.borrow().device())
111
.expect("Should send VkDevice back to the caller successfully.");
112
}
113
WindowEvent::User(UserEvent::PostCommand {
114
image,
115
last_layout_transition,
116
acquire_timepoint,
117
release_timepoint,
118
image_return,
119
promise,
120
}) => {
121
// If this post triggers a resize event then the recursive wndproc call will
122
// call into this function again and trigger another borrow which will panic.
123
// TODO (b/314379499): figure out a way to avoid this
124
let image = self.post_worker.borrow_mut().post(
125
image,
126
last_layout_transition,
127
acquire_timepoint,
128
release_timepoint,
129
);
130
promise.signal();
131
image_return
132
.send(image)
133
.expect("Should send ExternalImage back to the caller successfully.");
134
}
135
WindowEvent::Resized => {
136
// If this resize event triggers another resize event then this will fail.
137
// TODO (b/314379499): figure out a way to avoid this
138
if let Err(err) = self.post_worker.borrow_mut().recreate_swapchain() {
139
panic!(
140
concat!(
141
"Failed to recreate the swapchain when handling the Resized window ",
142
"event: {:?}."
143
),
144
err
145
);
146
}
147
}
148
}
149
}
150
}
151
152
struct HostStateBuilder {
153
vulkan_library: Arc<VulkanLibrary>,
154
device_uuid: [u8; vk::UUID_SIZE],
155
driver_uuid: [u8; vk::UUID_SIZE],
156
}
157
158
impl ApplicationStateBuilder for HostStateBuilder {
159
type Target = HostState;
160
161
fn build<T: Window>(self, window: Arc<T>) -> Result<HostState> {
162
let post_worker = VulkanPostWorker::new(
163
self.vulkan_library,
164
&self.device_uuid,
165
&self.driver_uuid,
166
Arc::clone(&window) as _,
167
)
168
.context("creating the post worker")?;
169
Ok(HostState {
170
post_worker: RefCell::new(post_worker),
171
})
172
}
173
}
174
175
pub struct HostDisplayImpl<T: WindowEventLoop<HostState>> {
176
ash_device: ash::Device,
177
device: Arc<Device>,
178
window_event_loop: T,
179
imported_semaphores: HashMap<SemaphoreId, Arc<Semaphore>>,
180
imported_images: HashMap<ImageId, ExternalImage>,
181
used_image_receivers: HashMap<ImageId, Receiver<ExternalImage>>,
182
}
183
184
impl<T: WindowEventLoop<HostState>> HostDisplayImpl<T> {
185
/// # Safety
186
/// The parent window must outlive the lifetime of this object.
187
#[deny(unsafe_op_in_unsafe_fn)]
188
pub unsafe fn new(
189
vulkan_library: Arc<VulkanLibrary>,
190
parent: NativeWindowType,
191
initial_window_size: &Size2D<i32, UnknownUnit>,
192
device_uuid: [u8; UUID_SIZE],
193
driver_uuid: [u8; UUID_SIZE],
194
) -> Result<Self> {
195
let vulkan_state_builder = HostStateBuilder {
196
vulkan_library,
197
device_uuid,
198
driver_uuid,
199
};
200
// SAFETY: Safe because it is guaranteed by the safety requirement of this function that
201
// the parent window outlives the event loop object.
202
let window_event_loop = unsafe {
203
T::create(parent, initial_window_size, vulkan_state_builder)
204
.context("create window and event loop")?
205
};
206
let (vk_device_tx, vk_device_rx) = channel();
207
window_event_loop
208
.send_event(UserEvent::GetVulkanDevice(vk_device_tx))
209
.context("retrieve VkDevice from the window event loop")?;
210
let device = loop {
211
const TIMEOUT: Duration = Duration::from_secs(60);
212
match vk_device_rx.recv_timeout(TIMEOUT) {
213
Ok(value) => break value,
214
215
Err(RecvTimeoutError::Timeout) => {
216
warn!(
217
"Didn't receive the VkDevice from the event loop for {:?}. Retry.",
218
TIMEOUT
219
);
220
continue;
221
}
222
Err(e) => {
223
return Err(format_err!(
224
"Failed to receive VkDevice from the event loop: {:?}.",
225
e
226
));
227
}
228
}
229
};
230
let ash_device =
231
// SAFETY: Safe because we trust the vulkan device we get from the window event loop and
232
// the instance_fn comes from an instance we know is valid because the device was created
233
// with it.
234
unsafe { ash::Device::load(&device.instance().fns().v1_0, device.internal_object()) };
235
Ok(Self {
236
ash_device,
237
device,
238
window_event_loop,
239
imported_semaphores: Default::default(),
240
imported_images: Default::default(),
241
used_image_receivers: Default::default(),
242
})
243
}
244
245
pub fn move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()> {
246
self.window_event_loop.move_window(pos)
247
}
248
249
pub fn import_semaphore(
250
&mut self,
251
semaphore_id: SemaphoreId,
252
descriptor: &dyn AsRawDescriptor,
253
) -> Result<()> {
254
let mut type_create_info = vk::SemaphoreTypeCreateInfo::builder()
255
.semaphore_type(vk::SemaphoreType::TIMELINE)
256
.initial_value(0)
257
.build();
258
let create_info = vk::SemaphoreCreateInfo::builder()
259
.push_next(&mut type_create_info)
260
.build();
261
// SAFETY: Safe because create_info and it's fields are local to this function and outlive
262
// this function call.
263
let semaphore = unsafe { self.ash_device.create_semaphore(&create_info, None) }
264
.context("create timeline semaphore")?;
265
266
let res = import_semaphore_from_descriptor(&self.device, semaphore, descriptor);
267
ensure!(
268
res == vk::Result::SUCCESS,
269
"Failed to import the external handle to the semaphore: {}.",
270
res
271
);
272
273
// SAFETY: Safe irrespective of vulkan spec conformance
274
let res = unsafe {
275
Semaphore::from_handle(
276
Arc::clone(&self.device),
277
semaphore,
278
SemaphoreCreateInfo {
279
// Note that as of vulkano version 0.34.1, this
280
// export_handle_types field is only used to validate which
281
// export APIs can be used in the future. We do not export
282
// this semaphore so we do not need to specify any export
283
// handle types.
284
export_handle_types: ExternalSemaphoreHandleTypes::empty(),
285
..Default::default()
286
},
287
)
288
};
289
290
if self
291
.imported_semaphores
292
.insert(semaphore_id, Arc::new(res))
293
.is_some()
294
{
295
warn!("Reused semaphore_id {}", semaphore_id);
296
}
297
298
Ok(())
299
}
300
301
pub fn import_image(
302
&mut self,
303
image_id: ImageId,
304
descriptor: &dyn AsRawDescriptor,
305
metadata: VulkanDisplayImageImportMetadata,
306
) -> Result<()> {
307
let image_create_flags = metadata.flags;
308
let ImageCreateFlags {
309
sparse_binding,
310
sparse_residency,
311
sparse_aliased,
312
mutable_format,
313
cube_compatible,
314
array_2d_compatible,
315
block_texel_view_compatible,
316
_ne: _,
317
} = vk::ImageCreateFlags::from_raw(image_create_flags).into();
318
assert!(
319
!(sparse_binding || sparse_residency || sparse_aliased),
320
"unsupported image create flags {image_create_flags:#x}"
321
);
322
let image_type = vk::ImageType::from_raw(metadata.image_type);
323
let image_extent = metadata.extent;
324
let image_dimensions = match image_type {
325
vk::ImageType::TYPE_2D => ImageDimensions::Dim2d {
326
width: image_extent.width,
327
height: image_extent.height,
328
array_layers: metadata.array_layers,
329
},
330
_ => unimplemented!(),
331
};
332
let format = {
333
let format = metadata.format;
334
vk::Format::from_raw(format)
335
.try_into()
336
.map_err(|_| format_err!("Failed to convert {:#x} to format.", format))?
337
};
338
let image_samples = {
339
let samples = metadata.samples;
340
vk::SampleCountFlags::from_raw(samples)
341
.try_into()
342
.map_err(|_| {
343
format_err!("Failed to convert {:#x} to sample count flag.", samples)
344
})?
345
};
346
let image_tiling = {
347
let tiling = metadata.tiling;
348
vk::ImageTiling::from_raw(tiling)
349
.try_into()
350
.map_err(|_| format_err!("Failed to convert {:#x} to image tiling enum.", tiling))?
351
};
352
let image_usage = {
353
let usage = metadata.usage;
354
vk::ImageUsageFlags::from_raw(usage).into()
355
};
356
let image_sharing = {
357
let sharing_mode = metadata.sharing_mode;
358
match vk::SharingMode::from_raw(sharing_mode) {
359
vk::SharingMode::EXCLUSIVE => Sharing::Exclusive,
360
vk::SharingMode::CONCURRENT => {
361
let mut queue_family_indices = SmallVec::new();
362
queue_family_indices.copy_from_slice(&metadata.queue_family_indices);
363
Sharing::Concurrent(queue_family_indices)
364
}
365
_ => return Err(format_err!("Invalid sharing mode {:#x}.", sharing_mode)),
366
}
367
};
368
let image_initial_layout = {
369
let initial_layout = metadata.initial_layout;
370
vk::ImageLayout::from_raw(initial_layout)
371
.try_into()
372
.map_err(|_| {
373
format_err!(
374
"Failed to convert the initial layout {:#x} to an image layout.",
375
initial_layout
376
)
377
})?
378
};
379
380
let image = ExternalImage::import(
381
&self.device,
382
UnsafeImageCreateInfo {
383
dimensions: image_dimensions,
384
format: Some(format),
385
mip_levels: metadata.mip_levels,
386
samples: image_samples,
387
tiling: image_tiling,
388
usage: image_usage,
389
stencil_usage: ImageUsage::empty(),
390
sharing: image_sharing,
391
initial_layout: image_initial_layout,
392
external_memory_handle_types: create_post_image_external_memory_handle_types(),
393
mutable_format,
394
cube_compatible,
395
array_2d_compatible,
396
block_texel_view_compatible,
397
..Default::default()
398
},
399
MemoryAllocateInfo {
400
allocation_size: metadata.allocation_size,
401
memory_type_index: metadata.memory_type_index,
402
export_handle_types: ExternalMemoryHandleTypes::empty(),
403
..Default::default()
404
},
405
create_post_image_memory_import_info(descriptor),
406
metadata.dedicated_allocation,
407
0,
408
)
409
.context("import the composition result image")?;
410
411
if self.imported_images.insert(image_id, image).is_some() {
412
warn!("Reused image_id {}", image_id);
413
}
414
Ok(())
415
}
416
417
pub fn delete_imported_image_or_semaphore(&mut self, import_id: u32) {
418
// Import ids are shared between images and semaphores, so first try to remove from
419
// self.imported_sempahores, and if that returns none then try to remove from images.
420
if self.imported_semaphores.remove(&import_id).is_none() {
421
if let Some(receiver) = self.used_image_receivers.remove(&import_id) {
422
if let Err(e) = receiver.recv() {
423
error!("Failed to receive used image from post worker: {}", e);
424
}
425
} else if self.imported_images.remove(&import_id).is_none() {
426
error!("Import id {} has not been imported", import_id);
427
}
428
}
429
}
430
431
pub fn post(
432
&mut self,
433
image_id: ImageId,
434
last_layout_transition: (i32, i32),
435
acquire_semaphore: Option<SemaphoreTimepoint>,
436
release_semaphore: SemaphoreTimepoint,
437
) -> Result<Waitable> {
438
let image = if let Some(receiver) = self.used_image_receivers.remove(&image_id) {
439
receiver
440
.recv()
441
.context("failed to receive used image from post worker")?
442
} else {
443
self.imported_images
444
.remove(&image_id)
445
.ok_or(anyhow!("Image id {} has not been imported", image_id))?
446
};
447
448
let acquire_timepoint =
449
if let Some(SemaphoreTimepoint { import_id, value }) = acquire_semaphore {
450
let semaphore = self
451
.imported_semaphores
452
.get(&import_id)
453
.ok_or(anyhow!("Semaphore id {} has not been imported", import_id))?;
454
Some(Timepoint {
455
semaphore: semaphore.clone(),
456
value,
457
})
458
} else {
459
None
460
};
461
462
let release_timepoint = {
463
let semaphore = self
464
.imported_semaphores
465
.get(&release_semaphore.import_id)
466
.ok_or(anyhow!(
467
"Semaphore id {} has not been imported",
468
release_semaphore.import_id
469
))?;
470
Timepoint {
471
semaphore: semaphore.clone(),
472
value: release_semaphore.value,
473
}
474
};
475
476
let last_layout_transition: (ImageLayout, ImageLayout) = (
477
ash::vk::ImageLayout::from_raw(last_layout_transition.0)
478
.try_into()
479
.map_err(|_| {
480
anyhow!(
481
"Failed to convert {:#x} to a valid image layout.",
482
last_layout_transition.0
483
)
484
})?,
485
ash::vk::ImageLayout::from_raw(last_layout_transition.1)
486
.try_into()
487
.map_err(|_| {
488
anyhow!(
489
"Failed to convert {:#x} to a valid image layout.",
490
last_layout_transition.1
491
)
492
})?,
493
);
494
495
let (promise, waitable) = create_promise_and_waitable();
496
497
let (image_return_tx, image_return_rx) = channel();
498
self.used_image_receivers.insert(image_id, image_return_rx);
499
500
self.window_event_loop
501
.send_event(UserEvent::PostCommand {
502
image,
503
last_layout_transition,
504
acquire_timepoint,
505
release_timepoint,
506
image_return: image_return_tx,
507
promise,
508
})
509
.context("send user defined message to the window event loop")?;
510
Ok(waitable)
511
}
512
}
513
514
pub(crate) type HostDisplay = HostDisplayImpl<PlatformWindowEventLoop<HostState>>;
515
516