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