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