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