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