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