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