Skip to main content

wgpu_hal/vulkan/
mod.rs

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