wgpu_hal/vulkan/mod.rs
1/*!
2# Vulkan API internals.
3
4## Stack memory
5
6Ash expects slices, which we don't generally have available.
7We cope with this requirement by the combination of the following ways:
8 - temporarily allocating `Vec` on heap, where overhead is permitted
9 - growing temporary local storage
10
11## Framebuffers and Render passes
12
13Render passes are cached on the device and kept forever.
14
15Framebuffers are also cached on the device, but they are removed when
16any of the image views (they have) gets removed.
17If Vulkan supports image-less framebuffers,
18then the actual views are excluded from the framebuffer key.
19
20## Fences
21
22If timeline semaphores are available, they are used 1:1 with wgpu-hal fences.
23Otherwise, we manage a pool of `VkFence` objects behind each `hal::Fence`.
24
25!*/
26
27mod adapter;
28mod command;
29pub mod conv;
30mod descriptor;
31mod device;
32mod drm;
33mod instance;
34mod sampler;
35mod semaphore_list;
36mod swapchain;
37
38pub use adapter::PhysicalDeviceFeatures;
39
40use alloc::{boxed::Box, ffi::CString, sync::Arc, vec::Vec};
41use core::{borrow::Borrow, ffi::CStr, fmt, marker::PhantomData, mem, num::NonZeroU32};
42
43use arrayvec::ArrayVec;
44use ash::{ext, khr, vk};
45use bytemuck::{Pod, Zeroable};
46use hashbrown::HashSet;
47use parking_lot::{Mutex, RwLock};
48
49use naga::FastHashMap;
50use wgt::InternalCounter;
51
52use semaphore_list::SemaphoreList;
53
54use crate::vulkan::semaphore_list::{SemaphoreListMode, SemaphoreType};
55
56const MAX_TOTAL_ATTACHMENTS: usize = crate::MAX_COLOR_ATTACHMENTS * 2 + 1;
57
58#[derive(Clone, Debug)]
59pub struct Api;
60
61impl crate::Api for Api {
62 const VARIANT: wgt::Backend = wgt::Backend::Vulkan;
63
64 type Instance = Instance;
65 type Surface = Surface;
66 type Adapter = Adapter;
67 type Device = Device;
68
69 type Queue = Queue;
70 type CommandEncoder = CommandEncoder;
71 type CommandBuffer = CommandBuffer;
72
73 type Buffer = Buffer;
74 type Texture = Texture;
75 type SurfaceTexture = SurfaceTexture;
76 type TextureView = TextureView;
77 type Sampler = Sampler;
78 type QuerySet = QuerySet;
79 type Fence = Fence;
80 type AccelerationStructure = AccelerationStructure;
81 type PipelineCache = PipelineCache;
82
83 type BindGroupLayout = BindGroupLayout;
84 type BindGroup = BindGroup;
85 type PipelineLayout = PipelineLayout;
86 type ShaderModule = ShaderModule;
87 type RenderPipeline = RenderPipeline;
88 type ComputePipeline = ComputePipeline;
89 type RayTracingPipeline = RayTracingPipeline;
90}
91
92crate::impl_dyn_resource!(
93 Adapter,
94 AccelerationStructure,
95 BindGroup,
96 BindGroupLayout,
97 Buffer,
98 CommandBuffer,
99 CommandEncoder,
100 ComputePipeline,
101 Device,
102 Fence,
103 Instance,
104 PipelineCache,
105 PipelineLayout,
106 QuerySet,
107 Queue,
108 RenderPipeline,
109 RayTracingPipeline,
110 Sampler,
111 ShaderModule,
112 Surface,
113 SurfaceTexture,
114 Texture,
115 TextureView
116);
117
118struct DebugUtils {
119 extension: ext::debug_utils::Instance,
120 messenger: vk::DebugUtilsMessengerEXT,
121
122 /// Owning pointer to the debug messenger callback user data.
123 ///
124 /// `InstanceShared::drop` destroys the debug messenger before
125 /// dropping this, so the callback should never receive a dangling
126 /// user data pointer.
127 #[allow(dead_code)]
128 callback_data: Box<DebugUtilsMessengerUserData>,
129}
130
131#[derive(Debug)]
132pub struct DebugUtilsCreateInfo {
133 severity: vk::DebugUtilsMessageSeverityFlagsEXT,
134 message_type: vk::DebugUtilsMessageTypeFlagsEXT,
135 callback_data: Box<DebugUtilsMessengerUserData>,
136}
137
138#[derive(Debug)]
139/// The properties related to the validation layer needed for the
140/// DebugUtilsMessenger for their workarounds
141struct ValidationLayerProperties {
142 /// Validation layer description, from `vk::LayerProperties`.
143 layer_description: CString,
144
145 /// Validation layer specification version, from `vk::LayerProperties`.
146 layer_spec_version: u32,
147}
148
149/// User data needed by `instance::debug_utils_messenger_callback`.
150///
151/// When we create the [`vk::DebugUtilsMessengerEXT`], the `pUserData`
152/// pointer refers to one of these values.
153#[derive(Debug)]
154pub struct DebugUtilsMessengerUserData {
155 /// The properties related to the validation layer, if present
156 validation_layer_properties: Option<ValidationLayerProperties>,
157
158 /// If the OBS layer is present. OBS never increments the version of their layer,
159 /// so there's no reason to have the version.
160 has_obs_layer: bool,
161}
162
163pub struct InstanceShared {
164 raw: ash::Instance,
165 extensions: Vec<&'static CStr>,
166 flags: wgt::InstanceFlags,
167 memory_budget_thresholds: wgt::MemoryBudgetThresholds,
168 debug_utils: Option<DebugUtils>,
169 get_physical_device_properties: Option<khr::get_physical_device_properties2::Instance>,
170 entry: ash::Entry,
171 has_nv_optimus: bool,
172 android_sdk_version: u32,
173 /// The instance API version.
174 ///
175 /// Which is the version of Vulkan supported for instance-level functionality.
176 ///
177 /// It is associated with a `VkInstance` and its children,
178 /// except for a `VkPhysicalDevice` and its children.
179 instance_api_version: u32,
180
181 // The `drop_guard` field must be the last field of this struct so it is dropped last.
182 // Do not add new fields after it.
183 drop_guard: Option<crate::DropGuard>,
184}
185
186impl fmt::Debug for InstanceShared {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 let Self {
189 raw: _,
190 extensions,
191 flags,
192 memory_budget_thresholds,
193 debug_utils: _,
194 get_physical_device_properties: _,
195 entry: _,
196 has_nv_optimus,
197 android_sdk_version,
198 instance_api_version,
199 drop_guard: _,
200 } = self;
201 f.debug_struct("InstanceShared")
202 .field("extensions", extensions)
203 .field("flags", flags)
204 .field("memory_budget_thresholds", memory_budget_thresholds)
205 .field("has_nv_optimus", has_nv_optimus)
206 .field("android_sdk_version", android_sdk_version)
207 .field("instance_api_version", instance_api_version)
208 .finish_non_exhaustive()
209 }
210}
211
212#[derive(Debug)]
213pub struct Instance {
214 shared: Arc<InstanceShared>,
215}
216
217#[expect(missing_debug_implementations, reason = "TODO?")]
218pub struct Surface {
219 swapchain: RwLock<Option<Box<dyn swapchain::Swapchain>>>,
220 inner: Box<dyn swapchain::Surface>,
221}
222
223impl Surface {
224 /// Returns the raw Vulkan surface handle.
225 ///
226 /// Returns `None` if the surface is a DXGI surface.
227 pub unsafe fn raw_native_handle(&self) -> Option<vk::SurfaceKHR> {
228 Some(
229 self.inner
230 .as_any()
231 .downcast_ref::<swapchain::NativeSurface>()?
232 .as_raw(),
233 )
234 }
235
236 /// Get the raw Vulkan swapchain associated with this surface.
237 ///
238 /// Returns [`None`] if the surface is not configured or if the swapchain
239 /// is a DXGI swapchain.
240 pub fn raw_native_swapchain(&self) -> Option<vk::SwapchainKHR> {
241 let read = self.swapchain.read();
242 Some(
243 read.as_ref()?
244 .as_any()
245 .downcast_ref::<swapchain::NativeSwapchain>()?
246 .as_raw(),
247 )
248 }
249
250 /// Set the present timing information which will be used for the next [presentation](crate::Queue::present()) of this surface,
251 /// using [VK_GOOGLE_display_timing].
252 ///
253 /// This can be used to give an id to presentations, for future use of [`vk::PastPresentationTimingGOOGLE`].
254 /// Note that `wgpu-hal` does *not* provide a way to use that API - you should manually access this through [`ash`].
255 ///
256 /// This can also be used to add a "not before" timestamp to the presentation.
257 ///
258 /// The exact semantics of the fields are also documented in the [specification](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentTimeGOOGLE.html) for the extension.
259 ///
260 /// # Panics
261 ///
262 /// - If the surface hasn't been configured.
263 /// - If the surface has been configured for a DXGI swapchain.
264 /// - If the device doesn't [support present timing](wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING).
265 ///
266 /// [VK_GOOGLE_display_timing]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
267 #[track_caller]
268 pub fn set_next_present_time(&self, present_timing: vk::PresentTimeGOOGLE) {
269 let mut swapchain = self.swapchain.write();
270 swapchain
271 .as_mut()
272 .expect("Surface should have been configured")
273 .as_any_mut()
274 .downcast_mut::<swapchain::NativeSwapchain>()
275 .expect("Surface should have a native Vulkan swapchain")
276 .set_next_present_time(present_timing);
277 }
278}
279
280#[derive(Debug)]
281pub struct SurfaceTexture {
282 index: u32,
283 texture: Texture,
284 metadata: Box<dyn swapchain::SurfaceTextureMetadata>,
285}
286
287impl crate::DynSurfaceTexture for SurfaceTexture {}
288
289impl Borrow<Texture> for SurfaceTexture {
290 fn borrow(&self) -> &Texture {
291 &self.texture
292 }
293}
294
295impl Borrow<dyn crate::DynTexture> for SurfaceTexture {
296 fn borrow(&self) -> &dyn crate::DynTexture {
297 &self.texture
298 }
299}
300
301#[derive(Debug)]
302pub struct Adapter {
303 raw: vk::PhysicalDevice,
304 instance: Arc<InstanceShared>,
305 //queue_families: Vec<vk::QueueFamilyProperties>,
306 known_memory_flags: vk::MemoryPropertyFlags,
307 phd_capabilities: adapter::PhysicalDeviceProperties,
308 phd_features: PhysicalDeviceFeatures,
309 downlevel_flags: wgt::DownlevelFlags,
310 private_caps: PrivateCapabilities,
311 workarounds: Workarounds,
312}
313
314// TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`.
315enum ExtensionFn<T> {
316 /// The loaded function pointer struct for an extension.
317 Extension(T),
318 /// The extension was promoted to a core version of Vulkan and the functions on `ash`'s `DeviceV1_x` traits should be used.
319 Promoted,
320}
321
322struct DeviceExtensionFunctions {
323 debug_utils: Option<ext::debug_utils::Device>,
324 draw_indirect_count: Option<khr::draw_indirect_count::Device>,
325 timeline_semaphore: Option<ExtensionFn<khr::timeline_semaphore::Device>>,
326 ray_tracing: Option<RayTracingDeviceExtensionFunctions>,
327 ray_tracing_pipelines: Option<khr::ray_tracing_pipeline::Device>,
328 mesh_shading: Option<ext::mesh_shader::Device>,
329 #[cfg_attr(not(unix), allow(dead_code))]
330 external_memory_fd: Option<khr::external_memory_fd::Device>,
331}
332
333struct RayTracingDeviceExtensionFunctions {
334 acceleration_structure: khr::acceleration_structure::Device,
335 buffer_device_address: khr::buffer_device_address::Device,
336}
337
338/// Set of internal capabilities, which don't show up in the exposed
339/// device geometry, but affect the code paths taken internally.
340#[derive(Clone, Debug)]
341struct PrivateCapabilities {
342 image_view_usage: bool,
343 timeline_semaphores: bool,
344 texture_d24: bool,
345 texture_d24_s8: bool,
346 texture_s8: bool,
347 /// Ability to present contents to any screen. Only needed to work around broken platform configurations.
348 can_present: bool,
349 non_coherent_map_mask: wgt::BufferAddress,
350 multi_draw_indirect: bool,
351 max_draw_indirect_count: u32,
352
353 /// True if this adapter advertises the [`robustBufferAccess`][vrba] feature.
354 ///
355 /// Note that Vulkan's `robustBufferAccess` is not sufficient to implement
356 /// `wgpu_hal`'s guarantee that shaders will not access buffer contents via
357 /// a given bindgroup binding outside that binding's [accessible
358 /// region][ar]. Enabling `robustBufferAccess` does ensure that
359 /// out-of-bounds reads and writes are not undefined behavior (that's good),
360 /// but still permits out-of-bounds reads to return data from anywhere
361 /// within the buffer, not just the accessible region.
362 ///
363 /// [ar]: ../struct.BufferBinding.html#accessible-region
364 /// [vrba]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#features-robustBufferAccess
365 robust_buffer_access: bool,
366
367 robust_image_access: bool,
368
369 /// True if this adapter supports the [`VK_EXT_robustness2`] extension's
370 /// [`robustBufferAccess2`] feature.
371 ///
372 /// This is sufficient to implement `wgpu_hal`'s [required bounds-checking][ar] of
373 /// shader accesses to buffer contents. If this feature is not available,
374 /// this backend must have Naga inject bounds checks in the generated
375 /// SPIR-V.
376 ///
377 /// [`VK_EXT_robustness2`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_robustness2.html
378 /// [`robustBufferAccess2`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceRobustness2FeaturesEXT.html#features-robustBufferAccess2
379 /// [ar]: ../struct.BufferBinding.html#accessible-region
380 robust_buffer_access2: bool,
381
382 robust_image_access2: bool,
383 zero_initialize_workgroup_memory: bool,
384 image_format_list: bool,
385 maximum_samplers: u32,
386
387 /// True if this adapter supports the [`VK_KHR_shader_integer_dot_product`] extension
388 /// (promoted to Vulkan 1.3).
389 ///
390 /// This is used to generate optimized code for WGSL's `dot4{I, U}8Packed`.
391 ///
392 /// [`VK_KHR_shader_integer_dot_product`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_integer_dot_product.html
393 shader_integer_dot_product: bool,
394
395 /// True if this adapter supports 8-bit integers provided by the
396 /// [`VK_KHR_shader_float16_int8`] extension (promoted to Vulkan 1.2).
397 ///
398 /// Allows shaders to declare the "Int8" capability. Note, however, that this
399 /// feature alone allows the use of 8-bit integers "only in the `Private`,
400 /// `Workgroup` (for non-Block variables), and `Function` storage classes"
401 /// ([see spec]). To use 8-bit integers in the interface storage classes (e.g.,
402 /// `StorageBuffer`), you also need to enable the corresponding feature in
403 /// `VkPhysicalDevice8BitStorageFeatures` and declare the corresponding SPIR-V
404 /// capability (e.g., `StorageBuffer8BitAccess`).
405 ///
406 /// [`VK_KHR_shader_float16_int8`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_shader_float16_int8.html
407 /// [see spec]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceShaderFloat16Int8Features.html#extension-features-shaderInt8
408 shader_int8: bool,
409
410 /// This is done to panic before undefined behavior, and is imperfect.
411 /// Basically, to allow implementations to emulate mv using instancing, if you
412 /// want to draw `n` instances to VR, you must draw `2n` instances, but you
413 /// can never draw more than `u32::MAX` instances. Therefore, when drawing
414 /// multiview on some vulkan implementations, it might restrict the instance
415 /// count, which isn't usually a thing in webgpu. We don't expose this limit
416 /// because its strange, i.e. only occurs on certain vulkan implementations
417 /// if you are drawing more than 128 million instances. We still want to avoid
418 /// undefined behavior in this situation, so we panic if the limit is violated.
419 multiview_instance_index_limit: u32,
420
421 /// BufferUsages::ACCELERATION_STRUCTURE_SCRATCH allows usage as a scratch buffer.
422 /// Vulkan has no way to specify this as a usage, and it maps to other usages, but
423 /// these usages do not have as high of an alignment requirement using the buffer as
424 /// a scratch buffer when building acceleration structures.
425 scratch_buffer_alignment: u32,
426
427 /// `get_raytracing_pipeline_group_data` requires both a group count and a data size.
428 /// The data size parameter is just this * the group count, so we store this to not
429 /// require an unnecessary parameter.
430 ray_tracing_pipeline_group_data_size: u32,
431}
432
433bitflags::bitflags!(
434 /// Workaround flags.
435 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
436 pub struct Workarounds: u32 {
437 /// Only generate SPIR-V for one entry point at a time.
438 const SEPARATE_ENTRY_POINTS = 0x1;
439 /// Qualcomm OOMs when there are zero color attachments but a non-null pointer
440 /// to a subpass resolve attachment array. This nulls out that pointer in that case.
441 const EMPTY_RESOLVE_ATTACHMENT_LISTS = 0x2;
442 /// If the following code returns false, then nvidia will end up filling the wrong range.
443 ///
444 /// ```skip
445 /// fn nvidia_succeeds() -> bool {
446 /// # let (copy_length, start_offset) = (0, 0);
447 /// if copy_length >= 4096 {
448 /// if start_offset % 16 != 0 {
449 /// if copy_length == 4096 {
450 /// return true;
451 /// }
452 /// if copy_length % 16 == 0 {
453 /// return false;
454 /// }
455 /// }
456 /// }
457 /// true
458 /// }
459 /// ```
460 ///
461 /// As such, we need to make sure all calls to vkCmdFillBuffer are aligned to 16 bytes
462 /// if they cover a range of 4096 bytes or more.
463 const FORCE_FILL_BUFFER_WITH_SIZE_GREATER_4096_ALIGNED_OFFSET_16 = 0x4;
464 }
465);
466
467#[derive(Clone, Debug, Eq, Hash, PartialEq)]
468struct AttachmentKey {
469 format: vk::Format,
470 layout: vk::ImageLayout,
471 ops: crate::AttachmentOps,
472}
473
474impl AttachmentKey {
475 /// Returns an attachment key for a compatible attachment.
476 fn compatible(format: vk::Format, layout: vk::ImageLayout) -> Self {
477 Self {
478 format,
479 layout,
480 ops: crate::AttachmentOps::all(),
481 }
482 }
483}
484
485#[derive(Clone, Eq, Hash, PartialEq)]
486struct ColorAttachmentKey {
487 base: AttachmentKey,
488 resolve: Option<AttachmentKey>,
489}
490
491#[derive(Clone, Eq, Hash, PartialEq)]
492struct DepthStencilAttachmentKey {
493 base: AttachmentKey,
494 stencil_ops: crate::AttachmentOps,
495}
496
497#[derive(Clone, Eq, Default, Hash, PartialEq)]
498struct RenderPassKey {
499 colors: ArrayVec<Option<ColorAttachmentKey>, { crate::MAX_COLOR_ATTACHMENTS }>,
500 depth_stencil: Option<DepthStencilAttachmentKey>,
501 sample_count: u32,
502 multiview_mask: Option<NonZeroU32>,
503}
504
505struct DeviceShared {
506 raw: ash::Device,
507 family_index: u32,
508 queue_index: u32,
509 raw_queue: vk::Queue,
510 instance: Arc<InstanceShared>,
511 physical_device: vk::PhysicalDevice,
512 enabled_extensions: Vec<&'static CStr>,
513 extension_fns: DeviceExtensionFunctions,
514 vendor_id: u32,
515 pipeline_cache_validation_key: [u8; 16],
516 timestamp_period: f32,
517 private_caps: PrivateCapabilities,
518 workarounds: Workarounds,
519 features: wgt::Features,
520 render_passes: Mutex<FastHashMap<RenderPassKey, vk::RenderPass>>,
521 sampler_cache: Mutex<sampler::SamplerCache>,
522 memory_allocations_counter: InternalCounter,
523
524 /// Because we have cached framebuffers which are not deleted from until
525 /// the device is destroyed, if the implementation of vulkan re-uses handles
526 /// we need some way to differentiate between the old handle and the new handle.
527 /// This factory allows us to have a dedicated identity value for each texture.
528 texture_identity_factory: ResourceIdentityFactory<vk::Image>,
529 /// As above, for texture views.
530 texture_view_identity_factory: ResourceIdentityFactory<vk::ImageView>,
531
532 empty_descriptor_set_layout: vk::DescriptorSetLayout,
533
534 /// Whether a bind group layout lets a stage other than fragment or
535 /// compute read a texture. Until one does, a texture read by shaders is
536 /// waited for only in those two stages, so a pass that samples what the
537 /// previous pass drew no longer holds its vertex work behind that pass's
538 /// fragment work. Set once, never cleared. Command buffers begun after
539 /// it is set wait in upstream's stages, and a [`CatchUpBarrier`] covers
540 /// the textures earlier buffers left waiting in fewer.
541 texture_reads_before_fragment: core::sync::atomic::AtomicBool,
542
543 // The `drop_guard` field must be the last field of this struct so it is dropped last.
544 // Do not add new fields after it.
545 drop_guard: Option<crate::DropGuard>,
546}
547
548impl Drop for DeviceShared {
549 fn drop(&mut self) {
550 for &raw in self.render_passes.lock().values() {
551 unsafe { self.raw.destroy_render_pass(raw, None) };
552 }
553 unsafe {
554 self.raw
555 .destroy_descriptor_set_layout(self.empty_descriptor_set_layout, None)
556 };
557 if self.drop_guard.is_none() {
558 unsafe { self.raw.destroy_device(None) };
559 }
560 }
561}
562
563#[expect(
564 missing_debug_implementations,
565 reason = "needs work to not be disastrously verbose"
566)]
567pub struct Device {
568 mem_allocator: Mutex<gpu_allocator::vulkan::Allocator>,
569 desc_allocator: Mutex<descriptor::DescriptorAllocator>,
570 valid_ash_memory_types: u32,
571 naga_options: naga::back::spv::Options<'static>,
572 #[cfg(feature = "renderdoc")]
573 render_doc: crate::auxil::renderdoc::RenderDoc,
574 counters: Arc<wgt::HalCounters>,
575 // Struct members are dropped from first to last, put the Device last to ensure that
576 // all resources that depends on it are destroyed before it like the mem_allocator
577 shared: Arc<DeviceShared>,
578}
579
580impl Drop for Device {
581 fn drop(&mut self) {}
582}
583
584/// Semaphores for forcing queue submissions to run in order.
585///
586/// The [`wgpu_hal::Queue`] trait promises that if two calls to [`submit`] are
587/// ordered, then the first submission will finish on the GPU before the second
588/// submission begins. To get this behavior on Vulkan we need to pass semaphores
589/// to [`vkQueueSubmit`] for the commands to wait on before beginning execution,
590/// and to signal when their execution is done.
591///
592/// Normally this can be done with a single semaphore, waited on and then
593/// signalled for each submission. At any given time there's exactly one
594/// submission that would signal the semaphore, and exactly one waiting on it,
595/// as Vulkan requires.
596///
597/// However, as of Oct 2021, bug [#5508] in the Mesa ANV drivers caused them to
598/// hang if we use a single semaphore. The workaround is to alternate between
599/// two semaphores. The bug has been fixed in Mesa, but we should probably keep
600/// the workaround until, say, Oct 2026.
601///
602/// [`wgpu_hal::Queue`]: crate::Queue
603/// [`submit`]: crate::Queue::submit
604/// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
605/// [#5508]: https://gitlab.freedesktop.org/mesa/mesa/-/issues/5508
606#[derive(Clone)]
607struct RelaySemaphores {
608 /// The semaphore the next submission should wait on before beginning
609 /// execution on the GPU. This is `None` for the first submission, which
610 /// should not wait on anything at all.
611 wait: Option<vk::Semaphore>,
612
613 /// The semaphore the next submission should signal when it has finished
614 /// execution on the GPU.
615 signal: vk::Semaphore,
616}
617
618impl RelaySemaphores {
619 fn new(device: &DeviceShared) -> Result<Self, crate::DeviceError> {
620 Ok(Self {
621 wait: None,
622 signal: device.new_binary_semaphore("RelaySemaphores: 1")?,
623 })
624 }
625
626 /// Advances the semaphores, returning the semaphores that should be used for a submission.
627 fn advance(&mut self, device: &DeviceShared) -> Result<Self, crate::DeviceError> {
628 let old = self.clone();
629
630 // Build the state for the next submission.
631 match self.wait {
632 None => {
633 // The `old` values describe the first submission to this queue.
634 // The second submission should wait on `old.signal`, and then
635 // signal a new semaphore which we'll create now.
636 self.wait = Some(old.signal);
637 self.signal = device.new_binary_semaphore("RelaySemaphores: 2")?;
638 }
639 Some(ref mut wait) => {
640 // What this submission signals, the next should wait.
641 mem::swap(wait, &mut self.signal);
642 }
643 };
644
645 Ok(old)
646 }
647
648 /// Destroys the semaphores.
649 unsafe fn destroy(&self, device: &ash::Device) {
650 unsafe {
651 if let Some(wait) = self.wait {
652 device.destroy_semaphore(wait, None);
653 }
654 device.destroy_semaphore(self.signal, None);
655 }
656 }
657}
658
659pub struct Queue {
660 raw: vk::Queue,
661 device: Arc<DeviceShared>,
662 family_index: u32,
663 relay_semaphores: Mutex<RelaySemaphores>,
664 signal_semaphores: Mutex<SemaphoreList>,
665 wait_semaphores: Mutex<SemaphoreList>,
666 catch_up_barrier: Mutex<Option<CatchUpBarrier>>,
667}
668
669impl fmt::Debug for Queue {
670 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671 let Self {
672 raw: _,
673 device: _,
674 family_index,
675 relay_semaphores: _,
676 signal_semaphores: _,
677 wait_semaphores: _,
678 catch_up_barrier: _,
679 } = self;
680 f.debug_struct("Queue")
681 .field("family_index", family_index)
682 .finish_non_exhaustive()
683 }
684}
685
686impl Queue {
687 pub fn as_raw(&self) -> vk::Queue {
688 self.raw
689 }
690}
691
692impl Drop for Queue {
693 fn drop(&mut self) {
694 unsafe { self.relay_semaphores.lock().destroy(&self.device.raw) };
695 if let Some(barrier) = self.catch_up_barrier.get_mut().take() {
696 unsafe { self.device.raw.destroy_command_pool(barrier.pool, None) };
697 }
698 }
699}
700
701/// A reusable command buffer that orders all earlier work and writes before
702/// all later work. A submission runs it where it moves from command buffers
703/// begun before the device widened its texture stages to ones begun after.
704///
705/// The earlier buffers waited for texture writes only in fragment and
706/// compute, and a later buffer may read such a texture in a vertex shader
707/// while the texture stays in a read state, which takes no new barrier.
708/// Separate submissions already run one after another ([`RelaySemaphores`]),
709/// and waits for earlier reads need nothing: a barrier's source stages
710/// include every logically earlier stage.
711struct CatchUpBarrier {
712 pool: vk::CommandPool,
713 raw: vk::CommandBuffer,
714}
715
716impl CatchUpBarrier {
717 fn record(device: &DeviceShared, family_index: u32) -> Result<Self, crate::DeviceError> {
718 let pool_info = vk::CommandPoolCreateInfo::default().queue_family_index(family_index);
719 let pool = unsafe { device.raw.create_command_pool(&pool_info, None) }
720 .map_err(map_host_device_oom_err)?;
721 match unsafe { Self::record_into(device, pool) } {
722 Ok(raw) => Ok(Self { pool, raw }),
723 Err(err) => {
724 unsafe { device.raw.destroy_command_pool(pool, None) };
725 Err(err)
726 }
727 }
728 }
729
730 unsafe fn record_into(
731 device: &DeviceShared,
732 pool: vk::CommandPool,
733 ) -> Result<vk::CommandBuffer, crate::DeviceError> {
734 let allocate_info = vk::CommandBufferAllocateInfo::default()
735 .command_pool(pool)
736 .level(vk::CommandBufferLevel::PRIMARY)
737 .command_buffer_count(1);
738 let raw = unsafe { device.raw.allocate_command_buffers(&allocate_info) }
739 .map_err(map_host_device_oom_err)?
740 .pop()
741 .ok_or(crate::DeviceError::Unexpected)?;
742 let begin_info = vk::CommandBufferBeginInfo::default()
743 .flags(vk::CommandBufferUsageFlags::SIMULTANEOUS_USE);
744 unsafe { device.raw.begin_command_buffer(raw, &begin_info) }
745 .map_err(map_host_device_oom_err)?;
746 let barrier = vk::MemoryBarrier::default()
747 .src_access_mask(vk::AccessFlags::MEMORY_WRITE)
748 .dst_access_mask(vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE);
749 unsafe {
750 device.raw.cmd_pipeline_barrier(
751 raw,
752 vk::PipelineStageFlags::ALL_COMMANDS,
753 vk::PipelineStageFlags::ALL_COMMANDS,
754 vk::DependencyFlags::empty(),
755 &[barrier],
756 &[],
757 &[],
758 )
759 };
760 unsafe { device.raw.end_command_buffer(raw) }.map_err(map_host_device_oom_err)?;
761 Ok(raw)
762 }
763}
764
765impl Queue {
766 /// The Vulkan command buffers of a submission, with the
767 /// [`CatchUpBarrier`] ahead of each widened buffer that follows an
768 /// unwidened one.
769 fn submission(
770 &self,
771 command_buffers: &[&CommandBuffer],
772 ) -> Result<Vec<vk::CommandBuffer>, crate::DeviceError> {
773 let mut raw = Vec::with_capacity(command_buffers.len() + 1);
774 let mut unwidened_before = false;
775 for cmd in command_buffers {
776 if !cmd.texture_stages_widened {
777 unwidened_before = true;
778 } else if unwidened_before {
779 raw.push(self.catch_up_barrier()?);
780 unwidened_before = false;
781 }
782 raw.push(cmd.raw);
783 }
784 Ok(raw)
785 }
786
787 fn catch_up_barrier(&self) -> Result<vk::CommandBuffer, crate::DeviceError> {
788 let mut barrier = self.catch_up_barrier.lock();
789 if let Some(ref barrier) = *barrier {
790 return Ok(barrier.raw);
791 }
792 let recorded = CatchUpBarrier::record(&self.device, self.family_index)?;
793 let raw = recorded.raw;
794 *barrier = Some(recorded);
795 Ok(raw)
796 }
797}
798#[derive(Debug)]
799enum BufferMemoryBacking {
800 Managed(gpu_allocator::vulkan::Allocation),
801 VulkanMemory {
802 memory: vk::DeviceMemory,
803 offset: u64,
804 size: u64,
805 },
806}
807impl BufferMemoryBacking {
808 fn memory(&self) -> vk::DeviceMemory {
809 match self {
810 Self::Managed(m) => unsafe { m.memory() },
811 Self::VulkanMemory { memory, .. } => *memory,
812 }
813 }
814 fn offset(&self) -> u64 {
815 match self {
816 Self::Managed(m) => m.offset(),
817 Self::VulkanMemory { offset, .. } => *offset,
818 }
819 }
820 fn size(&self) -> u64 {
821 match self {
822 Self::Managed(m) => m.size(),
823 Self::VulkanMemory { size, .. } => *size,
824 }
825 }
826}
827/// Describes who owns a [`Buffer`]'s `vk::Buffer` handle and its backing memory,
828/// and therefore what cleanup is required when the buffer is destroyed.
829#[derive(Debug)]
830enum BufferOwnership {
831 /// wgpu-hal owns the `vk::Buffer` and its backing memory. On cleanup the buffer
832 /// handle is destroyed and the memory is released.
833 Managed(Mutex<BufferMemoryBacking>),
834 /// wgpu-hal owns the `vk::Buffer` handle but the backing memory is kept alive
835 /// by the caller. On cleanup only the buffer handle is destroyed.
836 RawHandle,
837 /// Caller owns the `vk::Buffer` and its backing memory. On cleanup the
838 /// [`crate::DropGuard`] runs the caller's cleanup callback and wgpu-hal touches
839 /// neither the handle nor the memory.
840 External(crate::DropGuard),
841}
842
843#[derive(Debug)]
844pub struct Buffer {
845 raw: vk::Buffer,
846
847 // This field must be last, because it may contain a `DropGuard` which needs to be dropped after all other fields.
848 ownership: BufferOwnership,
849}
850impl Buffer {
851 /// # Safety
852 ///
853 /// - `vk_buffer`'s memory must be managed by the caller
854 /// - Externally imported buffers can't be mapped by `wgpu`
855 pub unsafe fn from_raw(vk_buffer: vk::Buffer) -> Self {
856 Self {
857 raw: vk_buffer,
858 ownership: BufferOwnership::RawHandle,
859 }
860 }
861
862 /// # Safety
863 /// - `vk_buffer` must outlive the returned `Buffer`.
864 /// - wgpu-hal will NOT call `vkDestroyBuffer`; the caller remains responsible for the buffer handle's destruction.
865 /// The `drop_callback` runs when the `Buffer` drops and may be used to release caller-side bookkeeping.
866 /// - Externally imported buffers can't be mapped by `wgpu`.
867 pub unsafe fn from_raw_externally_owned(
868 vk_buffer: vk::Buffer,
869 drop_callback: crate::DropCallback,
870 ) -> Self {
871 Self {
872 raw: vk_buffer,
873 ownership: BufferOwnership::External(crate::DropGuard::new(drop_callback)),
874 }
875 }
876
877 /// # Safety
878 /// - We will use this buffer and the buffer's backing memory range as if we have exclusive ownership over it, until the wgpu resource is dropped and the wgpu-hal object is cleaned up
879 /// - Externally imported buffers can't be mapped by `wgpu`
880 /// - `offset` and `size` must be valid with the allocation of `memory`
881 pub unsafe fn from_raw_managed(
882 vk_buffer: vk::Buffer,
883 memory: vk::DeviceMemory,
884 offset: u64,
885 size: u64,
886 ) -> Self {
887 Self {
888 raw: vk_buffer,
889 ownership: BufferOwnership::Managed(Mutex::new(BufferMemoryBacking::VulkanMemory {
890 memory,
891 offset,
892 size,
893 })),
894 }
895 }
896
897 /// # Safety
898 /// - The buffer handle must not be manually destroyed
899 pub unsafe fn raw_handle(&self) -> vk::Buffer {
900 self.raw
901 }
902}
903
904impl crate::DynBuffer for Buffer {}
905
906#[derive(Debug)]
907pub struct AccelerationStructure {
908 raw: vk::AccelerationStructureKHR,
909 buffer: vk::Buffer,
910 allocation: gpu_allocator::vulkan::Allocation,
911 compacted_size_query: Option<vk::QueryPool>,
912}
913
914impl crate::DynAccelerationStructure for AccelerationStructure {}
915
916#[derive(Debug)]
917pub enum TextureMemory {
918 // shared memory in GPU allocator (owned by wgpu-hal)
919 Allocation(gpu_allocator::vulkan::Allocation),
920
921 // dedicated memory (owned by wgpu-hal)
922 Dedicated(vk::DeviceMemory),
923
924 // memory not owned by wgpu
925 External,
926}
927
928#[derive(Debug)]
929pub struct Texture {
930 raw: vk::Image,
931 memory: TextureMemory,
932 format: wgt::TextureFormat,
933 copy_size: crate::CopyExtent,
934 identity: ResourceIdentity<vk::Image>,
935
936 // The `drop_guard` field must be the last field of this struct so it is dropped last.
937 // Do not add new fields after it.
938 drop_guard: Option<crate::DropGuard>,
939}
940
941impl crate::DynTexture for Texture {}
942
943impl Texture {
944 /// # Safety
945 ///
946 /// - The image handle must not be manually destroyed
947 pub unsafe fn raw_handle(&self) -> vk::Image {
948 self.raw
949 }
950
951 /// # Safety
952 ///
953 /// - The caller must not free the `vk::DeviceMemory` or
954 /// `gpu_alloc::MemoryBlock` in the returned `TextureMemory`.
955 pub unsafe fn memory(&self) -> &TextureMemory {
956 &self.memory
957 }
958}
959
960pub struct TextureView {
961 raw_texture: vk::Image,
962 raw: vk::ImageView,
963 _layers: NonZeroU32,
964 format: wgt::TextureFormat,
965 raw_format: vk::Format,
966 base_mip_level: u32,
967 dimension: wgt::TextureViewDimension,
968 texture_identity: ResourceIdentity<vk::Image>,
969 view_identity: ResourceIdentity<vk::ImageView>,
970}
971
972// Written out to leave `raw_format` alone: every hal resource is formatted
973// through its `dyn` vtable, which keeps ash's large `vk::Format` table in
974// every binary for a value `format` already names.
975impl fmt::Debug for TextureView {
976 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
977 f.debug_struct("TextureView")
978 .field("raw", &self.raw)
979 .field("format", &self.format)
980 .field("dimension", &self.dimension)
981 .field("base_mip_level", &self.base_mip_level)
982 .finish_non_exhaustive()
983 }
984}
985
986impl crate::DynTextureView for TextureView {}
987
988impl TextureView {
989 /// # Safety
990 ///
991 /// - The image view handle must not be manually destroyed
992 pub unsafe fn raw_handle(&self) -> vk::ImageView {
993 self.raw
994 }
995
996 /// Returns the raw texture view, along with its identity.
997 fn identified_raw_view(&self) -> IdentifiedTextureView {
998 IdentifiedTextureView {
999 raw: self.raw,
1000 identity: self.view_identity,
1001 }
1002 }
1003}
1004
1005pub struct Sampler {
1006 raw: vk::Sampler,
1007 create_info: vk::SamplerCreateInfo<'static>,
1008}
1009
1010// Written out to leave `create_info` alone: formatting it keeps ash's
1011// `vk::StructureType` table, about 36 KB of code, in every binary.
1012impl fmt::Debug for Sampler {
1013 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1014 f.debug_struct("Sampler")
1015 .field("raw", &self.raw)
1016 .finish_non_exhaustive()
1017 }
1018}
1019
1020impl crate::DynSampler for Sampler {}
1021
1022/// Information about a binding within a specific BindGroupLayout / BindGroup.
1023/// This will be used to construct a [`naga::back::spv::BindingInfo`], where
1024/// the descriptor set value will be taken from the index of the group.
1025#[derive(Copy, Clone, Debug)]
1026struct BindingInfo {
1027 binding: u32,
1028 binding_array_size: Option<NonZeroU32>,
1029}
1030
1031#[derive(Debug)]
1032pub struct BindGroupLayout {
1033 raw: vk::DescriptorSetLayout,
1034 desc_count: descriptor::DescriptorCounts,
1035 /// Sorted list of entries.
1036 entries: Box<[wgt::BindGroupLayoutEntry]>,
1037 /// Map of original binding index to remapped binding index and optional
1038 /// array size.
1039 binding_map: Vec<(u32, BindingInfo)>,
1040 contains_binding_arrays: bool,
1041}
1042
1043impl crate::DynBindGroupLayout for BindGroupLayout {}
1044
1045#[derive(Debug)]
1046pub struct PipelineLayout {
1047 raw: vk::PipelineLayout,
1048 binding_map: naga::back::spv::BindingMap,
1049}
1050
1051impl crate::DynPipelineLayout for PipelineLayout {}
1052
1053#[derive(Debug)]
1054pub struct BindGroup {
1055 set: descriptor::DescriptorSet,
1056}
1057
1058impl crate::DynBindGroup for BindGroup {}
1059
1060/// Miscellaneous allocation recycling pool for `CommandAllocator`.
1061#[derive(Default)]
1062struct Temp {
1063 marker: Vec<u8>,
1064 buffer_barriers: Vec<vk::BufferMemoryBarrier<'static>>,
1065 image_barriers: Vec<vk::ImageMemoryBarrier<'static>>,
1066}
1067
1068impl Temp {
1069 fn clear(&mut self) {
1070 self.marker.clear();
1071 self.buffer_barriers.clear();
1072 self.image_barriers.clear();
1073 }
1074
1075 fn make_c_str(&mut self, name: &str) -> &CStr {
1076 self.marker.clear();
1077 self.marker.extend_from_slice(name.as_bytes());
1078 self.marker.push(0);
1079 unsafe { CStr::from_bytes_with_nul_unchecked(&self.marker) }
1080 }
1081}
1082
1083/// Generates unique IDs for each resource of type `T`.
1084///
1085/// Because vk handles are not permanently unique, this
1086/// provides a way to generate unique IDs for each resource.
1087struct ResourceIdentityFactory<T> {
1088 #[cfg(not(target_has_atomic = "64"))]
1089 next_id: Mutex<u64>,
1090 #[cfg(target_has_atomic = "64")]
1091 next_id: core::sync::atomic::AtomicU64,
1092 _phantom: PhantomData<T>,
1093}
1094
1095impl<T> ResourceIdentityFactory<T> {
1096 fn new() -> Self {
1097 Self {
1098 #[cfg(not(target_has_atomic = "64"))]
1099 next_id: Mutex::new(0),
1100 #[cfg(target_has_atomic = "64")]
1101 next_id: core::sync::atomic::AtomicU64::new(0),
1102 _phantom: PhantomData,
1103 }
1104 }
1105
1106 /// Returns a new unique ID for a resource of type `T`.
1107 fn next(&self) -> ResourceIdentity<T> {
1108 #[cfg(not(target_has_atomic = "64"))]
1109 {
1110 let mut next_id = self.next_id.lock();
1111 let id = *next_id;
1112 *next_id += 1;
1113 ResourceIdentity {
1114 id,
1115 _phantom: PhantomData,
1116 }
1117 }
1118
1119 #[cfg(target_has_atomic = "64")]
1120 ResourceIdentity {
1121 id: self
1122 .next_id
1123 .fetch_add(1, core::sync::atomic::Ordering::Relaxed),
1124 _phantom: PhantomData,
1125 }
1126 }
1127}
1128
1129/// A unique identifier for a resource of type `T`.
1130///
1131/// This is used as a hashable key for resources, which
1132/// is permanently unique through the lifetime of the program.
1133#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
1134struct ResourceIdentity<T> {
1135 id: u64,
1136 _phantom: PhantomData<T>,
1137}
1138
1139#[derive(Clone, Eq, Hash, PartialEq)]
1140struct FramebufferKey {
1141 raw_pass: vk::RenderPass,
1142 /// Because this is used as a key in a hash map, we need to include the identity
1143 /// so that this hashes differently, even if the ImageView handles are the same
1144 /// between different views.
1145 attachment_identities: ArrayVec<ResourceIdentity<vk::ImageView>, { MAX_TOTAL_ATTACHMENTS }>,
1146 /// While this is redundant for calculating the hash, we need access to an array
1147 /// of all the raw ImageViews when we are creating the actual framebuffer,
1148 /// so we store this here.
1149 attachment_views: ArrayVec<vk::ImageView, { MAX_TOTAL_ATTACHMENTS }>,
1150 extent: wgt::Extent3d,
1151}
1152
1153impl FramebufferKey {
1154 fn push_view(&mut self, view: IdentifiedTextureView) {
1155 self.attachment_identities.push(view.identity);
1156 self.attachment_views.push(view.raw);
1157 }
1158}
1159
1160/// A texture view paired with its identity.
1161#[derive(Copy, Clone)]
1162struct IdentifiedTextureView {
1163 raw: vk::ImageView,
1164 identity: ResourceIdentity<vk::ImageView>,
1165}
1166
1167#[derive(Clone, Eq, Hash, PartialEq)]
1168struct TempTextureViewKey {
1169 texture: vk::Image,
1170 /// As this is used in a hashmap, we need to
1171 /// include the identity so that this hashes differently,
1172 /// even if the Image handles are the same between different images.
1173 texture_identity: ResourceIdentity<vk::Image>,
1174 format: vk::Format,
1175 mip_level: u32,
1176 depth_slice: u32,
1177}
1178
1179// Any state in this struct that may be dirty after an abandoned encoding must
1180// be reset for reused encoders in `begin_encoding`.
1181pub struct CommandEncoder {
1182 raw: vk::CommandPool,
1183 device: Arc<DeviceShared>,
1184
1185 /// The current command buffer, if `self` is in the ["recording"]
1186 /// state.
1187 ///
1188 /// ["recording"]: crate::CommandEncoder
1189 ///
1190 /// If non-`null`, the buffer is in the Vulkan "recording" state.
1191 active: vk::CommandBuffer,
1192
1193 /// What kind of pass we are currently within: compute or render.
1194 bind_point: vk::PipelineBindPoint,
1195
1196 /// Whether the device had widened its texture stages when the active
1197 /// buffer began, so that every texture barrier it records waits in
1198 /// upstream's stages.
1199 texture_stages_widened: bool,
1200
1201 /// Allocation recycling pool for this encoder.
1202 temp: Temp,
1203
1204 /// A pool of available command buffers.
1205 ///
1206 /// These are all in the Vulkan "initial" state.
1207 free: Vec<vk::CommandBuffer>,
1208
1209 /// A pool of discarded command buffers.
1210 ///
1211 /// These could be in any Vulkan state except "pending".
1212 discarded: Vec<vk::CommandBuffer>,
1213
1214 /// If this is true, the active renderpass enabled a debug span,
1215 /// and needs to be disabled on renderpass close.
1216 rpass_debug_marker_active: bool,
1217
1218 /// If set, the end of the next render/compute pass will write a timestamp at
1219 /// the given pool & location.
1220 end_of_pass_timer_query: Option<(vk::QueryPool, u32)>,
1221
1222 framebuffers: FastHashMap<FramebufferKey, vk::Framebuffer>,
1223 temp_texture_views: FastHashMap<TempTextureViewKey, IdentifiedTextureView>,
1224
1225 counters: Arc<wgt::HalCounters>,
1226
1227 current_pipeline_is_multiview: bool,
1228}
1229
1230impl Drop for CommandEncoder {
1231 fn drop(&mut self) {
1232 // SAFETY:
1233 //
1234 // VUID-vkDestroyCommandPool-commandPool-00041: wgpu_hal requires that a
1235 // `CommandBuffer` must live until its execution is complete, and that a
1236 // `CommandBuffer` must not outlive the `CommandEncoder` that built it.
1237 // Thus, we know that none of our `CommandBuffers` are in the "pending"
1238 // state.
1239 //
1240 // The other VUIDs are pretty obvious.
1241 unsafe {
1242 // `vkDestroyCommandPool` also frees any command buffers allocated
1243 // from that pool, so there's no need to explicitly call
1244 // `vkFreeCommandBuffers` on `cmd_encoder`'s `free` and `discarded`
1245 // fields.
1246 self.device.raw.destroy_command_pool(self.raw, None);
1247 }
1248
1249 for (_, fb) in self.framebuffers.drain() {
1250 unsafe { self.device.raw.destroy_framebuffer(fb, None) };
1251 }
1252
1253 for (_, view) in self.temp_texture_views.drain() {
1254 unsafe { self.device.raw.destroy_image_view(view.raw, None) };
1255 }
1256
1257 self.counters.command_encoders.sub(1);
1258 }
1259}
1260
1261impl CommandEncoder {
1262 /// # Safety
1263 ///
1264 /// - The command buffer handle must not be manually destroyed
1265 pub unsafe fn raw_handle(&self) -> vk::CommandBuffer {
1266 self.active
1267 }
1268}
1269
1270impl fmt::Debug for CommandEncoder {
1271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272 f.debug_struct("CommandEncoder")
1273 .field("raw", &self.raw)
1274 .finish()
1275 }
1276}
1277
1278#[derive(Debug)]
1279pub struct CommandBuffer {
1280 raw: vk::CommandBuffer,
1281 /// Whether every texture barrier in the buffer waits in upstream's
1282 /// stages; see [`CatchUpBarrier`].
1283 texture_stages_widened: bool,
1284}
1285
1286impl crate::DynCommandBuffer for CommandBuffer {}
1287
1288#[derive(Debug)]
1289pub enum ShaderModule {
1290 Raw(vk::ShaderModule),
1291 Intermediate {
1292 naga_shader: crate::NagaShader,
1293 runtime_checks: wgt::ShaderRuntimeChecks,
1294 },
1295}
1296
1297impl crate::DynShaderModule for ShaderModule {}
1298
1299#[derive(Debug)]
1300pub struct RenderPipeline {
1301 raw: vk::Pipeline,
1302 is_multiview: bool,
1303}
1304
1305impl crate::DynRenderPipeline for RenderPipeline {}
1306
1307#[derive(Debug)]
1308pub struct ComputePipeline {
1309 raw: vk::Pipeline,
1310}
1311
1312impl crate::DynComputePipeline for ComputePipeline {}
1313
1314#[derive(Debug)]
1315pub struct RayTracingPipeline {
1316 raw: vk::Pipeline,
1317}
1318
1319impl crate::DynRayTracingPipeline for RayTracingPipeline {}
1320
1321#[derive(Debug)]
1322pub struct PipelineCache {
1323 raw: vk::PipelineCache,
1324}
1325
1326impl crate::DynPipelineCache for PipelineCache {}
1327
1328#[derive(Debug)]
1329pub struct QuerySet {
1330 raw: vk::QueryPool,
1331}
1332
1333impl crate::DynQuerySet for QuerySet {}
1334
1335/// The [`Api::Fence`] type for [`vulkan::Api`].
1336///
1337/// This is an `enum` because there are two possible implementations of
1338/// `wgpu-hal` fences on Vulkan: Vulkan fences, which work on any version of
1339/// Vulkan, and Vulkan timeline semaphores, which are easier and cheaper but
1340/// require non-1.0 features.
1341///
1342/// [`Device::create_fence`] returns a [`TimelineSemaphore`] if
1343/// [`VK_KHR_timeline_semaphore`] is available and enabled, and a [`FencePool`]
1344/// otherwise.
1345///
1346/// [`Api::Fence`]: crate::Api::Fence
1347/// [`vulkan::Api`]: Api
1348/// [`Device::create_fence`]: crate::Device::create_fence
1349/// [`TimelineSemaphore`]: Fence::TimelineSemaphore
1350/// [`VK_KHR_timeline_semaphore`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_KHR_timeline_semaphore
1351/// [`FencePool`]: Fence::FencePool
1352#[derive(Debug)]
1353pub enum Fence {
1354 /// A Vulkan [timeline semaphore].
1355 ///
1356 /// These are simpler to use than Vulkan fences, since timeline semaphores
1357 /// work exactly the way [`wpgu_hal::Api::Fence`] is specified to work.
1358 ///
1359 /// [timeline semaphore]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-semaphores
1360 /// [`wpgu_hal::Api::Fence`]: crate::Api::Fence
1361 TimelineSemaphore(vk::Semaphore),
1362
1363 /// A collection of Vulkan [fence]s, each associated with a [`FenceValue`].
1364 ///
1365 /// The effective [`FenceValue`] of this variant is the greater of
1366 /// `last_completed` and the maximum value associated with a signalled fence
1367 /// in `active`.
1368 ///
1369 /// Fences are available in all versions of Vulkan, but since they only have
1370 /// two states, "signaled" and "unsignaled", we need to use a separate fence
1371 /// for each queue submission we might want to wait for, and remember which
1372 /// [`FenceValue`] each one represents.
1373 ///
1374 /// One should keep the fence pool read while there are any references to the
1375 /// fences inside of them. This ensures there are no race conditions when
1376 /// resetting the fences
1377 ///
1378 /// [fence]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-fences
1379 /// [`FenceValue`]: crate::FenceValue
1380 FencePool(RwLock<FencePool>),
1381}
1382
1383/// A shared fence type. The arc is expect to have a ref-count of one once a function has finished being called
1384///
1385/// A fence should have access synchronised as fence resetting might happen at any point. Resetting checks the ref-count
1386/// of the fence, so instead of copying the fence, it should have its `Arc` container cloned which shows not to reset
1387/// this fence as it is being used.
1388pub(super) type SynchronizedFence = Arc<vk::Fence>;
1389
1390#[derive(Debug)]
1391pub struct FencePool {
1392 last_completed: crate::FenceValue,
1393 /// The pending fence values have to be ascending.
1394 active: Vec<(crate::FenceValue, SynchronizedFence)>,
1395 // Don't need extra synchronisation around the fences here, if they are used they should be put into active.
1396 free: Vec<vk::Fence>,
1397}
1398
1399impl crate::DynFence for Fence {}
1400
1401impl Fence {
1402 /// Return the highest [`FenceValue`] among the signalled fences in `active`.
1403 ///
1404 /// As an optimization, assume that we already know that the fence has
1405 /// reached `last_completed`, and don't bother checking fences whose values
1406 /// are less than that: those fences remain in the `active` array only
1407 /// because we haven't called `maintain` yet to clean them up.
1408 ///
1409 /// [`FenceValue`]: crate::FenceValue
1410 fn check_active(
1411 device: &ash::Device,
1412 mut last_completed: crate::FenceValue,
1413 active: &[(crate::FenceValue, SynchronizedFence)],
1414 ) -> Result<crate::FenceValue, crate::DeviceError> {
1415 for &(value, ref raw) in active.iter() {
1416 unsafe {
1417 if value > last_completed
1418 && device
1419 // Don't need to clone as active should be from a read or
1420 // write lock which means this is already synchronised.
1421 .get_fence_status(**raw)
1422 .map_err(map_host_device_oom_and_lost_err)?
1423 {
1424 last_completed = value;
1425 }
1426 }
1427 }
1428 Ok(last_completed)
1429 }
1430
1431 /// Return the highest signalled [`FenceValue`] for `self`.
1432 ///
1433 /// [`FenceValue`]: crate::FenceValue
1434 fn get_latest(
1435 &self,
1436 device: &ash::Device,
1437 extension: Option<&ExtensionFn<khr::timeline_semaphore::Device>>,
1438 ) -> Result<crate::FenceValue, crate::DeviceError> {
1439 match *self {
1440 Self::TimelineSemaphore(raw) => unsafe {
1441 Ok(match *extension.unwrap() {
1442 ExtensionFn::Extension(ref ext) => ext
1443 .get_semaphore_counter_value(raw)
1444 .map_err(map_host_device_oom_and_lost_err)?,
1445 ExtensionFn::Promoted => device
1446 .get_semaphore_counter_value(raw)
1447 .map_err(map_host_device_oom_and_lost_err)?,
1448 })
1449 },
1450 Self::FencePool(ref pool) => {
1451 let FencePool {
1452 last_completed,
1453 ref active,
1454 free: _,
1455 } = *pool.read();
1456 Self::check_active(device, last_completed, active)
1457 }
1458 }
1459 }
1460
1461 /// Trim the internal state of this [`Fence`].
1462 ///
1463 /// This function has no externally visible effect, but you should call it
1464 /// periodically to keep this fence's resource consumption under control.
1465 ///
1466 /// For fences using the [`FencePool`] implementation, this function
1467 /// recycles fences that have been signaled. If you don't call this,
1468 /// [`Queue::submit`] will just keep allocating a new Vulkan fence every
1469 /// time it's called.
1470 ///
1471 /// [`FencePool`]: Fence::FencePool
1472 /// [`Queue::submit`]: crate::Queue::submit
1473 fn maintain(&self, device: &ash::Device) -> Result<(), crate::DeviceError> {
1474 match *self {
1475 Self::TimelineSemaphore(_) => {}
1476 Self::FencePool(ref pool) => {
1477 let FencePool {
1478 ref mut last_completed,
1479 ref mut active,
1480 ref mut free,
1481 } = *pool.write();
1482
1483 let base_free = free.len();
1484 let latest = Self::check_active(device, *last_completed, active)?;
1485
1486 active.retain_mut(|&mut (value, ref mut fence)| {
1487 if value > latest {
1488 true
1489 } else if let Some(fence) = Arc::get_mut(fence) {
1490 // No other references to these, so we have exclusive access. Add them to free and reset them later,
1491 // but drop them from active immediately
1492 free.push(*fence);
1493 false
1494 } else {
1495 // some other function is using it. Although this shouldn't be to long,
1496 // maintain shouldn't block, and it should be cleared up by the next time it happens
1497 true
1498 }
1499 });
1500
1501 if free.len() != base_free {
1502 unsafe { device.reset_fences(&free[base_free..]) }
1503 .map_err(map_device_oom_err)?
1504 }
1505 *last_completed = latest;
1506 }
1507 }
1508 Ok(())
1509 }
1510}
1511
1512impl crate::Queue for Queue {
1513 type A = Api;
1514
1515 unsafe fn submit(
1516 &self,
1517 command_buffers: &[&CommandBuffer],
1518 surface_textures: &[&SurfaceTexture],
1519 (signal_fence, signal_value): (&Fence, crate::FenceValue),
1520 ) -> Result<(), crate::DeviceError> {
1521 let mut fence_raw = vk::Fence::null();
1522
1523 let mut wait_semaphores = SemaphoreList::new(SemaphoreListMode::Wait);
1524 let mut signal_semaphores = SemaphoreList::new(SemaphoreListMode::Signal);
1525
1526 // Double check that the same swapchain image isn't being given to us multiple times,
1527 // as that will deadlock when we try to lock them all.
1528 debug_assert!(
1529 {
1530 let mut check = HashSet::with_capacity(surface_textures.len());
1531 // We compare the Box by pointer, as Eq isn't well defined for SurfaceSemaphores.
1532 for st in surface_textures {
1533 let ptr: *const () = <*const _>::cast(&*st.metadata);
1534 check.insert(ptr as usize);
1535 }
1536 check.len() == surface_textures.len()
1537 },
1538 "More than one surface texture is being used from the same swapchain. This will cause a deadlock in release."
1539 );
1540
1541 let locked_swapchain_semaphores = surface_textures
1542 .iter()
1543 .map(|st| st.metadata.get_semaphore_guard())
1544 .collect::<Vec<_>>();
1545
1546 for mut semaphores in locked_swapchain_semaphores {
1547 semaphores.set_used_fence_value(signal_value);
1548
1549 // If we're the first submission to operate on this image, wait on
1550 // its acquire semaphore, to make sure the presentation engine is
1551 // done with it.
1552 if let Some(sem) = semaphores.get_acquire_wait_semaphore() {
1553 wait_semaphores.push_wait(sem, vk::PipelineStageFlags::TOP_OF_PIPE);
1554 }
1555
1556 // Get a semaphore to signal when we're done writing to this surface
1557 // image. Presentation of this image will wait for this.
1558 let signal_semaphore = semaphores.get_submit_signal_semaphore(&self.device)?;
1559 signal_semaphores.push_signal(signal_semaphore);
1560 }
1561
1562 let mut guard = self.signal_semaphores.lock();
1563 if !guard.is_empty() {
1564 signal_semaphores.append(&mut guard);
1565 }
1566
1567 let mut wait_guard = self.wait_semaphores.lock();
1568 if !wait_guard.is_empty() {
1569 wait_semaphores.append(&mut wait_guard);
1570 }
1571
1572 // In order for submissions to be strictly ordered, we encode a dependency between each submission
1573 // using a pair of semaphores. This adds a wait if it is needed, and signals the next semaphore.
1574 let semaphore_state = self.relay_semaphores.lock().advance(&self.device)?;
1575
1576 if let Some(sem) = semaphore_state.wait {
1577 wait_semaphores.push_wait(
1578 SemaphoreType::Binary(sem),
1579 vk::PipelineStageFlags::TOP_OF_PIPE,
1580 );
1581 }
1582
1583 signal_semaphores.push_signal(SemaphoreType::Binary(semaphore_state.signal));
1584
1585 // We need to signal our wgpu::Fence if we have one, this adds it to the signal list.
1586 signal_fence.maintain(&self.device.raw)?;
1587 // Keeping the Arc around is probably unneeded - the fence should never be signaled as it was reset,
1588 // and newer submits should not happen until this submit is done. Therefore, it should be too high
1589 // to be reset.
1590 let shared_fence;
1591 match *signal_fence {
1592 Fence::TimelineSemaphore(raw) => {
1593 signal_semaphores.push_signal(SemaphoreType::Timeline(raw, signal_value));
1594 }
1595 Fence::FencePool(ref pool) => {
1596 let FencePool {
1597 ref mut active,
1598 ref mut free,
1599 ..
1600 } = *pool.write();
1601 shared_fence = match free.pop() {
1602 Some(raw) => Arc::new(raw),
1603 None => unsafe {
1604 let fence = self
1605 .device
1606 .raw
1607 .create_fence(&vk::FenceCreateInfo::default(), None)
1608 .map_err(map_host_device_oom_err)?;
1609 Arc::new(fence)
1610 },
1611 };
1612 fence_raw = *shared_fence;
1613 active.push((signal_value, shared_fence.clone()));
1614 }
1615 }
1616
1617 let vk_cmd_buffers = self.submission(command_buffers)?;
1618
1619 let mut vk_info = vk::SubmitInfo::default().command_buffers(&vk_cmd_buffers);
1620 let mut vk_timeline_info = mem::MaybeUninit::uninit();
1621 vk_info = SemaphoreList::add_to_submit(
1622 &mut wait_semaphores,
1623 &mut signal_semaphores,
1624 vk_info,
1625 &mut vk_timeline_info,
1626 );
1627
1628 profiling::scope!("vkQueueSubmit");
1629 unsafe {
1630 self.device
1631 .raw
1632 .queue_submit(self.raw, &[vk_info], fence_raw)
1633 .map_err(map_host_device_oom_and_lost_err)?
1634 };
1635 Ok(())
1636 }
1637
1638 unsafe fn present(
1639 &self,
1640 surface: &Surface,
1641 texture: SurfaceTexture,
1642 ) -> Result<(), crate::SurfaceError> {
1643 let mut swapchain = surface.swapchain.write();
1644
1645 unsafe { swapchain.as_mut().unwrap().present(self, texture) }
1646 }
1647
1648 unsafe fn get_timestamp_period(&self) -> f32 {
1649 self.device.timestamp_period
1650 }
1651
1652 unsafe fn wait_for_idle(&self) -> Result<(), crate::DeviceError> {
1653 unsafe { self.device.raw.queue_wait_idle(self.raw) }
1654 .map_err(map_host_device_oom_and_lost_err)
1655 }
1656}
1657
1658impl Queue {
1659 pub fn raw_device(&self) -> &ash::Device {
1660 &self.device.raw
1661 }
1662
1663 pub fn add_signal_semaphore(&self, semaphore: vk::Semaphore, semaphore_value: Option<u64>) {
1664 let mut guard = self.signal_semaphores.lock();
1665 if let Some(value) = semaphore_value {
1666 guard.push_signal(SemaphoreType::Timeline(semaphore, value));
1667 } else {
1668 guard.push_signal(SemaphoreType::Binary(semaphore));
1669 }
1670 }
1671
1672 /// Remove `semaphore` from the pending signal list if it is still present.
1673 ///
1674 /// Returns `true` if the semaphore was found and removed. If the submit
1675 /// already consumed it, this is a harmless no-op that returns `false`.
1676 pub fn remove_signal_semaphore(&self, semaphore: vk::Semaphore) -> bool {
1677 self.signal_semaphores.lock().remove(semaphore)
1678 }
1679
1680 /// Stage a semaphore wait on the next [`crate::Queue::submit`] call.
1681 ///
1682 /// `semaphore_value` selects the kind of payload the wait targets:
1683 ///
1684 /// - `Some(value)` - wait until `semaphore` (a timeline semaphore) has been signalled to at least `value`.
1685 /// - `None` - wait on a binary semaphore signal.
1686 ///
1687 /// `stage` is the pipeline stage at which the wait blocks downstream
1688 /// work (e.g. `vk::PipelineStageFlags::TOP_OF_PIPE` to gate the
1689 /// entire submission, or a more specific stage when only that stage
1690 /// reads the synchronised resource).
1691 pub fn add_wait_semaphore(
1692 &self,
1693 semaphore: vk::Semaphore,
1694 semaphore_value: Option<u64>,
1695 stage: vk::PipelineStageFlags,
1696 ) {
1697 let mut guard = self.wait_semaphores.lock();
1698 if let Some(value) = semaphore_value {
1699 guard.push_wait(SemaphoreType::Timeline(semaphore, value), stage);
1700 } else {
1701 guard.push_wait(SemaphoreType::Binary(semaphore), stage);
1702 }
1703 }
1704
1705 /// Remove `semaphore` from the pending wait list if it is still present.
1706 ///
1707 /// Returns `true` if the semaphore was found and removed. If the submit
1708 /// already consumed it, this is a no-op that returns `false`.
1709 pub fn remove_wait_semaphore(&self, semaphore: vk::Semaphore) -> bool {
1710 self.wait_semaphores.lock().remove(semaphore)
1711 }
1712}
1713
1714/// Maps
1715///
1716/// - VK_ERROR_OUT_OF_HOST_MEMORY
1717/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1718fn map_host_device_oom_err(err: vk::Result) -> crate::DeviceError {
1719 match err {
1720 vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
1721 get_oom_err(err)
1722 }
1723 e => get_unexpected_err(e),
1724 }
1725}
1726
1727/// Maps
1728///
1729/// - VK_ERROR_OUT_OF_HOST_MEMORY
1730/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1731/// - VK_ERROR_DEVICE_LOST
1732fn map_host_device_oom_and_lost_err(err: vk::Result) -> crate::DeviceError {
1733 match err {
1734 vk::Result::ERROR_DEVICE_LOST => get_lost_err(),
1735 other => map_host_device_oom_err(other),
1736 }
1737}
1738
1739/// Maps
1740///
1741/// - VK_ERROR_OUT_OF_HOST_MEMORY
1742/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1743/// - VK_ERROR_FRAGMENTATION
1744fn map_host_device_oom_and_fragmentation_err(err: vk::Result) -> crate::DeviceError {
1745 match err {
1746 vk::Result::ERROR_FRAGMENTATION => get_oom_err(err),
1747 other => map_host_device_oom_err(other),
1748 }
1749}
1750
1751/// Maps
1752///
1753/// - VK_ERROR_OUT_OF_HOST_MEMORY
1754/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1755/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1756fn map_host_device_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1757 // We don't use VK_KHR_buffer_device_address
1758 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1759 map_host_device_oom_err(err)
1760}
1761
1762/// Maps
1763///
1764/// - VK_ERROR_OUT_OF_HOST_MEMORY
1765fn map_host_oom_err(err: vk::Result) -> crate::DeviceError {
1766 match err {
1767 vk::Result::ERROR_OUT_OF_HOST_MEMORY => get_oom_err(err),
1768 e => get_unexpected_err(e),
1769 }
1770}
1771
1772/// Maps
1773///
1774/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1775fn map_device_oom_err(err: vk::Result) -> crate::DeviceError {
1776 match err {
1777 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => get_oom_err(err),
1778 e => get_unexpected_err(e),
1779 }
1780}
1781
1782/// Maps
1783///
1784/// - VK_ERROR_OUT_OF_HOST_MEMORY
1785/// - VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1786fn map_host_oom_and_ioca_err(err: vk::Result) -> crate::DeviceError {
1787 // We don't use VK_KHR_buffer_device_address
1788 // VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR
1789 map_host_oom_err(err)
1790}
1791
1792/// Maps
1793///
1794/// - VK_ERROR_OUT_OF_HOST_MEMORY
1795/// - VK_ERROR_OUT_OF_DEVICE_MEMORY
1796/// - VK_PIPELINE_COMPILE_REQUIRED_EXT
1797/// - VK_ERROR_INVALID_SHADER_NV
1798fn map_pipeline_err(err: vk::Result) -> crate::DeviceError {
1799 // We don't use VK_EXT_pipeline_creation_cache_control
1800 // VK_PIPELINE_COMPILE_REQUIRED_EXT
1801 // We don't use VK_NV_glsl_shader
1802 // VK_ERROR_INVALID_SHADER_NV
1803 map_host_device_oom_err(err)
1804}
1805
1806/// Returns [`crate::DeviceError::Unexpected`] or panics if the `internal_error_panic`
1807/// feature flag is enabled.
1808fn get_unexpected_err(_err: vk::Result) -> crate::DeviceError {
1809 #[cfg(feature = "internal_error_panic")]
1810 panic!("Unexpected Vulkan error: {_err:?}");
1811
1812 #[allow(unreachable_code)]
1813 crate::DeviceError::Unexpected
1814}
1815
1816/// Returns [`crate::DeviceError::OutOfMemory`].
1817fn get_oom_err(_err: vk::Result) -> crate::DeviceError {
1818 crate::DeviceError::OutOfMemory
1819}
1820
1821/// Returns [`crate::DeviceError::Lost`] or panics if the `device_lost_panic`
1822/// feature flag is enabled.
1823fn get_lost_err() -> crate::DeviceError {
1824 #[cfg(feature = "device_lost_panic")]
1825 panic!("Device lost");
1826
1827 #[allow(unreachable_code)]
1828 crate::DeviceError::Lost
1829}
1830
1831#[derive(Clone, Copy, Pod, Zeroable)]
1832#[repr(C)]
1833struct RawTlasInstance {
1834 transform: [f32; 12],
1835 custom_data_and_mask: u32,
1836 shader_binding_table_record_offset_and_flags: u32,
1837 acceleration_structure_reference: u64,
1838}
1839
1840/// Arguments to the [`CreateDeviceCallback`].
1841#[derive(Debug)]
1842pub struct CreateDeviceCallbackArgs<'arg, 'pnext, 'this>
1843where
1844 'this: 'pnext,
1845{
1846 /// The extensions to enable for the device. You must not remove anything from this list,
1847 /// but you may add to it.
1848 pub extensions: &'arg mut Vec<&'static CStr>,
1849 /// The physical device features to enable. You may enable features, but must not disable any.
1850 pub device_features: &'arg mut PhysicalDeviceFeatures,
1851 /// The queue create infos for the device. You may add or modify queue create infos as needed.
1852 pub queue_create_infos: &'arg mut Vec<vk::DeviceQueueCreateInfo<'pnext>>,
1853 /// The create info for the device. You may add or modify things in the pnext chain, but
1854 /// do not turn features off. Additionally, do not add things to the list of extensions,
1855 /// or to the feature set, as all changes to that member will be overwritten.
1856 pub create_info: &'arg mut vk::DeviceCreateInfo<'pnext>,
1857 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1858 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1859 /// don't actually directly use `'this`
1860 _phantom: PhantomData<&'this ()>,
1861}
1862
1863/// Callback to allow changing the vulkan device creation parameters.
1864///
1865/// # Safety:
1866/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1867/// as the create info value will be overwritten.
1868/// - Callback must not remove features.
1869/// - Callback must not change anything to what the instance does not support.
1870pub type CreateDeviceCallback<'this> =
1871 dyn for<'arg, 'pnext> FnOnce(CreateDeviceCallbackArgs<'arg, 'pnext, 'this>) + 'this;
1872
1873/// Arguments to the [`CreateInstanceCallback`].
1874#[expect(missing_debug_implementations, reason = "TODO?")]
1875pub struct CreateInstanceCallbackArgs<'arg, 'pnext, 'this>
1876where
1877 'this: 'pnext,
1878{
1879 /// The extensions to enable for the instance. You must not remove anything from this list,
1880 /// but you may add to it.
1881 pub extensions: &'arg mut Vec<&'static CStr>,
1882 /// The create info for the instance. You may add or modify things in the pnext chain, but
1883 /// do not turn features off. Additionally, do not add things to the list of extensions,
1884 /// all changes to that member will be overwritten.
1885 pub create_info: &'arg mut vk::InstanceCreateInfo<'pnext>,
1886 /// Vulkan entry point.
1887 pub entry: &'arg ash::Entry,
1888 /// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
1889 /// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
1890 /// don't actually directly use `'this`
1891 _phantom: PhantomData<&'this ()>,
1892}
1893
1894/// Callback to allow changing the vulkan instance creation parameters.
1895///
1896/// # Safety:
1897/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
1898/// as the create info value will be overwritten.
1899/// - Callback must not remove features.
1900/// - Callback must not change anything to what the instance does not support.
1901pub type CreateInstanceCallback<'this> =
1902 dyn for<'arg, 'pnext> FnOnce(CreateInstanceCallbackArgs<'arg, 'pnext, 'this>) + 'this;