Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/gpu_display/src/vulkan/post_worker.rs
5394 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
use std::any::Any;
6
use std::borrow::Borrow;
7
use std::collections::BTreeSet;
8
use std::marker::PhantomData;
9
use std::rc::Rc;
10
use std::sync::Arc;
11
use std::time::Duration;
12
13
use anyhow::bail;
14
use anyhow::ensure;
15
use anyhow::format_err;
16
use anyhow::Context;
17
use anyhow::Result;
18
use ash::vk::UUID_SIZE;
19
use ash::vk::{self};
20
use base::error;
21
use base::info;
22
use euclid::size2;
23
use euclid::Size2D;
24
use rand::rngs::ThreadRng;
25
use rand::seq::IteratorRandom;
26
use smallvec::SmallVec;
27
use vulkano::command_buffer::pool::standard::StandardCommandPoolAlloc;
28
use vulkano::command_buffer::pool::CommandPool;
29
use vulkano::command_buffer::pool::CommandPoolAlloc;
30
use vulkano::command_buffer::pool::CommandPoolBuilderAlloc;
31
use vulkano::command_buffer::pool::StandardCommandPool;
32
use vulkano::command_buffer::submit::SubmitPresentBuilder;
33
use vulkano::command_buffer::submit::SubmitPresentError;
34
use vulkano::command_buffer::sys::CommandBufferBeginInfo;
35
use vulkano::command_buffer::sys::UnsafeCommandBufferBuilder;
36
use vulkano::command_buffer::BlitImageInfo;
37
use vulkano::command_buffer::CommandBufferLevel;
38
use vulkano::command_buffer::CommandBufferUsage;
39
use vulkano::device::physical::PhysicalDevice;
40
use vulkano::device::Device;
41
use vulkano::device::DeviceCreateInfo;
42
use vulkano::device::DeviceExtensions;
43
use vulkano::device::Features;
44
use vulkano::device::Queue;
45
use vulkano::device::QueueCreateInfo;
46
use vulkano::format::Format;
47
use vulkano::image::ImageAccess;
48
use vulkano::image::ImageAspects;
49
use vulkano::image::ImageLayout;
50
use vulkano::image::ImageSubresourceRange;
51
use vulkano::image::ImageUsage;
52
use vulkano::image::{self};
53
use vulkano::instance::Instance;
54
use vulkano::instance::InstanceCreateInfo;
55
use vulkano::instance::InstanceExtensions;
56
use vulkano::swapchain::acquire_next_image_raw;
57
use vulkano::swapchain::AcquireError;
58
use vulkano::swapchain::AcquiredImage;
59
use vulkano::swapchain::CompositeAlpha;
60
use vulkano::swapchain::PresentInfo;
61
use vulkano::swapchain::PresentMode;
62
use vulkano::swapchain::SwapchainCreateInfo;
63
use vulkano::swapchain::{self};
64
use vulkano::sync::AccessFlags;
65
use vulkano::sync::DependencyInfo;
66
use vulkano::sync::ExternalFenceHandleTypes;
67
use vulkano::sync::Fence;
68
use vulkano::sync::FenceCreateInfo;
69
use vulkano::sync::ImageMemoryBarrier;
70
use vulkano::sync::PipelineStages;
71
use vulkano::sync::QueueFamilyTransfer;
72
use vulkano::sync::Semaphore;
73
use vulkano::sync::SemaphoreCreateInfo;
74
use vulkano::sync::Sharing;
75
use vulkano::SynchronizedVulkanObject;
76
use vulkano::Version;
77
use vulkano::VulkanLibrary;
78
use vulkano::VulkanObject;
79
80
use super::sys::Window;
81
use super::ExternalImage;
82
use crate::vulkan::external_image::AcquireImageMemoryBarrier;
83
use crate::vulkan::external_image::ReleaseImageMemoryBarrier;
84
85
type VulkanoWindow = Arc<dyn Any + Send + Sync>;
86
type Surface = swapchain::Surface<VulkanoWindow>;
87
type Swapchain = swapchain::Swapchain<VulkanoWindow>;
88
type SwapchainImage = image::swapchain::SwapchainImage<VulkanoWindow>;
89
90
fn create_swapchain_create_info<T>(
91
physical_device: &PhysicalDevice,
92
surface: &Surface,
93
image_size: &Size2D<u32, T>,
94
) -> Result<SwapchainCreateInfo> {
95
let surface_capabilities = physical_device
96
.surface_capabilities(surface, Default::default())
97
.context("query surface cpabilities")?;
98
ensure!(
99
surface_capabilities.supported_usage_flags.transfer_dst,
100
"The swapchain image must support USAGE_TRANSFER_DST. Supported usages: {:?}",
101
surface_capabilities,
102
);
103
if let Some([width, height]) = surface_capabilities.current_extent {
104
ensure!(
105
*image_size == size2(width, height),
106
"The passed in size {}x{} doesn't match the current surface extent {}x{}.",
107
image_size.width,
108
image_size.height,
109
width,
110
height
111
);
112
}
113
ensure!(
114
image_size.width <= surface_capabilities.max_image_extent[0]
115
&& image_size.width >= surface_capabilities.min_image_extent[0],
116
"The passed in width {} must be within the range of [{}, {}].",
117
image_size.width,
118
surface_capabilities.min_image_extent[0],
119
surface_capabilities.max_image_extent[0]
120
);
121
ensure!(
122
image_size.height <= surface_capabilities.max_image_extent[1]
123
&& image_size.height >= surface_capabilities.min_image_extent[1],
124
"The passed in width {} must be within the range of [{}, {}].",
125
image_size.height,
126
surface_capabilities.min_image_extent[1],
127
surface_capabilities.max_image_extent[1]
128
);
129
// Triple buffering if possible.
130
let min_image_count = 3.clamp(
131
surface_capabilities.min_image_count,
132
surface_capabilities.max_image_count.unwrap_or(u32::MAX),
133
);
134
let pre_transform = surface_capabilities.current_transform;
135
let available_format_and_color_space = physical_device
136
.surface_formats(surface, Default::default())
137
.context("query formats and color spaces supported by the surface")?;
138
let (image_format, image_color_space) = available_format_and_color_space
139
.iter()
140
.find(|(image_format, _)| matches!(image_format, Format::B8G8R8A8_UNORM))
141
.copied()
142
.ok_or_else(|| {
143
format_err!(
144
concat!(
145
"No supported formats and color spaces found. All supported formats and color ",
146
"spaces are {:?}"
147
),
148
available_format_and_color_space
149
)
150
})?;
151
let present_modes = physical_device
152
.surface_present_modes(surface)
153
.context("query the supported present mode")?
154
.collect::<Vec<_>>();
155
assert!(
156
present_modes
157
.iter()
158
.any(|mode| matches!(mode, PresentMode::Fifo)),
159
concat!(
160
"The Vulkan spec requires the support of FIFO present mode, but it is not supported. ",
161
"All supported present modes: {:?}."
162
),
163
present_modes
164
);
165
let present_mode = PresentMode::Fifo;
166
Ok(SwapchainCreateInfo {
167
min_image_count,
168
image_format: Some(image_format),
169
image_color_space,
170
image_extent: [image_size.width, image_size.height],
171
image_array_layers: 1,
172
image_usage: ImageUsage {
173
transfer_dst: true,
174
..ImageUsage::empty()
175
},
176
image_sharing: Sharing::Exclusive,
177
pre_transform,
178
composite_alpha: CompositeAlpha::Opaque,
179
present_mode,
180
clipped: true,
181
..Default::default()
182
})
183
}
184
185
#[derive(Clone)]
186
pub struct Timepoint {
187
pub semaphore: Arc<Semaphore>,
188
pub value: u64,
189
}
190
191
/// PostResource contains the required structures and information for posting to an individual
192
/// swapchain image.
193
struct PostResource {
194
acquire_swapchain_image_semaphore: Semaphore,
195
command_buffer_alloc: StandardCommandPoolAlloc,
196
command_complete_fence: Fence,
197
command_complete_semaphore: Semaphore,
198
}
199
200
impl PostResource {
201
fn new(device: &Arc<Device>, command_buffer_alloc: StandardCommandPoolAlloc) -> Result<Self> {
202
let acquire_swapchain_image_semaphore =
203
Semaphore::new(Arc::clone(device), SemaphoreCreateInfo::default())
204
.context("create semaphore for acquiring next swapchain image")?;
205
let command_complete_semaphore =
206
Semaphore::new(Arc::clone(device), SemaphoreCreateInfo::default())
207
.context("create semaphore to be signaled after the command has completed")?;
208
let command_complete_fence = Fence::new(
209
Arc::clone(device),
210
FenceCreateInfo {
211
signaled: true,
212
export_handle_types: ExternalFenceHandleTypes::empty(),
213
..Default::default()
214
},
215
)
216
.context("create fence signaled when the command has completed")?;
217
Ok(Self {
218
acquire_swapchain_image_semaphore,
219
command_buffer_alloc,
220
command_complete_fence,
221
command_complete_semaphore,
222
})
223
}
224
225
/// Submit a blit of post_image to swap_chain image.
226
///
227
/// The image in `post_image` needs to be transferred from an ExternalImage before it can be
228
/// used as a blit source. It also may need to be transitioned to TransferSrcOptimal. This
229
/// function will transition it back to an ExternalImage as part of the same submission. The
230
/// image in `swapchain_image` will also be transferred to TransferDstOptimal for the blit and
231
/// then finally to PresentSrc after the blit.
232
fn record_and_submit_post_command(
233
&mut self,
234
device: &Device,
235
ash_device: &ash::Device,
236
post_image: ExternalImage,
237
last_layout_transition: (ImageLayout, ImageLayout),
238
swapchain_image: Arc<SwapchainImage>,
239
graphics_queue: &Queue,
240
present_queue: &Queue,
241
post_image_acquire_timepoint: Option<&Timepoint>,
242
post_image_release_timepoint: &Timepoint,
243
) -> ExternalImage {
244
assert_eq!(
245
device.internal_object(),
246
ash_device.handle(),
247
"The vulkano device and the ash device must refer to the same VkDevice."
248
);
249
// SAFETY: Safe because self.command_buffer_alloc outlives command_buffer_builder.
250
let mut command_buffer_builder = unsafe {
251
UnsafeCommandBufferBuilder::new(
252
self.command_buffer_alloc.inner(),
253
CommandBufferBeginInfo {
254
usage: CommandBufferUsage::OneTimeSubmit,
255
inheritance_info: None,
256
..Default::default()
257
},
258
)
259
}
260
.unwrap_or_else(|e| panic!("Failed to begin recording the command buffer: {e:?}"));
261
let post_image_old_layout = last_layout_transition.0;
262
// SAFETY: Safe because the image in image_access (which is eventually converted back to
263
// post_image where it still lives) outlives the command buffer.
264
let image_access = unsafe {
265
post_image.acquire(
266
&mut command_buffer_builder,
267
AcquireImageMemoryBarrier {
268
source_stages: PipelineStages {
269
transfer: true,
270
..PipelineStages::empty()
271
},
272
destination_stages: PipelineStages {
273
transfer: true,
274
..PipelineStages::empty()
275
},
276
destination_access: AccessFlags {
277
transfer_read: true,
278
..AccessFlags::empty()
279
},
280
destination_queue_family_index: graphics_queue.queue_family_index(),
281
subresource_range: ImageSubresourceRange {
282
aspects: ImageAspects {
283
color: true,
284
..ImageAspects::empty()
285
},
286
mip_levels: 0..1,
287
array_layers: 0..1,
288
},
289
},
290
last_layout_transition,
291
)
292
};
293
let mut image_memory_barriers = SmallVec::from_vec(vec![ImageMemoryBarrier {
294
source_stages: PipelineStages {
295
transfer: true,
296
..PipelineStages::empty()
297
},
298
source_access: AccessFlags::empty(),
299
destination_stages: PipelineStages {
300
transfer: true,
301
..PipelineStages::empty()
302
},
303
destination_access: AccessFlags {
304
transfer_write: true,
305
..AccessFlags::empty()
306
},
307
old_layout: ImageLayout::Undefined,
308
new_layout: ImageLayout::TransferDstOptimal,
309
// No need for ownership transfer, because we don't care about the content.
310
queue_family_transfer: None,
311
subresource_range: ImageSubresourceRange {
312
aspects: ImageAspects {
313
color: true,
314
..ImageAspects::empty()
315
},
316
mip_levels: 0..1,
317
array_layers: 0..1,
318
},
319
..ImageMemoryBarrier::image(Arc::clone(swapchain_image.inner().image))
320
}]);
321
if !matches!(
322
image_access.initial_layout_requirement(),
323
ImageLayout::TransferSrcOptimal
324
) {
325
image_memory_barriers.push(ImageMemoryBarrier {
326
source_stages: PipelineStages {
327
transfer: true,
328
..PipelineStages::empty()
329
},
330
source_access: AccessFlags {
331
transfer_read: true,
332
..AccessFlags::empty()
333
},
334
destination_stages: PipelineStages {
335
transfer: true,
336
..PipelineStages::empty()
337
},
338
destination_access: AccessFlags {
339
transfer_read: true,
340
..AccessFlags::empty()
341
},
342
old_layout: image_access.initial_layout_requirement(),
343
new_layout: ImageLayout::TransferSrcOptimal,
344
queue_family_transfer: None,
345
subresource_range: ImageSubresourceRange {
346
aspects: ImageAspects {
347
color: true,
348
..ImageAspects::empty()
349
},
350
mip_levels: 0..1,
351
array_layers: 0..1,
352
},
353
..ImageMemoryBarrier::image(Arc::clone(image_access.inner().image))
354
});
355
}
356
// SAFETY: Safe because the image in image_access (which is eventually converted back to
357
// post_image where it still lives) outlives the command buffer.
358
unsafe {
359
command_buffer_builder.pipeline_barrier(&DependencyInfo {
360
image_memory_barriers,
361
..Default::default()
362
});
363
}
364
let image_access = Arc::new(image_access);
365
// SAFETY: Safe because the image in image_access (which is eventually converted back to
366
// post_image where it still lives) outlives the command buffer, and because
367
// swapchain_image lives until the end of this function, thus outliving the command buffer.
368
unsafe {
369
command_buffer_builder.blit_image(&BlitImageInfo::images(
370
Arc::clone(&image_access) as Arc<_>,
371
Arc::clone(&swapchain_image) as Arc<_>,
372
));
373
}
374
let queue_family_transfer =
375
if graphics_queue.queue_family_index() == present_queue.queue_family_index() {
376
None
377
} else {
378
Some(QueueFamilyTransfer {
379
source_index: graphics_queue.queue_family_index(),
380
destination_index: present_queue.queue_family_index(),
381
})
382
};
383
let mut image_memory_barriers = SmallVec::from_vec(vec![ImageMemoryBarrier {
384
source_stages: PipelineStages {
385
transfer: true,
386
..PipelineStages::empty()
387
},
388
source_access: AccessFlags {
389
transfer_write: true,
390
..AccessFlags::empty()
391
},
392
destination_stages: PipelineStages::empty(),
393
destination_access: AccessFlags::empty(),
394
old_layout: ImageLayout::TransferDstOptimal,
395
new_layout: ImageLayout::PresentSrc,
396
queue_family_transfer,
397
subresource_range: ImageSubresourceRange {
398
aspects: ImageAspects {
399
color: true,
400
..ImageAspects::empty()
401
},
402
mip_levels: 0..1,
403
array_layers: 0..1,
404
},
405
..ImageMemoryBarrier::image(Arc::clone(swapchain_image.inner().image))
406
}]);
407
if !matches!(
408
image_access.final_layout_requirement(),
409
ImageLayout::TransferSrcOptimal
410
) {
411
image_memory_barriers.push(ImageMemoryBarrier {
412
source_stages: PipelineStages {
413
transfer: true,
414
..PipelineStages::empty()
415
},
416
source_access: AccessFlags {
417
transfer_read: true,
418
..AccessFlags::empty()
419
},
420
destination_stages: PipelineStages {
421
transfer: true,
422
..PipelineStages::empty()
423
},
424
destination_access: AccessFlags {
425
transfer_read: true,
426
..AccessFlags::empty()
427
},
428
old_layout: ImageLayout::TransferSrcOptimal,
429
new_layout: image_access.final_layout_requirement(),
430
queue_family_transfer: None,
431
subresource_range: ImageSubresourceRange {
432
aspects: ImageAspects {
433
color: true,
434
..ImageAspects::empty()
435
},
436
mip_levels: 0..1,
437
array_layers: 0..1,
438
},
439
..ImageMemoryBarrier::image(Arc::clone(image_access.inner().image))
440
});
441
}
442
// SAFETY: Safe because the image in image_access (which is eventually converted back to
443
// post_image where it still lives) outlives the command buffer.
444
unsafe {
445
command_buffer_builder.pipeline_barrier(&DependencyInfo {
446
image_memory_barriers,
447
..Default::default()
448
});
449
}
450
let image_access = Arc::try_unwrap(image_access).expect("should be the sole owner");
451
// SAFETY: Safe because the image in post_image outlives the command buffer.
452
let post_image = unsafe {
453
image_access.release(
454
&mut command_buffer_builder,
455
ReleaseImageMemoryBarrier {
456
source_stages: PipelineStages {
457
transfer: true,
458
..PipelineStages::empty()
459
},
460
source_access: AccessFlags {
461
transfer_read: true,
462
..AccessFlags::empty()
463
},
464
destination_stages: PipelineStages {
465
transfer: true,
466
..PipelineStages::empty()
467
},
468
new_layout: post_image_old_layout,
469
source_queue_family_index: graphics_queue.queue_family_index(),
470
},
471
)
472
};
473
let command_buffer = command_buffer_builder
474
.build()
475
.unwrap_or_else(|e| panic!("Failed to end the command buffer recording: {e:#}"));
476
477
assert!(self
478
.command_complete_fence
479
.is_signaled()
480
.unwrap_or_else(|e| panic!("Failed to query the status of the fence: {e:?}")));
481
self.command_complete_fence
482
.reset()
483
.unwrap_or_else(|e| panic!("Failed to reset the fence for the post resources: {e:?}"));
484
{
485
// The first values are not used, because they are not binary semaphores.
486
let mut wait_semaphore_values = vec![0];
487
let signal_semaphore_values = [0, post_image_release_timepoint.value];
488
let mut wait_semaphores =
489
vec![self.acquire_swapchain_image_semaphore.internal_object()];
490
let mut wait_dst_stage_mask = vec![vk::PipelineStageFlags::TRANSFER];
491
let command_buffers = [command_buffer.internal_object()];
492
let signal_semaphores = [
493
self.command_complete_semaphore.internal_object(),
494
post_image_release_timepoint.semaphore.internal_object(),
495
];
496
497
if let Some(post_image_acquire_timepoint) = post_image_acquire_timepoint {
498
wait_semaphore_values.push(post_image_acquire_timepoint.value);
499
wait_semaphores.push(post_image_acquire_timepoint.semaphore.internal_object());
500
wait_dst_stage_mask.push(vk::PipelineStageFlags::TRANSFER);
501
}
502
503
let mut timeline_semaphore_submit_info = vk::TimelineSemaphoreSubmitInfo::builder()
504
.wait_semaphore_values(&wait_semaphore_values)
505
.signal_semaphore_values(&signal_semaphore_values)
506
.build();
507
let submit_info = vk::SubmitInfo::builder()
508
.wait_semaphores(&wait_semaphores)
509
.wait_dst_stage_mask(&wait_dst_stage_mask)
510
.command_buffers(&command_buffers)
511
.signal_semaphores(&signal_semaphores)
512
.push_next(&mut timeline_semaphore_submit_info)
513
.build();
514
515
// SAFETY: Safe because the contents of submit_info and all the things it points to
516
// outlive this function call.
517
unsafe {
518
ash_device.queue_submit(
519
*graphics_queue.internal_object_guard(),
520
&[submit_info],
521
self.command_complete_fence.internal_object(),
522
)
523
}
524
.unwrap_or_else(|e| panic!("Failed to submit the command: {e:?}"));
525
}
526
post_image
527
}
528
}
529
530
/// VulkanPostWorker owns the vulkan surface and swapchain, and can post images to it.
531
pub struct VulkanPostWorker {
532
physical_device: Arc<PhysicalDevice>,
533
ash_device: Arc<ash::Device>,
534
device: Arc<Device>,
535
window: Arc<dyn Window>,
536
swapchain: Arc<Swapchain>,
537
swapchain_images: Vec<Arc<SwapchainImage>>,
538
graphics_queue: Arc<Queue>,
539
present_queue: Arc<Queue>,
540
post_resources: Vec<PostResource>,
541
_command_pool: Arc<StandardCommandPool>,
542
rng: ThreadRng,
543
// Mark Worker as !Sync and !Send
544
_marker: PhantomData<Rc<()>>,
545
}
546
547
impl VulkanPostWorker {
548
/// Initialize the post worker which does the following:
549
/// - Create the VkInstance
550
/// - Create the VkDevice and VkQueue
551
/// - Create the Swapchain
552
/// - Create a PostResource for each swapchain image
553
pub(crate) fn new(
554
vulkan_library: Arc<VulkanLibrary>,
555
device_uuid: &[u8; UUID_SIZE],
556
driver_uuid: &[u8; UUID_SIZE],
557
window: Arc<dyn Window>,
558
) -> Result<Self> {
559
// Create the Vulkan instance.
560
let api_version = vulkan_library.api_version();
561
if api_version < Version::V1_1 {
562
bail!("Vulkan instance version too low: {:?}", api_version);
563
}
564
let instance = Instance::new(
565
vulkan_library,
566
InstanceCreateInfo {
567
application_name: Some("vulkan_display_host".to_owned()),
568
enabled_extensions: InstanceExtensions {
569
khr_external_memory_capabilities: true,
570
khr_get_physical_device_properties2: true,
571
khr_surface: true,
572
khr_win32_surface: true,
573
..InstanceExtensions::empty()
574
},
575
..Default::default()
576
},
577
)
578
.context("create VkInstance")?;
579
assert!(instance.api_version() >= Version::V1_1);
580
581
// Choose the Vulkan physical device.
582
let mut physical_devices = instance
583
.enumerate_physical_devices()
584
.context("enumerate physical devices")?;
585
let physical_device = physical_devices.find(|physical_device| {
586
let properties = physical_device.properties();
587
if let (Some(current_device_uuid), Some(current_driver_uuid)) = (
588
properties.device_uuid.as_ref(),
589
properties.driver_uuid.as_ref(),
590
) {
591
current_device_uuid == device_uuid && current_driver_uuid == driver_uuid
592
} else {
593
false
594
}
595
});
596
let physical_device = if let Some(physical_device) = physical_device {
597
physical_device
598
} else {
599
bail!("Failed to find the target physical device.");
600
};
601
{
602
let properties = physical_device.properties();
603
info!(
604
"The post worker chooses the device: name: {}, vendor_id: {}",
605
properties.device_name, properties.vendor_id
606
);
607
}
608
let api_version = physical_device.api_version();
609
if api_version < Version::V1_1 {
610
bail!(
611
"The physical device Vulkan version is too low: {:#}",
612
api_version
613
);
614
}
615
ensure!(
616
physical_device.supported_features().timeline_semaphore,
617
"The physical device doesn't support timeline semaphore."
618
);
619
620
let surface = Arc::clone(&window)
621
.create_vulkan_surface(Arc::clone(&instance))
622
.context("Failed to create the surface.")?;
623
624
let queue_family_properties = physical_device.queue_family_properties();
625
let queue_family_indices = (0u32..queue_family_properties
626
.len()
627
.try_into()
628
.expect("queue family index too large"))
629
.collect::<Vec<_>>();
630
// Find the present queue.
631
let mut present_queue_family_index = None;
632
for queue_family_index in queue_family_indices.iter().copied() {
633
let supported = physical_device
634
.surface_support(queue_family_index, surface.borrow())
635
.with_context(|| {
636
format!("query if queue family index {queue_family_index} supports the present")
637
})?;
638
if supported {
639
present_queue_family_index = Some(queue_family_index);
640
break;
641
}
642
}
643
let present_queue_family_index = match present_queue_family_index {
644
Some(queue_index) => queue_index,
645
None => bail!("No queue supports presentation."),
646
};
647
648
// Find the graphics queue.
649
let graphics_queue_family_index = if queue_family_properties
650
[usize::try_from(present_queue_family_index).expect("queue family index too large")]
651
.queue_flags
652
.graphics
653
{
654
Some(present_queue_family_index)
655
} else {
656
queue_family_indices
657
.iter()
658
.copied()
659
.find(|queue_family_index| {
660
queue_family_properties[usize::try_from(*queue_family_index)
661
.expect("queue family index too large")]
662
.queue_flags
663
.graphics
664
})
665
};
666
let graphics_queue_family_index = match graphics_queue_family_index {
667
Some(queue_index) => queue_index,
668
None => bail!("No queue supports graphics"),
669
};
670
671
// Create VkDevice.
672
let queue_create_infos =
673
BTreeSet::from([present_queue_family_index, graphics_queue_family_index])
674
.iter()
675
.copied()
676
.map(|queue_family_index| QueueCreateInfo {
677
queue_family_index,
678
..Default::default()
679
})
680
.collect();
681
let (device, queues) = Device::new(
682
Arc::clone(&physical_device),
683
DeviceCreateInfo {
684
enabled_extensions: DeviceExtensions {
685
khr_external_fence: true,
686
khr_external_fence_win32: true,
687
khr_external_semaphore: true,
688
khr_external_semaphore_win32: true,
689
khr_external_memory: true,
690
khr_external_memory_win32: true,
691
khr_swapchain: true,
692
khr_timeline_semaphore: true,
693
..DeviceExtensions::empty()
694
},
695
enabled_features: Features {
696
timeline_semaphore: true,
697
..Features::empty()
698
},
699
queue_create_infos,
700
..Default::default()
701
},
702
)
703
.context("create VkDevice")?;
704
705
// Create the swapchain.
706
let (swapchain, swapchain_images) = Swapchain::new(
707
Arc::clone(&device),
708
Arc::clone(&surface),
709
create_swapchain_create_info(
710
physical_device.borrow(),
711
surface.borrow(),
712
&window
713
.get_inner_size()
714
.context("get the window size to create the swapchain")?,
715
)
716
.context("create the swapchain create info")?,
717
)
718
.context("create Vulkan swapchain")?;
719
let queues = queues.collect::<Vec<_>>();
720
let graphics_queue = queues
721
.iter()
722
.find(|queue| queue.queue_family_index() == graphics_queue_family_index)
723
.cloned()
724
.expect("Graphics queue not found.");
725
let present_queue = queues
726
.iter()
727
.find(|queue| queue.queue_family_index() == present_queue_family_index)
728
.cloned()
729
.expect("Present queue not found.");
730
731
let ash_device =
732
// SAFETY: Safe because instance_fn comes from an instance we created and know is valid and
733
// we also created device which is valid.
734
unsafe { ash::Device::load(&instance.fns().v1_0, device.internal_object()) };
735
736
// TODO (b/327677792): StandardCommandPool must be put inside an Arc, it's intended to work
737
// that way. We need move to a newer version of vulkano to fix this.
738
#[allow(clippy::arc_with_non_send_sync)]
739
let command_pool = Arc::new(
740
StandardCommandPool::new(Arc::clone(&device), graphics_queue_family_index)
741
.context("create command pool")?,
742
);
743
let command_buffer_allocs = command_pool
744
.allocate(
745
CommandBufferLevel::Primary,
746
(swapchain_images.len() + 1)
747
.try_into()
748
.expect("too many swapchain images"),
749
)
750
.context("allocate command buffers")?
751
.map(CommandPoolBuilderAlloc::into_alloc);
752
let post_resources: Vec<PostResource> = command_buffer_allocs
753
.map(|command_buffer| {
754
PostResource::new(&device, command_buffer)
755
.context("create resources for posting one frame")
756
})
757
.collect::<Result<_>>()?;
758
759
Ok(Self {
760
physical_device,
761
ash_device: Arc::new(ash_device),
762
device,
763
window,
764
swapchain,
765
swapchain_images,
766
graphics_queue,
767
present_queue,
768
post_resources,
769
_command_pool: command_pool,
770
rng: rand::rng(),
771
_marker: Default::default(),
772
})
773
}
774
775
fn drain_queues(&self) -> Result<()> {
776
self.graphics_queue
777
.wait()
778
.context("wait for the graphics queue to become idle")?;
779
self.present_queue
780
.wait()
781
.context("wait for the present queue to become idle")?;
782
Ok(())
783
}
784
785
pub fn recreate_swapchain(&mut self) -> Result<()> {
786
let swapchain_create_info = create_swapchain_create_info(
787
self.physical_device.borrow(),
788
self.swapchain.surface(),
789
&self
790
.window
791
.get_inner_size()
792
.context("get the window size when recreating the swapchain")?,
793
)
794
.context("create swapchain create info")?;
795
self.drain_queues()
796
.context("wait for queues to become idel")?;
797
(self.swapchain, self.swapchain_images) = self
798
.swapchain
799
.recreate(swapchain_create_info)
800
.context("recreate swapchain")?;
801
Ok(())
802
}
803
804
/// Acquire a swapchain image and call the supplied function with the swapchain's PostResource,
805
/// SwapchainImage, and the generic `userdata`. Userdata is likely to be the source image(s)
806
/// that are being presented to the swapchain image and the userdata is returned to the caller
807
/// for reuse. After `f` is called, this function will submit a present call for that swapchain
808
/// image.
809
///
810
/// Note: It is the responsibility of `f` to make sure that the swapchain image has been
811
/// transferred to the PresentSrc layout.
812
fn with_swapchain_image<T>(
813
&mut self,
814
mut f: impl FnMut(&mut PostResource, Arc<SwapchainImage>, T) -> T,
815
mut userdata: T,
816
) -> Result<T> {
817
let mut attempts = 0;
818
const MAX_ATTEMPTS: i32 = 5;
819
loop {
820
if attempts > 0 {
821
info!("Recreate the swapchain: attempt {}", attempts);
822
self.recreate_swapchain()
823
.context("recreate the swapchain")?;
824
}
825
if attempts > MAX_ATTEMPTS {
826
bail!(
827
"The swapchain is always suboptimal or out of date with {} attempts.",
828
attempts
829
);
830
}
831
attempts += 1;
832
833
let post_resource_index = (0..self.post_resources.len()).find(|i| {
834
self.post_resources[*i]
835
.command_complete_fence
836
.is_signaled()
837
.unwrap_or_else(|e| panic!("Failed to retrieve the fence status: {e}"))
838
});
839
let post_resource = match post_resource_index {
840
Some(i) => &mut self.post_resources[i],
841
None => {
842
let post_resource = self
843
.post_resources
844
.iter_mut()
845
.choose(&mut self.rng)
846
.expect("post resources shouldn't be empty");
847
post_resource
848
.command_complete_fence
849
.wait(Some(Duration::from_secs(5)))
850
.unwrap_or_else(|e| {
851
panic!("Failed to wait for one recource to be available: {e:?}.")
852
});
853
post_resource
854
}
855
};
856
857
// SAFETY: Safe because self.swapchain and
858
// post_resource.acquire_swapchain_image_semaphore contain no pointers and outlive this
859
// call.
860
let (index, present_wait_semaphore) = match unsafe {
861
acquire_next_image_raw(
862
self.swapchain.borrow(),
863
None,
864
Some(&post_resource.acquire_swapchain_image_semaphore),
865
None,
866
)
867
} {
868
Ok(AcquiredImage {
869
suboptimal: true, ..
870
})
871
| Err(AcquireError::OutOfDate) => continue,
872
Ok(AcquiredImage {
873
id: index,
874
suboptimal: false,
875
}) => {
876
userdata = f(
877
post_resource,
878
Arc::clone(&self.swapchain_images[index]),
879
userdata,
880
);
881
(index, &post_resource.command_complete_semaphore)
882
}
883
Err(err) => return Err(err).context("acquire next image"),
884
};
885
let present_info = PresentInfo {
886
index,
887
..PresentInfo::swapchain(Arc::clone(&self.swapchain))
888
};
889
let mut submit_present_builder = SubmitPresentBuilder::new();
890
// SAFETY: Safe because before destroying the swapchain, we always wait for the queues
891
// to be idle. Hence the swapchain must outlive this presenting.
892
unsafe { submit_present_builder.add_swapchain(&present_info) };
893
// SAFETY: Safe because of the safety requirement of this function.
894
unsafe { submit_present_builder.add_wait_semaphore(present_wait_semaphore) }
895
match submit_present_builder.submit(self.present_queue.borrow()) {
896
Ok(()) => return Ok(userdata),
897
Err(SubmitPresentError::OutOfDate) => continue,
898
Err(err) => return Err(err).context("submit the present request"),
899
}
900
}
901
}
902
903
pub fn post(
904
&mut self,
905
image: ExternalImage,
906
last_layout_transition: (ImageLayout, ImageLayout),
907
acquire_timepoint: Option<Timepoint>,
908
release_timepoint: Timepoint,
909
) -> ExternalImage {
910
let device = Arc::clone(&self.device);
911
let ash_device = Arc::clone(&self.ash_device);
912
let graphics_queue = Arc::clone(&self.graphics_queue);
913
let present_queue = Arc::clone(&self.present_queue);
914
self.with_swapchain_image(
915
|post_resource, swapchain_image, image| {
916
let out_image = post_resource.record_and_submit_post_command(
917
device.borrow(),
918
ash_device.borrow(),
919
image,
920
last_layout_transition,
921
swapchain_image,
922
graphics_queue.borrow(),
923
present_queue.borrow(),
924
acquire_timepoint.as_ref(),
925
&release_timepoint,
926
);
927
out_image
928
},
929
image,
930
)
931
.unwrap_or_else(|err| panic!("Failed when posting a frame: {err:?}"))
932
}
933
934
pub fn device(&self) -> Arc<Device> {
935
Arc::clone(&self.device)
936
}
937
}
938
939
impl Drop for VulkanPostWorker {
940
fn drop(&mut self) {
941
if let Err(err) = self.drain_queues() {
942
error!(
943
"Failed to wait for queues to become idle when destroying the post worker: {:?}",
944
err
945
);
946
}
947
}
948
}
949
950