Skip to main content

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