Skip to main content

wgpu_hal/vulkan/
device.rs

1use alloc::{borrow::ToOwned as _, collections::BTreeMap, ffi::CString, sync::Arc, vec::Vec};
2use core::{
3    ffi::CStr,
4    mem::{self, MaybeUninit},
5    num::NonZeroU32,
6    ptr,
7    time::Duration,
8};
9
10use arrayvec::ArrayVec;
11use ash::{ext, vk};
12use hashbrown::hash_map::Entry;
13use wgpu_sync::{Mutex, RwLock};
14
15use super::{conv, descriptor::DescriptorCounts, RawTlasInstance};
16use crate::TlasInstance;
17
18impl super::DeviceShared {
19    /// Set the name of `object` to `name`.
20    ///
21    /// If `name` contains an interior null byte, then the name set will be truncated to that byte.
22    ///
23    /// # Safety
24    ///
25    /// This method inherits the safety contract from [`vkSetDebugUtilsObjectName`]. In particular:
26    ///
27    /// - `object` must be a valid handle for one of the following:
28    ///   - An instance-level object from the same instance as this device.
29    ///   - A physical-device-level object that descends from the same physical device as this
30    ///     device.
31    ///   - A device-level object that descends from this device.
32    /// - `object` must be externally synchronized—only the calling thread should access it during
33    ///   this call.
34    ///
35    /// [`vkSetDebugUtilsObjectName`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkSetDebugUtilsObjectNameEXT.html
36    pub(super) unsafe fn set_object_name(&self, object: impl vk::Handle, name: &str) {
37        let Some(extension) = self.extension_fns.debug_utils.as_ref() else {
38            return;
39        };
40
41        // Keep variables outside the if-else block to ensure they do not
42        // go out of scope while we hold a pointer to them
43        let mut buffer: [u8; 64] = [0u8; 64];
44        let buffer_vec: Vec<u8>;
45
46        // Append a null terminator to the string
47        let name_bytes = if name.len() < buffer.len() {
48            // Common case, string is very small. Allocate a copy on the stack.
49            buffer[..name.len()].copy_from_slice(name.as_bytes());
50            // Add null terminator
51            buffer[name.len()] = 0;
52            &buffer[..name.len() + 1]
53        } else {
54            // Less common case, the string is large.
55            // This requires a heap allocation.
56            buffer_vec = name
57                .as_bytes()
58                .iter()
59                .cloned()
60                .chain(core::iter::once(0))
61                .collect();
62            &buffer_vec
63        };
64
65        let name = CStr::from_bytes_until_nul(name_bytes).expect("We have added a null byte");
66
67        let _result = unsafe {
68            extension.set_debug_utils_object_name(
69                &vk::DebugUtilsObjectNameInfoEXT::default()
70                    .object_handle(object)
71                    .object_name(name),
72            )
73        };
74    }
75
76    pub fn make_render_pass(
77        &self,
78        key: super::RenderPassKey,
79    ) -> Result<vk::RenderPass, crate::DeviceError> {
80        Ok(match self.render_passes.lock().entry(key) {
81            Entry::Occupied(e) => *e.get(),
82            Entry::Vacant(e) => {
83                let super::RenderPassKey {
84                    ref colors,
85                    ref depth_stencil,
86                    sample_count,
87                    multiview_mask,
88                    depth_read_only,
89                    stencil_read_only,
90                } = *e.key();
91
92                let mut vk_attachments = Vec::new();
93                let mut color_refs = Vec::with_capacity(colors.len());
94                let mut resolve_refs = Vec::with_capacity(color_refs.capacity());
95                let mut ds_ref = None;
96                let samples = vk::SampleCountFlags::from_raw(sample_count);
97                let unused = vk::AttachmentReference {
98                    attachment: vk::ATTACHMENT_UNUSED,
99                    layout: vk::ImageLayout::UNDEFINED,
100                };
101                for cat in colors.iter() {
102                    let (color_ref, resolve_ref) =
103                        if let Some(super::ColorAttachmentKey { base, resolve }) = cat {
104                            let super::AttachmentKey {
105                                format,
106                                layout,
107                                ops,
108                            } = *base;
109
110                            let color_ref = vk::AttachmentReference {
111                                attachment: vk_attachments.len() as u32,
112                                layout,
113                            };
114                            vk_attachments.push({
115                                let (load_op, store_op) = conv::map_attachment_ops(ops);
116                                vk::AttachmentDescription::default()
117                                    .format(format)
118                                    .samples(samples)
119                                    .load_op(load_op)
120                                    .store_op(store_op)
121                                    .initial_layout(layout)
122                                    .final_layout(layout)
123                            });
124                            let resolve_ref = if let Some(rat) = resolve {
125                                let super::AttachmentKey {
126                                    format,
127                                    layout,
128                                    ops,
129                                } = *rat;
130
131                                let (load_op, store_op) = conv::map_attachment_ops(ops);
132                                let vk_attachment = vk::AttachmentDescription::default()
133                                    .format(format)
134                                    .samples(vk::SampleCountFlags::TYPE_1)
135                                    .load_op(load_op)
136                                    .store_op(store_op)
137                                    .initial_layout(layout)
138                                    .final_layout(layout);
139                                vk_attachments.push(vk_attachment);
140
141                                vk::AttachmentReference {
142                                    attachment: vk_attachments.len() as u32 - 1,
143                                    layout,
144                                }
145                            } else {
146                                unused
147                            };
148
149                            (color_ref, resolve_ref)
150                        } else {
151                            (unused, unused)
152                        };
153
154                    color_refs.push(color_ref);
155                    resolve_refs.push(resolve_ref);
156                }
157
158                if let Some(ds) = depth_stencil {
159                    let super::DepthStencilAttachmentKey {
160                        ref base,
161                        stencil_ops,
162                    } = *ds;
163
164                    let super::AttachmentKey {
165                        format,
166                        layout,
167                        ops,
168                    } = *base;
169
170                    ds_ref = Some(vk::AttachmentReference {
171                        attachment: vk_attachments.len() as u32,
172                        layout,
173                    });
174                    let (load_op, mut store_op) = conv::map_attachment_ops(ops);
175                    let (stencil_load_op, mut stencil_store_op) =
176                        conv::map_attachment_ops(stencil_ops);
177
178                    let store_op_for_read_only = if self.private_caps.store_op_none {
179                        vk::AttachmentStoreOp::NONE
180                    } else {
181                        vk::AttachmentStoreOp::STORE
182                    };
183                    if depth_read_only {
184                        store_op = store_op_for_read_only;
185                    }
186                    if stencil_read_only {
187                        stencil_store_op = store_op_for_read_only;
188                    }
189
190                    let vk_attachment = vk::AttachmentDescription::default()
191                        .format(format)
192                        .samples(samples)
193                        .load_op(load_op)
194                        .store_op(store_op)
195                        .stencil_load_op(stencil_load_op)
196                        .stencil_store_op(stencil_store_op)
197                        .initial_layout(layout)
198                        .final_layout(layout);
199                    vk_attachments.push(vk_attachment);
200                }
201
202                let vk_subpasses = [{
203                    let mut vk_subpass = vk::SubpassDescription::default()
204                        .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
205                        .color_attachments(&color_refs)
206                        .resolve_attachments(&resolve_refs);
207
208                    if self
209                        .workarounds
210                        .contains(super::Workarounds::EMPTY_RESOLVE_ATTACHMENT_LISTS)
211                        && resolve_refs.is_empty()
212                    {
213                        vk_subpass.p_resolve_attachments = ptr::null();
214                    }
215
216                    if let Some(ref reference) = ds_ref {
217                        vk_subpass = vk_subpass.depth_stencil_attachment(reference)
218                    }
219                    vk_subpass
220                }];
221
222                let mut vk_info = vk::RenderPassCreateInfo::default()
223                    .attachments(&vk_attachments)
224                    .subpasses(&vk_subpasses);
225
226                let mut multiview_info;
227                let mask;
228                if let Some(multiview_mask) = multiview_mask {
229                    mask = [multiview_mask.get()];
230
231                    // On Vulkan 1.1 or later, this is an alias for core functionality
232                    multiview_info = vk::RenderPassMultiviewCreateInfoKHR::default()
233                        .view_masks(&mask)
234                        .correlation_masks(&mask);
235                    vk_info = vk_info.push_next(&mut multiview_info);
236                }
237
238                let raw = unsafe {
239                    self.raw
240                        .create_render_pass(&vk_info, None)
241                        .map_err(super::map_host_device_oom_err)?
242                };
243
244                *e.insert(raw)
245            }
246        })
247    }
248
249    fn make_memory_ranges<'a, I: 'a + Iterator<Item = crate::MemoryRange>>(
250        &self,
251        buffer: &'a super::Buffer,
252        ranges: I,
253    ) -> Option<impl 'a + Iterator<Item = vk::MappedMemoryRange<'a>>> {
254        let super::BufferOwnership::Managed(ref allocation) = buffer.ownership else {
255            return None;
256        };
257        let allocation = allocation.lock();
258        let mask = self.private_caps.non_coherent_map_mask;
259        Some(ranges.map(move |range| {
260            vk::MappedMemoryRange::default()
261                .memory(allocation.memory())
262                .offset((allocation.offset() + range.start) & !mask)
263                .size((range.end - range.start + mask) & !mask)
264        }))
265    }
266}
267
268struct CompiledStage {
269    create_info: vk::PipelineShaderStageCreateInfo<'static>,
270    _entry_point: CString,
271    temp_raw_module: Option<vk::ShaderModule>,
272}
273
274struct MemoryProperties {
275    base: vk::PhysicalDeviceMemoryProperties,
276    heap_budget: ArrayVec<vk::DeviceSize, { vk::MAX_MEMORY_HEAPS }>,
277    heap_usage: ArrayVec<vk::DeviceSize, { vk::MAX_MEMORY_HEAPS }>,
278}
279
280impl MemoryProperties {
281    fn types(&self) -> &[vk::MemoryType] {
282        let count = self.base.memory_type_count as usize;
283        &self.base.memory_types[0..count]
284    }
285
286    fn heaps(&self) -> &[vk::MemoryHeap] {
287        let count = self.base.memory_heap_count as usize;
288        &self.base.memory_heaps[0..count]
289    }
290
291    fn heap_budget(&self) -> &[vk::DeviceSize] {
292        let count = self.base.memory_heap_count as usize;
293        &self.heap_budget[0..count]
294    }
295
296    fn heap_usage(&self) -> &[vk::DeviceSize] {
297        let count = self.base.memory_heap_count as usize;
298        &self.heap_usage[0..count]
299    }
300}
301
302impl super::Device {
303    /// # Safety
304    ///
305    /// - `vk_image` must be created respecting `desc`
306    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `vk_image`. If
307    ///   `drop_callback` is [`Some`], `vk_image` must be valid until the callback is called.
308    /// - If the `ImageCreateFlags` does not contain `MUTABLE_FORMAT`, the `view_formats` of `desc` must be empty.
309    /// - If `memory` is not [`super::TextureMemory::External`], wgpu-hal will take ownership of the
310    ///   memory (which is presumed to back `vk_image`). Otherwise, the memory must remain valid until
311    ///   `drop_callback` is called.
312    pub unsafe fn texture_from_raw(
313        &self,
314        vk_image: vk::Image,
315        desc: &crate::TextureDescriptor,
316        drop_callback: Option<crate::DropCallback>,
317        memory: super::TextureMemory,
318    ) -> super::Texture {
319        let identity = self.shared.texture_identity_factory.next();
320        let drop_guard = crate::DropGuard::from_option(drop_callback);
321
322        if let Some(label) = desc.label {
323            unsafe { self.shared.set_object_name(vk_image, label) };
324        }
325
326        super::Texture {
327            raw: vk_image,
328            drop_guard,
329            memory,
330            format: desc.format,
331            copy_size: desc.copy_extent(),
332            identity,
333        }
334    }
335
336    fn find_memory_type_index(
337        &self,
338        type_bits_req: u32,
339        flags_req: vk::MemoryPropertyFlags,
340    ) -> Option<usize> {
341        let mem_properties = unsafe {
342            self.shared
343                .instance
344                .raw
345                .get_physical_device_memory_properties(self.shared.physical_device)
346        };
347
348        // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html
349        for (i, mem_ty) in mem_properties.memory_types_as_slice().iter().enumerate() {
350            let types_bits = 1 << i;
351            let is_required_memory_type = type_bits_req & types_bits != 0;
352            let has_required_properties = mem_ty.property_flags & flags_req == flags_req;
353            if is_required_memory_type && has_required_properties {
354                return Some(i);
355            }
356        }
357
358        None
359    }
360
361    fn create_image_without_memory(
362        &self,
363        desc: &crate::TextureDescriptor,
364        external_memory_image_create_info: Option<&mut vk::ExternalMemoryImageCreateInfo>,
365    ) -> Result<ImageWithoutMemory, crate::DeviceError> {
366        self.create_image_without_memory_with_tiling(
367            desc,
368            vk::ImageTiling::OPTIMAL,
369            external_memory_image_create_info,
370            None,
371        )
372    }
373
374    fn create_image_without_memory_with_tiling(
375        &self,
376        desc: &crate::TextureDescriptor,
377        tiling: vk::ImageTiling,
378        external_memory_image_create_info: Option<&mut vk::ExternalMemoryImageCreateInfo>,
379        drm_modifier_info: Option<&mut vk::ImageDrmFormatModifierExplicitCreateInfoEXT>,
380    ) -> Result<ImageWithoutMemory, crate::DeviceError> {
381        let copy_size = desc.copy_extent();
382
383        let mut raw_flags = vk::ImageCreateFlags::empty();
384        if desc.dimension == wgt::TextureDimension::D3
385            && desc.usage.contains(wgt::TextureUses::COLOR_TARGET)
386        {
387            raw_flags |= vk::ImageCreateFlags::TYPE_2D_ARRAY_COMPATIBLE;
388        }
389        if desc.is_cube_compatible() {
390            raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
391        }
392
393        let original_format = self.shared.private_caps.map_texture_format(desc.format);
394        let mut vk_view_formats = vec![];
395        if !desc.view_formats.is_empty() {
396            raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
397
398            if self.shared.private_caps.image_format_list {
399                vk_view_formats = desc
400                    .view_formats
401                    .iter()
402                    .map(|f| self.shared.private_caps.map_texture_format(*f))
403                    .collect();
404                vk_view_formats.push(original_format)
405            }
406        }
407        if desc.format.is_multi_planar_format() {
408            raw_flags |=
409                vk::ImageCreateFlags::MUTABLE_FORMAT | vk::ImageCreateFlags::EXTENDED_USAGE;
410        }
411
412        let mut vk_info = vk::ImageCreateInfo::default()
413            .flags(raw_flags)
414            .image_type(conv::map_texture_dimension(desc.dimension))
415            .format(original_format)
416            .extent(conv::map_copy_extent(&copy_size))
417            .mip_levels(desc.mip_level_count)
418            .array_layers(desc.array_layer_count())
419            .samples(vk::SampleCountFlags::from_raw(desc.sample_count))
420            .tiling(tiling)
421            .usage(conv::map_texture_usage(desc.usage))
422            .sharing_mode(vk::SharingMode::EXCLUSIVE)
423            .initial_layout(vk::ImageLayout::UNDEFINED);
424
425        let mut format_list_info = vk::ImageFormatListCreateInfo::default();
426        if !vk_view_formats.is_empty() {
427            format_list_info = format_list_info.view_formats(&vk_view_formats);
428            vk_info = vk_info.push_next(&mut format_list_info);
429        }
430
431        if let Some(ext_info) = external_memory_image_create_info {
432            vk_info = vk_info.push_next(ext_info);
433        }
434
435        if let Some(drm_info) = drm_modifier_info {
436            vk_info = vk_info.push_next(drm_info);
437        }
438
439        let raw = unsafe { self.shared.raw.create_image(&vk_info, None) }.map_err(map_err)?;
440        fn map_err(err: vk::Result) -> crate::DeviceError {
441            // We don't use VK_EXT_image_compression_control
442            // VK_ERROR_COMPRESSION_EXHAUSTED_EXT
443            super::map_host_device_oom_and_ioca_err(err)
444        }
445        let mut req = unsafe { self.shared.raw.get_image_memory_requirements(raw) };
446
447        if desc.usage.contains(wgt::TextureUses::TRANSIENT) {
448            let mem_type_index = self.find_memory_type_index(
449                req.memory_type_bits,
450                vk::MemoryPropertyFlags::LAZILY_ALLOCATED,
451            );
452            if let Some(mem_type_index) = mem_type_index {
453                req.memory_type_bits = 1 << mem_type_index;
454            }
455        }
456
457        Ok(ImageWithoutMemory {
458            raw,
459            requirements: req,
460        })
461    }
462
463    /// # Safety
464    ///
465    /// - Vulkan (with VK_KHR_external_memory_win32)
466    /// - The `d3d11_shared_handle` must be valid and respecting `desc`
467    /// - `VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT` flag is used because we need to hold a reference to the handle
468    #[cfg(windows)]
469    pub unsafe fn texture_from_d3d11_shared_handle(
470        &self,
471        d3d11_shared_handle: windows::Win32::Foundation::HANDLE,
472        desc: &crate::TextureDescriptor,
473    ) -> Result<super::Texture, crate::DeviceError> {
474        if !self
475            .shared
476            .features
477            .contains(wgt::Features::VULKAN_EXTERNAL_MEMORY_WIN32)
478        {
479            log::error!("Vulkan driver does not support VK_KHR_external_memory_win32");
480            return Err(crate::DeviceError::Unexpected);
481        }
482
483        let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::default()
484            .handle_types(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE);
485
486        let image =
487            self.create_image_without_memory(desc, Some(&mut external_memory_image_info))?;
488
489        // Some external memory types require dedicated allocation
490        // https://docs.vulkan.org/guide/latest/extensions/external.html#_importing_memory
491        let mut dedicated_allocate_info =
492            vk::MemoryDedicatedAllocateInfo::default().image(image.raw);
493
494        let mut import_memory_info = vk::ImportMemoryWin32HandleInfoKHR::default()
495            .handle_type(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE)
496            .handle(d3d11_shared_handle.0 as _);
497        // TODO: We should use `push_next` instead, but currently ash does not provide this method for the `ImportMemoryWin32HandleInfoKHR` type.
498        #[allow(clippy::unnecessary_mut_passed)]
499        {
500            import_memory_info.p_next = <*const _>::cast(&mut dedicated_allocate_info);
501        }
502
503        let mem_type_index = self
504            .find_memory_type_index(
505                image.requirements.memory_type_bits,
506                vk::MemoryPropertyFlags::DEVICE_LOCAL,
507            )
508            .ok_or(crate::DeviceError::Unexpected)?;
509
510        let memory_allocate_info = vk::MemoryAllocateInfo::default()
511            .allocation_size(image.requirements.size)
512            .memory_type_index(mem_type_index as _)
513            .push_next(&mut import_memory_info);
514        let memory = unsafe { self.shared.raw.allocate_memory(&memory_allocate_info, None) }
515            .map_err(super::map_host_device_oom_err)?;
516
517        unsafe { self.shared.raw.bind_image_memory(image.raw, memory, 0) }
518            .map_err(super::map_host_device_oom_err)?;
519
520        Ok(unsafe {
521            self.texture_from_raw(
522                image.raw,
523                desc,
524                None,
525                super::TextureMemory::Dedicated(memory),
526            )
527        })
528    }
529
530    /// Import a DMA-buf as a texture. Currently only supports single-plane DMA-bufs.
531    ///
532    /// # Safety
533    ///
534    /// - Requires `VULKAN_EXTERNAL_MEMORY_DMA_BUF` feature (implies VK_EXT_external_memory_dma_buf
535    ///   and VK_EXT_image_drm_format_modifier)
536    /// - The `fd` must be a valid DMA-buf file descriptor matching `desc`
537    /// - On success, Vulkan takes ownership of the file descriptor. On failure,
538    ///   the file descriptor is closed.
539    /// - The `drm_modifier`, `stride`, and `offset` must match the DMA-buf layout
540    #[cfg(unix)]
541    pub unsafe fn texture_from_dmabuf_fd(
542        &self,
543        fd: std::os::unix::io::OwnedFd,
544        desc: &crate::TextureDescriptor,
545        drm_modifier: u64,
546        stride: u64,
547        offset: u64,
548    ) -> Result<super::Texture, crate::DeviceError> {
549        use std::os::unix::io::IntoRawFd;
550
551        if !self
552            .shared
553            .features
554            .contains(wgt::Features::VULKAN_EXTERNAL_MEMORY_DMA_BUF)
555        {
556            log::error!(
557                "Vulkan driver does not support VK_EXT_external_memory_dma_buf \
558                 or VK_EXT_image_drm_format_modifier"
559            );
560            return Err(crate::DeviceError::Unexpected);
561        }
562
563        let external_memory_fd_fn = self
564            .shared
565            .extension_fns
566            .external_memory_fd
567            .as_ref()
568            .ok_or_else(|| {
569                log::error!("VK_KHR_external_memory_fd extension not loaded");
570                crate::DeviceError::Unexpected
571            })?;
572
573        let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::default()
574            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
575
576        let plane_layout = vk::SubresourceLayout {
577            offset,
578            row_pitch: stride,
579            size: 0,
580            array_pitch: 0,
581            depth_pitch: 0,
582        };
583        let mut drm_modifier_info = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()
584            .drm_format_modifier(drm_modifier)
585            .plane_layouts(core::slice::from_ref(&plane_layout));
586
587        let image = self.create_image_without_memory_with_tiling(
588            desc,
589            vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT,
590            Some(&mut external_memory_image_info),
591            Some(&mut drm_modifier_info),
592        )?;
593
594        // Convert to raw fd. We must close it ourselves if any operation below
595        // fails, since Vulkan only takes ownership on successful vkAllocateMemory.
596        let fd_raw = fd.into_raw_fd();
597
598        let result = self.import_dmabuf_memory(
599            external_memory_fd_fn,
600            fd_raw,
601            image.raw,
602            &image.requirements,
603        );
604
605        match result {
606            Ok(memory) => Ok(unsafe {
607                self.texture_from_raw(
608                    image.raw,
609                    desc,
610                    None,
611                    super::TextureMemory::Dedicated(memory),
612                )
613            }),
614            Err(e) => {
615                // Clean up the VkImage on failure.
616                unsafe { self.shared.raw.destroy_image(image.raw, None) };
617                Err(e)
618            }
619        }
620    }
621
622    /// Import DMA-buf memory and bind it to the image.
623    ///
624    /// On failure, the raw fd is closed (if not yet consumed by Vulkan) and the
625    /// caller is responsible for destroying the VkImage.
626    #[cfg(unix)]
627    fn import_dmabuf_memory(
628        &self,
629        external_memory_fd_fn: &ash::khr::external_memory_fd::Device,
630        fd_raw: i32,
631        image: vk::Image,
632        requirements: &vk::MemoryRequirements,
633    ) -> Result<vk::DeviceMemory, crate::DeviceError> {
634        let mut fd_props = vk::MemoryFdPropertiesKHR::default();
635        unsafe {
636            external_memory_fd_fn.get_memory_fd_properties(
637                vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
638                fd_raw,
639                &mut fd_props,
640            )
641        }
642        .map_err(|e| {
643            unsafe { libc::close(fd_raw) };
644            super::map_host_device_oom_err(e)
645        })?;
646
647        let mem_type_index = self
648            .find_memory_type_index(
649                requirements.memory_type_bits & fd_props.memory_type_bits,
650                vk::MemoryPropertyFlags::empty(),
651            )
652            .ok_or_else(|| {
653                unsafe { libc::close(fd_raw) };
654                crate::DeviceError::Unexpected
655            })?;
656
657        let mut dedicated_allocate_info = vk::MemoryDedicatedAllocateInfo::default().image(image);
658
659        let mut import_memory_info = vk::ImportMemoryFdInfoKHR::default()
660            .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
661            .fd(fd_raw);
662
663        let memory_allocate_info = vk::MemoryAllocateInfo::default()
664            .allocation_size(requirements.size)
665            .memory_type_index(mem_type_index as _)
666            .push_next(&mut import_memory_info)
667            .push_next(&mut dedicated_allocate_info);
668
669        // vkAllocateMemory takes ownership of the fd on success.
670        // On failure, the fd is NOT consumed and we must close it.
671        let memory = unsafe { self.shared.raw.allocate_memory(&memory_allocate_info, None) }
672            .map_err(|e| {
673                unsafe { libc::close(fd_raw) };
674                super::map_host_device_oom_err(e)
675            })?;
676
677        // From this point, the fd is consumed. Only VkDeviceMemory needs cleanup on error.
678        unsafe { self.shared.raw.bind_image_memory(image, memory, 0) }.map_err(|e| {
679            unsafe { self.shared.raw.free_memory(memory, None) };
680            super::map_host_device_oom_err(e)
681        })?;
682
683        Ok(memory)
684    }
685
686    fn create_shader_module_impl(
687        &self,
688        spv: &[u32],
689        label: &crate::Label<'_>,
690    ) -> Result<vk::ShaderModule, crate::DeviceError> {
691        let vk_info = vk::ShaderModuleCreateInfo::default()
692            .flags(vk::ShaderModuleCreateFlags::empty())
693            .code(spv);
694
695        let raw = unsafe {
696            profiling::scope!("vkCreateShaderModule");
697            self.shared
698                .raw
699                .create_shader_module(&vk_info, None)
700                .map_err(map_err)?
701        };
702        fn map_err(err: vk::Result) -> crate::DeviceError {
703            // We don't use VK_NV_glsl_shader
704            // VK_ERROR_INVALID_SHADER_NV
705            super::map_host_device_oom_err(err)
706        }
707
708        if let Some(label) = label {
709            unsafe { self.shared.set_object_name(raw, label) };
710        }
711
712        Ok(raw)
713    }
714
715    fn compile_stage(
716        &self,
717        stage: &crate::ProgrammableStage<super::ShaderModule>,
718        naga_stage: naga::ShaderStage,
719        binding_map: &naga::back::spv::BindingMap,
720    ) -> Result<CompiledStage, crate::PipelineError> {
721        let stage_flags = crate::auxil::map_naga_stage(naga_stage);
722        let vk_module = match *stage.module {
723            super::ShaderModule::Raw(raw) => raw,
724            super::ShaderModule::Intermediate {
725                ref naga_shader,
726                runtime_checks,
727            } => {
728                let pipeline_options = naga::back::spv::PipelineOptions {
729                    entry_point: stage.entry_point.to_owned(),
730                    shader_stage: naga_stage,
731                };
732                let needs_temp_options = !runtime_checks.bounds_checks
733                    || !runtime_checks.force_loop_bounding
734                    || !runtime_checks.ray_query_initialization_tracking
735                    || !binding_map.is_empty()
736                    || naga_shader.debug_source.is_some()
737                    || !stage.zero_initialize_workgroup_memory
738                    || !runtime_checks.task_shader_dispatch_tracking
739                    || !runtime_checks.mesh_shader_primitive_indices_clamp
740                    || !runtime_checks.int_div_checks;
741
742                let mut temp_options;
743                let options = if needs_temp_options {
744                    temp_options = self.naga_options.clone();
745                    if !runtime_checks.bounds_checks {
746                        temp_options.bounds_check_policies = naga::proc::BoundsCheckPolicies {
747                            index: naga::proc::BoundsCheckPolicy::Unchecked,
748                            buffer: naga::proc::BoundsCheckPolicy::Unchecked,
749                            image_load: naga::proc::BoundsCheckPolicy::Unchecked,
750                            binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
751                        };
752                    }
753                    if !runtime_checks.force_loop_bounding {
754                        temp_options.force_loop_bounding = false;
755                    }
756                    if !runtime_checks.ray_query_initialization_tracking {
757                        temp_options.ray_query_initialization_tracking = false;
758                    }
759                    if !binding_map.is_empty() {
760                        temp_options.binding_map = binding_map.clone();
761                    }
762
763                    if let Some(ref debug) = naga_shader.debug_source {
764                        temp_options.debug_info = Some(naga::back::spv::DebugInfo {
765                            source_code: &debug.source_code,
766                            file_name: debug.file_name.as_ref(),
767                            language: naga::back::spv::SourceLanguage::WGSL,
768                        })
769                    }
770                    if !stage.zero_initialize_workgroup_memory {
771                        temp_options.zero_initialize_workgroup_memory =
772                            naga::back::spv::ZeroInitializeWorkgroupMemoryMode::None;
773                    }
774                    if !runtime_checks.task_shader_dispatch_tracking {
775                        temp_options.task_dispatch_limits = None;
776                    }
777                    temp_options.mesh_shader_primitive_indices_clamp =
778                        runtime_checks.mesh_shader_primitive_indices_clamp;
779                    temp_options.emit_int_div_checks = runtime_checks.int_div_checks;
780
781                    &temp_options
782                } else {
783                    &self.naga_options
784                };
785
786                let (module, info) = naga::back::pipeline_constants::process_overrides(
787                    &naga_shader.module,
788                    &naga_shader.info,
789                    Some((naga_stage, stage.entry_point)),
790                    stage.constants,
791                )
792                .map_err(|e| {
793                    crate::PipelineError::PipelineConstants(stage_flags, format!("{e}"))
794                })?;
795
796                let spv = {
797                    profiling::scope!("naga::spv::write_vec");
798                    naga::back::spv::write_vec(&module, &info, options, Some(&pipeline_options))
799                }
800                .map_err(|e| crate::PipelineError::Linkage(stage_flags, format!("{e}")))?;
801                self.create_shader_module_impl(&spv, &None)?
802            }
803        };
804
805        let mut flags = vk::PipelineShaderStageCreateFlags::empty();
806        if self.shared.features.contains(wgt::Features::SUBGROUP) {
807            flags |= vk::PipelineShaderStageCreateFlags::ALLOW_VARYING_SUBGROUP_SIZE
808        }
809
810        let entry_point = CString::new(stage.entry_point).unwrap();
811        let mut create_info = vk::PipelineShaderStageCreateInfo::default()
812            .flags(flags)
813            .stage(conv::map_shader_stage(stage_flags))
814            .module(vk_module);
815
816        // Circumvent struct lifetime check because of a self-reference inside CompiledStage
817        create_info.p_name = entry_point.as_ptr();
818
819        Ok(CompiledStage {
820            create_info,
821            _entry_point: entry_point,
822            temp_raw_module: match *stage.module {
823                super::ShaderModule::Raw(_) => None,
824                super::ShaderModule::Intermediate { .. } => Some(vk_module),
825            },
826        })
827    }
828
829    /// Returns the queue family index of the device's internal queue.
830    ///
831    /// This is useful for constructing memory barriers needed for queue family ownership transfer when
832    /// external memory is involved (from/to `VK_QUEUE_FAMILY_EXTERNAL_KHR` and `VK_QUEUE_FAMILY_FOREIGN_EXT`
833    /// for example).
834    pub fn queue_family_index(&self) -> u32 {
835        self.shared.family_index
836    }
837
838    pub fn queue_index(&self) -> u32 {
839        self.shared.queue_index
840    }
841
842    pub fn raw_device(&self) -> &ash::Device {
843        &self.shared.raw
844    }
845
846    pub fn raw_physical_device(&self) -> vk::PhysicalDevice {
847        self.shared.physical_device
848    }
849
850    pub fn raw_queue(&self) -> vk::Queue {
851        self.shared.raw_queue
852    }
853
854    pub fn enabled_device_extensions(&self) -> &[&'static CStr] {
855        &self.shared.enabled_extensions
856    }
857
858    pub fn shared_instance(&self) -> &super::InstanceShared {
859        &self.shared.instance
860    }
861
862    fn get_memory_properties(&self) -> Option<MemoryProperties> {
863        if !self
864            .shared
865            .enabled_extensions
866            .contains(&ext::memory_budget::NAME)
867        {
868            return None;
869        }
870
871        let get_physical_device_properties = self
872            .shared
873            .instance
874            .get_physical_device_properties
875            .as_ref()
876            .unwrap();
877
878        let mut budget = vk::PhysicalDeviceMemoryBudgetPropertiesEXT::default();
879        let mut props = vk::PhysicalDeviceMemoryProperties2::default().push_next(&mut budget);
880
881        unsafe {
882            get_physical_device_properties
883                .get_physical_device_memory_properties2(self.shared.physical_device, &mut props);
884        }
885
886        let vk::PhysicalDeviceMemoryProperties2 {
887            memory_properties, ..
888        } = props;
889
890        let mut heap_budget = ArrayVec::from(budget.heap_budget);
891        let mut heap_usage = ArrayVec::from(budget.heap_usage);
892        heap_budget.truncate(memory_properties.memory_heap_count as usize);
893        heap_usage.truncate(memory_properties.memory_heap_count as usize);
894
895        Some(MemoryProperties {
896            base: memory_properties,
897            heap_budget,
898            heap_usage,
899        })
900    }
901
902    /// Predict whether a proposed allocation will result in an OOM condition.
903    ///
904    /// If so, returns `Err(crate::DeviceError::OutOfMemory)`. If not, returns
905    /// `Ok(())`.
906    ///
907    /// The prediction quality depends on accurately selecting the heap that
908    /// [`gpu_allocator`] will use for the allocation, and is subject to
909    /// deteriorate if the logic in [`gpu_allocator`] changes.
910    fn error_if_would_oom_on_resource_allocation(
911        &self,
912        location: gpu_allocator::MemoryLocation,
913        requirements: &vk::MemoryRequirements,
914    ) -> Result<(), crate::DeviceError> {
915        use gpu_allocator::MemoryLocation;
916
917        let Some(threshold) = self
918            .shared
919            .instance
920            .memory_budget_thresholds
921            .for_resource_creation
922        else {
923            return Ok(());
924        };
925
926        let Some(memory_properties) = self.get_memory_properties() else {
927            return Ok(());
928        };
929
930        let preferred_flags = match location {
931            MemoryLocation::GpuOnly => vk::MemoryPropertyFlags::DEVICE_LOCAL,
932            MemoryLocation::CpuToGpu => {
933                vk::MemoryPropertyFlags::HOST_VISIBLE
934                    | vk::MemoryPropertyFlags::HOST_COHERENT
935                    | vk::MemoryPropertyFlags::DEVICE_LOCAL
936            }
937            MemoryLocation::GpuToCpu => {
938                vk::MemoryPropertyFlags::HOST_VISIBLE
939                    | vk::MemoryPropertyFlags::HOST_COHERENT
940                    | vk::MemoryPropertyFlags::HOST_CACHED
941            }
942            MemoryLocation::Unknown => vk::MemoryPropertyFlags::empty(),
943        };
944
945        let mut selected_heap = memory_properties
946            .types()
947            .iter()
948            .enumerate()
949            .find(|(i, ty)| {
950                (1 << i) & requirements.memory_type_bits != 0
951                    && ty.property_flags.contains(preferred_flags)
952            });
953
954        if selected_heap.is_none() {
955            let required_flags = match location {
956                MemoryLocation::GpuOnly => vk::MemoryPropertyFlags::DEVICE_LOCAL,
957                MemoryLocation::CpuToGpu | MemoryLocation::GpuToCpu => {
958                    vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT
959                }
960                MemoryLocation::Unknown => vk::MemoryPropertyFlags::empty(),
961            };
962            selected_heap = memory_properties
963                .types()
964                .iter()
965                .enumerate()
966                .find(|(i, ty)| {
967                    (1 << i) & requirements.memory_type_bits != 0
968                        && ty.property_flags.contains(required_flags)
969                });
970        }
971
972        if let Some((_, ty)) = selected_heap {
973            let i = ty.heap_index as usize;
974            let heap_usage = memory_properties.heap_usage()[i];
975            let heap_budget = memory_properties.heap_budget()[i];
976            if heap_usage + requirements.size < heap_budget / 100 * threshold as u64 {
977                Ok(())
978            } else {
979                log::warn!(
980                    "Allocation would result in an OOM condition\n\
981                    Request: {requirements:?}\n\
982                    Heap {index} had {heap_usage}B used of {heap_budget}B total before this request.",
983                    index = ty.heap_index,
984                );
985                Err(crate::DeviceError::OutOfMemory)
986            }
987        } else {
988            log::warn!("Failed to find a suitable heap for {requirements:?}");
989            Err(crate::DeviceError::OutOfMemory)
990        }
991    }
992}
993
994impl crate::Device for super::Device {
995    type A = super::Api;
996
997    unsafe fn create_buffer(
998        &self,
999        desc: &crate::BufferDescriptor,
1000    ) -> Result<(super::Buffer, wgt::BufferAddress), crate::DeviceError> {
1001        let vk_info = vk::BufferCreateInfo::default()
1002            .size(desc.size)
1003            .usage(conv::map_buffer_usage(desc.usage))
1004            .sharing_mode(vk::SharingMode::EXCLUSIVE);
1005
1006        let raw = unsafe {
1007            self.shared
1008                .raw
1009                .create_buffer(&vk_info, None)
1010                .map_err(super::map_host_device_oom_and_ioca_err)?
1011        };
1012
1013        let mut requirements = unsafe { self.shared.raw.get_buffer_memory_requirements(raw) };
1014
1015        let is_cpu_read = desc.usage.contains(wgt::BufferUses::MAP_READ);
1016        let is_cpu_write = desc.usage.contains(wgt::BufferUses::MAP_WRITE);
1017
1018        let location = match (is_cpu_read, is_cpu_write) {
1019            (true, true) => gpu_allocator::MemoryLocation::CpuToGpu,
1020            (true, false) => gpu_allocator::MemoryLocation::GpuToCpu,
1021            (false, true) => gpu_allocator::MemoryLocation::CpuToGpu,
1022            (false, false) => gpu_allocator::MemoryLocation::GpuOnly,
1023        };
1024
1025        self.error_if_would_oom_on_resource_allocation(location, &requirements)
1026            .inspect_err(|_| {
1027                unsafe { self.shared.raw.destroy_buffer(raw, None) };
1028            })?;
1029
1030        let name = desc.label.unwrap_or("Unlabeled buffer");
1031
1032        if desc
1033            .usage
1034            .contains(wgt::BufferUses::ACCELERATION_STRUCTURE_SCRATCH)
1035        {
1036            // There is no way to specify this usage to Vulkan so we must make sure the alignment requirement is large enough.
1037            requirements.alignment = requirements
1038                .alignment
1039                .max(self.shared.private_caps.scratch_buffer_alignment as u64);
1040        }
1041
1042        let allocation = self
1043            .mem_allocator
1044            .lock()
1045            .allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
1046                name,
1047                requirements: vk::MemoryRequirements {
1048                    memory_type_bits: requirements.memory_type_bits & self.valid_ash_memory_types,
1049                    ..requirements
1050                },
1051                location,
1052                linear: true, // Buffers are always linear
1053                allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
1054            })
1055            .inspect_err(|_| {
1056                unsafe { self.shared.raw.destroy_buffer(raw, None) };
1057            })?;
1058
1059        unsafe {
1060            self.shared
1061                .raw
1062                .bind_buffer_memory(raw, allocation.memory(), allocation.offset())
1063        }
1064        .map_err(super::map_host_device_oom_and_ioca_err)
1065        .inspect_err(|_| {
1066            unsafe { self.shared.raw.destroy_buffer(raw, None) };
1067        })?;
1068
1069        if let Some(label) = desc.label {
1070            unsafe { self.shared.set_object_name(raw, label) };
1071        }
1072
1073        self.counters.buffer_memory.add(allocation.size() as isize);
1074        self.counters.buffers.add(1);
1075
1076        Ok((
1077            super::Buffer {
1078                raw,
1079                ownership: super::BufferOwnership::Managed(Mutex::new(
1080                    super::BufferMemoryBacking::Managed(allocation),
1081                )),
1082            },
1083            desc.size,
1084        ))
1085    }
1086    unsafe fn destroy_buffer(&self, buffer: super::Buffer) {
1087        match buffer.ownership {
1088            super::BufferOwnership::Managed(allocation) => {
1089                unsafe { self.shared.raw.destroy_buffer(buffer.raw, None) };
1090                let allocation = allocation.into_inner();
1091                self.counters.buffer_memory.sub(allocation.size() as isize);
1092                match allocation {
1093                    super::BufferMemoryBacking::Managed(allocation) => {
1094                        let result = self.mem_allocator.lock().free(allocation);
1095                        if let Err(err) = result {
1096                            log::warn!("Failed to free buffer allocation: {err}");
1097                        }
1098                    }
1099                    super::BufferMemoryBacking::VulkanMemory { memory, .. } => unsafe {
1100                        self.shared.raw.free_memory(memory, None);
1101                    },
1102                }
1103            }
1104            super::BufferOwnership::RawHandle => {
1105                unsafe { self.shared.raw.destroy_buffer(buffer.raw, None) };
1106            }
1107            super::BufferOwnership::External(_drop_guard) => {
1108                // The caller owns the `vk::Buffer` and its memory. Dropping
1109                // `_drop_guard` at the end of this arm runs the cleanup callback.
1110            }
1111        }
1112
1113        self.counters.buffers.sub(1);
1114    }
1115
1116    unsafe fn add_raw_buffer(&self, _buffer: &super::Buffer) {
1117        self.counters.buffers.add(1);
1118    }
1119
1120    unsafe fn map_buffer(
1121        &self,
1122        buffer: &super::Buffer,
1123        range: crate::MemoryRange,
1124    ) -> Result<crate::BufferMapping, crate::DeviceError> {
1125        let super::BufferOwnership::Managed(ref allocation) = buffer.ownership else {
1126            crate::hal_usage_error("tried to map external buffer")
1127        };
1128        let mut allocation = allocation.lock();
1129        let super::BufferMemoryBacking::Managed(ref mut allocation) = *allocation else {
1130            crate::hal_usage_error("tried to map externally created buffer")
1131        };
1132        let is_coherent = allocation
1133            .memory_properties()
1134            .contains(vk::MemoryPropertyFlags::HOST_COHERENT);
1135        Ok(crate::BufferMapping {
1136            ptr: unsafe {
1137                allocation
1138                    .mapped_ptr()
1139                    .unwrap()
1140                    .cast()
1141                    .offset(range.start as isize)
1142            },
1143            is_coherent,
1144        })
1145    }
1146
1147    unsafe fn unmap_buffer(&self, buffer: &super::Buffer) {
1148        match buffer.ownership {
1149            super::BufferOwnership::Managed(_) => {
1150                // gpu-allocator maps the buffer when allocated and unmaps it when free'd
1151            }
1152            super::BufferOwnership::RawHandle | super::BufferOwnership::External(_) => {
1153                crate::hal_usage_error("tried to unmap external buffer")
1154            }
1155        }
1156    }
1157
1158    unsafe fn flush_mapped_ranges<I>(&self, buffer: &super::Buffer, ranges: I)
1159    where
1160        I: Iterator<Item = crate::MemoryRange>,
1161    {
1162        if let Some(vk_ranges) = self.shared.make_memory_ranges(buffer, ranges) {
1163            unsafe {
1164                self.shared
1165                    .raw
1166                    .flush_mapped_memory_ranges(
1167                        &smallvec::SmallVec::<[vk::MappedMemoryRange; 32]>::from_iter(vk_ranges),
1168                    )
1169            }
1170            .unwrap();
1171        }
1172    }
1173    unsafe fn invalidate_mapped_ranges<I>(&self, buffer: &super::Buffer, ranges: I)
1174    where
1175        I: Iterator<Item = crate::MemoryRange>,
1176    {
1177        if let Some(vk_ranges) = self.shared.make_memory_ranges(buffer, ranges) {
1178            unsafe {
1179                self.shared
1180                    .raw
1181                    .invalidate_mapped_memory_ranges(&smallvec::SmallVec::<
1182                        [vk::MappedMemoryRange; 32],
1183                    >::from_iter(vk_ranges))
1184            }
1185            .unwrap();
1186        }
1187    }
1188
1189    unsafe fn create_texture(
1190        &self,
1191        desc: &crate::TextureDescriptor,
1192    ) -> Result<super::Texture, crate::DeviceError> {
1193        let image = self.create_image_without_memory(desc, None)?;
1194
1195        self.error_if_would_oom_on_resource_allocation(
1196            gpu_allocator::MemoryLocation::GpuOnly,
1197            &image.requirements,
1198        )
1199        .inspect_err(|_| {
1200            unsafe { self.shared.raw.destroy_image(image.raw, None) };
1201        })?;
1202
1203        let name = desc.label.unwrap_or("Unlabeled texture");
1204
1205        let allocation = self
1206            .mem_allocator
1207            .lock()
1208            .allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
1209                name,
1210                requirements: vk::MemoryRequirements {
1211                    memory_type_bits: image.requirements.memory_type_bits
1212                        & self.valid_ash_memory_types,
1213                    ..image.requirements
1214                },
1215                location: gpu_allocator::MemoryLocation::GpuOnly,
1216                linear: false,
1217                allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
1218            })
1219            .inspect_err(|_| {
1220                unsafe { self.shared.raw.destroy_image(image.raw, None) };
1221            })?;
1222
1223        unsafe {
1224            self.shared
1225                .raw
1226                .bind_image_memory(image.raw, allocation.memory(), allocation.offset())
1227        }
1228        .map_err(super::map_host_device_oom_err)
1229        .inspect_err(|_| {
1230            unsafe { self.shared.raw.destroy_image(image.raw, None) };
1231        })?;
1232
1233        self.counters.texture_memory.add(allocation.size() as isize);
1234        self.counters.textures.add(1);
1235
1236        Ok(unsafe {
1237            self.texture_from_raw(
1238                image.raw,
1239                desc,
1240                None,
1241                super::TextureMemory::Allocation(allocation),
1242            )
1243        })
1244    }
1245
1246    unsafe fn destroy_texture(&self, texture: super::Texture) {
1247        if texture.drop_guard.is_none() {
1248            unsafe { self.shared.raw.destroy_image(texture.raw, None) };
1249        }
1250
1251        match texture.memory {
1252            super::TextureMemory::Allocation(allocation) => {
1253                self.counters.texture_memory.sub(allocation.size() as isize);
1254                let result = self.mem_allocator.lock().free(allocation);
1255                if let Err(err) = result {
1256                    log::warn!("Failed to free texture allocation: {err}");
1257                }
1258            }
1259            super::TextureMemory::Dedicated(memory) => unsafe {
1260                self.shared.raw.free_memory(memory, None);
1261            },
1262            super::TextureMemory::External => {}
1263        }
1264
1265        self.counters.textures.sub(1);
1266    }
1267
1268    unsafe fn add_raw_texture(&self, _texture: &super::Texture) {
1269        self.counters.textures.add(1);
1270    }
1271
1272    unsafe fn create_texture_view(
1273        &self,
1274        texture: &super::Texture,
1275        desc: &crate::TextureViewDescriptor,
1276    ) -> Result<super::TextureView, crate::DeviceError> {
1277        let mut swizzle = desc.swizzle;
1278
1279        // https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#textures-component-swizzle
1280        // If the image view has a depth/stencil format and the VkComponentSwizzle is VK_COMPONENT_SWIZZLE_ONE,
1281        // and VkPhysicalDeviceMaintenance5Properties::depthStencilSwizzleOneSupport is not VK_TRUE,
1282        // the value of the texel after swizzle is undefined.
1283        //
1284        // We convert `One` to `A` which should sample as 1.0,
1285        // according to https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#images-component-substitution
1286        if texture.format.is_depth_stencil_format()
1287            && !self.shared.private_caps.depth_stencil_swizzle_one_support
1288        {
1289            for component in [
1290                &mut swizzle.r,
1291                &mut swizzle.g,
1292                &mut swizzle.b,
1293                &mut swizzle.a,
1294            ] {
1295                if *component == wgt::ComponentSwizzle::One {
1296                    *component = wgt::ComponentSwizzle::A;
1297                }
1298            }
1299        }
1300
1301        let subresource_range = conv::map_subresource_range(&desc.range, texture.format);
1302        let raw_format = self.shared.private_caps.map_texture_format(desc.format);
1303        let mut vk_info = vk::ImageViewCreateInfo::default()
1304            .flags(vk::ImageViewCreateFlags::empty())
1305            .image(texture.raw)
1306            .view_type(conv::map_view_dimension(desc.dimension))
1307            .format(raw_format)
1308            .subresource_range(subresource_range)
1309            .components(conv::map_texture_component_swizzle(swizzle));
1310        let layers =
1311            NonZeroU32::new(subresource_range.layer_count).expect("Unexpected zero layer count");
1312
1313        let mut image_view_info;
1314        if self.shared.private_caps.image_view_usage && !desc.usage.is_empty() {
1315            image_view_info =
1316                vk::ImageViewUsageCreateInfo::default().usage(conv::map_texture_usage(desc.usage));
1317            vk_info = vk_info.push_next(&mut image_view_info);
1318        }
1319
1320        let raw = unsafe { self.shared.raw.create_image_view(&vk_info, None) }
1321            .map_err(super::map_host_device_oom_and_ioca_err)?;
1322
1323        if let Some(label) = desc.label {
1324            unsafe { self.shared.set_object_name(raw, label) };
1325        }
1326
1327        let identity = self.shared.texture_view_identity_factory.next();
1328
1329        self.counters.texture_views.add(1);
1330
1331        Ok(super::TextureView {
1332            raw_texture: texture.raw,
1333            raw,
1334            _layers: layers,
1335            format: desc.format,
1336            raw_format,
1337            base_mip_level: desc.range.base_mip_level,
1338            dimension: desc.dimension,
1339            texture_identity: texture.identity,
1340            view_identity: identity,
1341        })
1342    }
1343    unsafe fn destroy_texture_view(&self, view: super::TextureView) {
1344        unsafe { self.shared.raw.destroy_image_view(view.raw, None) };
1345
1346        self.counters.texture_views.sub(1);
1347    }
1348
1349    unsafe fn create_sampler(
1350        &self,
1351        desc: &crate::SamplerDescriptor,
1352    ) -> Result<super::Sampler, crate::DeviceError> {
1353        let mut create_info = vk::SamplerCreateInfo::default()
1354            .flags(vk::SamplerCreateFlags::empty())
1355            .mag_filter(conv::map_filter_mode(desc.mag_filter))
1356            .min_filter(conv::map_filter_mode(desc.min_filter))
1357            .mipmap_mode(conv::map_mip_filter_mode(desc.mipmap_filter))
1358            .address_mode_u(conv::map_address_mode(desc.address_modes[0]))
1359            .address_mode_v(conv::map_address_mode(desc.address_modes[1]))
1360            .address_mode_w(conv::map_address_mode(desc.address_modes[2]))
1361            .min_lod(desc.lod_clamp.start)
1362            .max_lod(desc.lod_clamp.end);
1363
1364        if let Some(fun) = desc.compare {
1365            create_info = create_info
1366                .compare_enable(true)
1367                .compare_op(conv::map_comparison(fun));
1368        }
1369
1370        if desc.anisotropy_clamp != 1 {
1371            // We only enable anisotropy if it is supported, and wgpu-hal interface guarantees
1372            // the clamp is in the range [1, 16] which is always supported if anisotropy is.
1373            create_info = create_info
1374                .anisotropy_enable(true)
1375                .max_anisotropy(desc.anisotropy_clamp as f32);
1376        }
1377
1378        if let Some(color) = desc.border_color {
1379            create_info = create_info.border_color(conv::map_border_color(color));
1380        }
1381
1382        let mut sampler_cache_guard = self.shared.sampler_cache.lock();
1383
1384        let raw = sampler_cache_guard.create_sampler(&self.shared.raw, create_info)?;
1385
1386        // Note: Cached samplers will just continually overwrite the label
1387        //
1388        // https://github.com/gfx-rs/wgpu/issues/6867
1389        if let Some(label) = desc.label {
1390            // SAFETY: we are holding a lock on the sampler cache,
1391            // so we can only be setting the name from one thread.
1392            unsafe { self.shared.set_object_name(raw, label) };
1393        }
1394
1395        drop(sampler_cache_guard);
1396
1397        self.counters.samplers.add(1);
1398
1399        Ok(super::Sampler { raw, create_info })
1400    }
1401    unsafe fn destroy_sampler(&self, sampler: super::Sampler) {
1402        self.shared.sampler_cache.lock().destroy_sampler(
1403            &self.shared.raw,
1404            sampler.create_info,
1405            sampler.raw,
1406        );
1407
1408        self.counters.samplers.sub(1);
1409    }
1410
1411    unsafe fn create_command_encoder(
1412        &self,
1413        desc: &crate::CommandEncoderDescriptor<super::Queue>,
1414    ) -> Result<super::CommandEncoder, crate::DeviceError> {
1415        let vk_info = vk::CommandPoolCreateInfo::default()
1416            .queue_family_index(desc.queue.family_index)
1417            .flags(vk::CommandPoolCreateFlags::TRANSIENT);
1418
1419        let raw = unsafe {
1420            self.shared
1421                .raw
1422                .create_command_pool(&vk_info, None)
1423                .map_err(super::map_host_device_oom_err)?
1424        };
1425
1426        self.counters.command_encoders.add(1);
1427
1428        Ok(super::CommandEncoder {
1429            raw,
1430            device: Arc::clone(&self.shared),
1431            active: vk::CommandBuffer::null(),
1432            bind_point: vk::PipelineBindPoint::default(),
1433            temp: super::Temp::default(),
1434            free: Vec::new(),
1435            discarded: Vec::new(),
1436            rpass_debug_marker_active: false,
1437            end_of_pass_timer_query: None,
1438            framebuffers: Default::default(),
1439            temp_texture_views: Default::default(),
1440            counters: Arc::clone(&self.counters),
1441            current_pipeline_is_multiview: false,
1442        })
1443    }
1444
1445    unsafe fn create_bind_group_layout(
1446        &self,
1447        desc: &crate::BindGroupLayoutDescriptor,
1448    ) -> Result<super::BindGroupLayout, crate::DeviceError> {
1449        // Iterate through the entries and accumulate our Vulkan
1450        // DescriptorSetLayoutBindings and DescriptorBindingFlags, as well as
1451        // our binding map and our descriptor counts.
1452        // Note: not bothering with on stack arrays here as it's low frequency
1453        let mut vk_bindings = Vec::new();
1454        let mut binding_flags = Vec::new();
1455        let mut binding_map = Vec::new();
1456        let mut next_binding = 0;
1457        let mut contains_binding_arrays = false;
1458        let mut desc_count = DescriptorCounts::default();
1459        for entry in desc.entries {
1460            if entry.count.is_some() {
1461                contains_binding_arrays = true;
1462            }
1463
1464            let partially_bound = desc
1465                .flags
1466                .contains(crate::BindGroupLayoutFlags::PARTIALLY_BOUND);
1467            let mut flags = vk::DescriptorBindingFlags::empty();
1468            if partially_bound && entry.count.is_some() {
1469                flags |= vk::DescriptorBindingFlags::PARTIALLY_BOUND;
1470            }
1471            if entry.count.is_some() {
1472                flags |= vk::DescriptorBindingFlags::UPDATE_AFTER_BIND;
1473            }
1474
1475            let count = entry.count.map_or(1, |c| c.get());
1476            match entry.ty {
1477                wgt::BindingType::ExternalTexture => unimplemented!(),
1478                _ => {
1479                    vk_bindings.push(vk::DescriptorSetLayoutBinding {
1480                        binding: next_binding,
1481                        descriptor_type: conv::map_binding_type(entry.ty),
1482                        descriptor_count: count,
1483                        stage_flags: conv::map_shader_stage(entry.visibility),
1484                        p_immutable_samplers: ptr::null(),
1485                        _marker: Default::default(),
1486                    });
1487                    binding_flags.push(flags);
1488                    binding_map.push((
1489                        entry.binding,
1490                        super::BindingInfo {
1491                            binding: next_binding,
1492                            binding_array_size: entry.count,
1493                        },
1494                    ));
1495                    next_binding += 1;
1496                }
1497            }
1498
1499            match entry.ty {
1500                wgt::BindingType::Buffer {
1501                    ty,
1502                    has_dynamic_offset,
1503                    ..
1504                } => match ty {
1505                    wgt::BufferBindingType::Uniform => {
1506                        if has_dynamic_offset {
1507                            desc_count.uniform_buffer_dynamic += count;
1508                        } else {
1509                            desc_count.uniform_buffer += count;
1510                        }
1511                    }
1512                    wgt::BufferBindingType::Storage { .. } => {
1513                        if has_dynamic_offset {
1514                            desc_count.storage_buffer_dynamic += count;
1515                        } else {
1516                            desc_count.storage_buffer += count;
1517                        }
1518                    }
1519                },
1520                wgt::BindingType::Sampler { .. } => {
1521                    desc_count.sampler += count;
1522                }
1523                wgt::BindingType::Texture { .. } => {
1524                    desc_count.sampled_image += count;
1525                }
1526                wgt::BindingType::StorageTexture { .. } => {
1527                    desc_count.storage_image += count;
1528                }
1529                wgt::BindingType::AccelerationStructure { .. } => {
1530                    desc_count.acceleration_structure += count;
1531                }
1532                wgt::BindingType::ExternalTexture => unimplemented!(),
1533            }
1534        }
1535
1536        let vk_info = vk::DescriptorSetLayoutCreateInfo::default()
1537            .bindings(&vk_bindings)
1538            .flags(if contains_binding_arrays {
1539                vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL
1540            } else {
1541                vk::DescriptorSetLayoutCreateFlags::empty()
1542            });
1543
1544        let mut binding_flag_info =
1545            vk::DescriptorSetLayoutBindingFlagsCreateInfo::default().binding_flags(&binding_flags);
1546
1547        let vk_info = vk_info.push_next(&mut binding_flag_info);
1548
1549        let raw = unsafe {
1550            self.shared
1551                .raw
1552                .create_descriptor_set_layout(&vk_info, None)
1553                .map_err(super::map_host_device_oom_err)?
1554        };
1555
1556        if let Some(label) = desc.label {
1557            unsafe { self.shared.set_object_name(raw, label) };
1558        }
1559
1560        let layout = super::BindGroupLayout {
1561            raw,
1562            desc_count,
1563            entries: desc.entries.into(),
1564            binding_map,
1565            contains_binding_arrays,
1566        };
1567
1568        let result = self
1569            .desc_allocator
1570            .lock()
1571            .register_layout(&self.shared.raw, &layout);
1572        if let Err(err) = result {
1573            unsafe {
1574                self.shared
1575                    .raw
1576                    .destroy_descriptor_set_layout(layout.raw, None)
1577            };
1578            return Err(err);
1579        }
1580
1581        self.counters.bind_group_layouts.add(1);
1582
1583        Ok(layout)
1584    }
1585    unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) {
1586        self.desc_allocator
1587            .lock()
1588            .unregister_layout(&self.shared.raw, &bg_layout);
1589
1590        unsafe {
1591            self.shared
1592                .raw
1593                .destroy_descriptor_set_layout(bg_layout.raw, None)
1594        };
1595
1596        self.counters.bind_group_layouts.sub(1);
1597    }
1598
1599    unsafe fn create_pipeline_layout(
1600        &self,
1601        desc: &crate::PipelineLayoutDescriptor<super::BindGroupLayout>,
1602    ) -> Result<super::PipelineLayout, crate::DeviceError> {
1603        //Note: not bothering with on stack array here as it's low frequency
1604        let vk_set_layouts = desc
1605            .bind_group_layouts
1606            .iter()
1607            .map(|bgl| match bgl {
1608                Some(bgl) => bgl.raw,
1609                None => {
1610                    // `VUID-VkPipelineLayoutCreateInfo-pSetLayouts-parameter`
1611                    // says `VK_NULL_HANDLE` is allowed but
1612                    // `VUID-VkPipelineLayoutCreateInfo-graphicsPipelineLibrary-06753`
1613                    // says it's not, unless the `graphicsPipelineLibrary`
1614                    // feature is enabled.
1615                    //
1616                    // We use an empty descriptor set layout to work around this.
1617                    self.shared.empty_descriptor_set_layout
1618                }
1619            })
1620            .collect::<Vec<_>>();
1621        let vk_immediates_ranges: Option<vk::PushConstantRange> = if desc.immediate_size != 0 {
1622            Some(vk::PushConstantRange {
1623                stage_flags: vk::ShaderStageFlags::ALL,
1624                offset: 0,
1625                size: desc.immediate_size,
1626            })
1627        } else {
1628            None
1629        };
1630
1631        let vk_info = vk::PipelineLayoutCreateInfo::default()
1632            .flags(vk::PipelineLayoutCreateFlags::empty())
1633            .set_layouts(&vk_set_layouts)
1634            .push_constant_ranges(vk_immediates_ranges.as_slice());
1635
1636        let raw = {
1637            profiling::scope!("vkCreatePipelineLayout");
1638            unsafe {
1639                self.shared
1640                    .raw
1641                    .create_pipeline_layout(&vk_info, None)
1642                    .map_err(super::map_host_device_oom_err)?
1643            }
1644        };
1645
1646        if let Some(label) = desc.label {
1647            unsafe { self.shared.set_object_name(raw, label) };
1648        }
1649
1650        let mut binding_map = BTreeMap::new();
1651        for (group, layout) in desc.bind_group_layouts.iter().enumerate() {
1652            let Some(layout) = layout else {
1653                continue;
1654            };
1655
1656            for &(binding, binding_info) in &layout.binding_map {
1657                binding_map.insert(
1658                    naga::ResourceBinding {
1659                        group: group as u32,
1660                        binding,
1661                    },
1662                    naga::back::spv::BindingInfo {
1663                        descriptor_set: group as u32,
1664                        binding: binding_info.binding,
1665                        binding_array_size: binding_info.binding_array_size.map(NonZeroU32::get),
1666                    },
1667                );
1668            }
1669        }
1670
1671        self.counters.pipeline_layouts.add(1);
1672        Ok(super::PipelineLayout { raw, binding_map })
1673    }
1674    unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) {
1675        unsafe {
1676            self.shared
1677                .raw
1678                .destroy_pipeline_layout(pipeline_layout.raw, None)
1679        };
1680
1681        self.counters.pipeline_layouts.sub(1);
1682    }
1683
1684    unsafe fn create_bind_group(
1685        &self,
1686        desc: &crate::BindGroupDescriptor<
1687            super::BindGroupLayout,
1688            super::Buffer,
1689            super::Sampler,
1690            super::TextureView,
1691            super::AccelerationStructure,
1692        >,
1693    ) -> Result<super::BindGroup, crate::DeviceError> {
1694        let set = unsafe {
1695            self.desc_allocator
1696                .lock()
1697                .alloc(&self.shared.raw, desc.layout)?
1698        };
1699
1700        if let Some(label) = desc.label {
1701            unsafe { self.shared.set_object_name(set.raw(), label) };
1702        }
1703
1704        /// Helper for splitting off and initializing a given number of elements on a pre-allocated
1705        /// stack, based on items returned from an [`ExactSizeIterator`].  Typically created from a
1706        /// [`MaybeUninit`] slice (see [`Vec::spare_capacity_mut()`]).
1707        /// The updated [`ExtensionStack`] of remaining uninitialized elements is returned, safely
1708        /// representing that the initialized and remaining elements are two independent mutable
1709        /// borrows.
1710        struct ExtendStack<'a, T> {
1711            remainder: &'a mut [MaybeUninit<T>],
1712        }
1713
1714        impl<'a, T> ExtendStack<'a, T> {
1715            fn from_vec_capacity(vec: &'a mut Vec<T>) -> Self {
1716                Self {
1717                    remainder: vec.spare_capacity_mut(),
1718                }
1719            }
1720
1721            fn extend_one(self, value: T) -> (Self, &'a mut T) {
1722                let (to_init, remainder) = self.remainder.split_first_mut().unwrap();
1723                let init = to_init.write(value);
1724                (Self { remainder }, init)
1725            }
1726
1727            fn extend(
1728                self,
1729                iter: impl IntoIterator<Item = T> + ExactSizeIterator,
1730            ) -> (Self, &'a mut [T]) {
1731                let (to_init, remainder) = self.remainder.split_at_mut(iter.len());
1732
1733                for (value, to_init) in iter.into_iter().zip(to_init.iter_mut()) {
1734                    to_init.write(value);
1735                }
1736
1737                // we can't use the safe (yet unstable) MaybeUninit::write_slice() here because of having an iterator to write
1738
1739                let init = {
1740                    // SAFETY: The loop above has initialized exactly as many items as to_init is
1741                    // long, so it is safe to cast away the MaybeUninit<T> wrapper into T.
1742
1743                    // Additional safety docs from unstable slice_assume_init_mut
1744                    // SAFETY: similar to safety notes for `slice_get_ref`, but we have a
1745                    // mutable reference which is also guaranteed to be valid for writes.
1746                    unsafe { mem::transmute::<&mut [MaybeUninit<T>], &mut [T]>(to_init) }
1747                };
1748                (Self { remainder }, init)
1749            }
1750        }
1751
1752        let mut writes = Vec::with_capacity(desc.entries.len());
1753        let mut buffer_infos = Vec::with_capacity(desc.buffers.len());
1754        let mut buffer_infos = ExtendStack::from_vec_capacity(&mut buffer_infos);
1755        let mut image_infos = Vec::with_capacity(desc.samplers.len() + desc.textures.len());
1756        let mut image_infos = ExtendStack::from_vec_capacity(&mut image_infos);
1757        // TODO: This length could be reduced to just the number of top-level acceleration
1758        // structure bindings, where multiple consecutive TLAS bindings that are set via
1759        // one `WriteDescriptorSet` count towards one "info" struct, not the total number of
1760        // acceleration structure bindings to write:
1761        let mut acceleration_structure_infos =
1762            Vec::with_capacity(desc.acceleration_structures.len());
1763        let mut acceleration_structure_infos =
1764            ExtendStack::from_vec_capacity(&mut acceleration_structure_infos);
1765        let mut raw_acceleration_structures =
1766            Vec::with_capacity(desc.acceleration_structures.len());
1767        let mut raw_acceleration_structures =
1768            ExtendStack::from_vec_capacity(&mut raw_acceleration_structures);
1769
1770        let layout_and_entry_iter = desc.entries.iter().map(|entry| {
1771            let layout = desc
1772                .layout
1773                .entries
1774                .iter()
1775                .find(|layout_entry| layout_entry.binding == entry.binding)
1776                .expect("internal error: no layout entry found with binding slot");
1777            (layout, entry)
1778        });
1779        let mut next_binding = 0;
1780        for (layout, entry) in layout_and_entry_iter {
1781            let write = vk::WriteDescriptorSet::default().dst_set(set.raw());
1782
1783            match layout.ty {
1784                wgt::BindingType::Sampler(_) => {
1785                    let start = entry.resource_index;
1786                    let end = start + entry.count;
1787                    let local_image_infos;
1788                    (image_infos, local_image_infos) =
1789                        image_infos.extend(desc.samplers[start as usize..end as usize].iter().map(
1790                            |sampler| vk::DescriptorImageInfo::default().sampler(sampler.raw),
1791                        ));
1792                    writes.push(
1793                        write
1794                            .dst_binding(next_binding)
1795                            .descriptor_type(conv::map_binding_type(layout.ty))
1796                            .image_info(local_image_infos),
1797                    );
1798                    next_binding += 1;
1799                }
1800                wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => {
1801                    let start = entry.resource_index;
1802                    let end = start + entry.count;
1803                    let local_image_infos;
1804                    (image_infos, local_image_infos) =
1805                        image_infos.extend(desc.textures[start as usize..end as usize].iter().map(
1806                            |binding| {
1807                                let layout =
1808                                    conv::derive_image_layout(binding.usage, binding.view.format);
1809                                vk::DescriptorImageInfo::default()
1810                                    .image_view(binding.view.raw)
1811                                    .image_layout(layout)
1812                            },
1813                        ));
1814                    writes.push(
1815                        write
1816                            .dst_binding(next_binding)
1817                            .descriptor_type(conv::map_binding_type(layout.ty))
1818                            .image_info(local_image_infos),
1819                    );
1820                    next_binding += 1;
1821                }
1822                wgt::BindingType::Buffer { .. } => {
1823                    let start = entry.resource_index;
1824                    let end = start + entry.count;
1825                    let local_buffer_infos;
1826                    (buffer_infos, local_buffer_infos) =
1827                        buffer_infos.extend(desc.buffers[start as usize..end as usize].iter().map(
1828                            |binding| {
1829                                vk::DescriptorBufferInfo::default()
1830                                    .buffer(binding.buffer.raw)
1831                                    .offset(binding.offset)
1832                                    .range(binding.size.get())
1833                            },
1834                        ));
1835                    writes.push(
1836                        write
1837                            .dst_binding(next_binding)
1838                            .descriptor_type(conv::map_binding_type(layout.ty))
1839                            .buffer_info(local_buffer_infos),
1840                    );
1841                    next_binding += 1;
1842                }
1843                wgt::BindingType::AccelerationStructure { .. } => {
1844                    let start = entry.resource_index;
1845                    let end = start + entry.count;
1846
1847                    let local_raw_acceleration_structures;
1848                    (
1849                        raw_acceleration_structures,
1850                        local_raw_acceleration_structures,
1851                    ) = raw_acceleration_structures.extend(
1852                        desc.acceleration_structures[start as usize..end as usize]
1853                            .iter()
1854                            .map(|acceleration_structure| acceleration_structure.raw),
1855                    );
1856
1857                    let local_acceleration_structure_infos;
1858                    (
1859                        acceleration_structure_infos,
1860                        local_acceleration_structure_infos,
1861                    ) = acceleration_structure_infos.extend_one(
1862                        vk::WriteDescriptorSetAccelerationStructureKHR::default()
1863                            .acceleration_structures(local_raw_acceleration_structures),
1864                    );
1865
1866                    writes.push(
1867                        write
1868                            .dst_binding(next_binding)
1869                            .descriptor_type(conv::map_binding_type(layout.ty))
1870                            .descriptor_count(entry.count)
1871                            .push_next(local_acceleration_structure_infos),
1872                    );
1873                    next_binding += 1;
1874                }
1875                wgt::BindingType::ExternalTexture => unimplemented!(),
1876            }
1877        }
1878
1879        unsafe { self.shared.raw.update_descriptor_sets(&writes, &[]) };
1880
1881        self.counters.bind_groups.add(1);
1882
1883        Ok(super::BindGroup { set })
1884    }
1885
1886    unsafe fn destroy_bind_group(&self, group: super::BindGroup) {
1887        unsafe { self.desc_allocator.lock().free(&self.shared.raw, group.set) };
1888
1889        self.counters.bind_groups.sub(1);
1890    }
1891
1892    unsafe fn create_shader_module(
1893        &self,
1894        desc: &crate::ShaderModuleDescriptor,
1895        shader: crate::ShaderInput,
1896    ) -> Result<super::ShaderModule, crate::ShaderError> {
1897        let shader_module = match shader {
1898            crate::ShaderInput::Naga(naga_shader)
1899                if self
1900                    .shared
1901                    .workarounds
1902                    .contains(super::Workarounds::SEPARATE_ENTRY_POINTS)
1903                    || !naga_shader.module.overrides.is_empty() =>
1904            {
1905                super::ShaderModule::Intermediate {
1906                    naga_shader,
1907                    runtime_checks: desc.runtime_checks,
1908                }
1909            }
1910            crate::ShaderInput::Naga(naga_shader) => {
1911                let mut naga_options = self.naga_options.clone();
1912                naga_options.debug_info =
1913                    naga_shader
1914                        .debug_source
1915                        .as_ref()
1916                        .map(|d| naga::back::spv::DebugInfo {
1917                            source_code: d.source_code.as_ref(),
1918                            file_name: d.file_name.as_ref(),
1919                            language: naga::back::spv::SourceLanguage::WGSL,
1920                        });
1921                if !desc.runtime_checks.bounds_checks {
1922                    naga_options.bounds_check_policies = naga::proc::BoundsCheckPolicies {
1923                        index: naga::proc::BoundsCheckPolicy::Unchecked,
1924                        buffer: naga::proc::BoundsCheckPolicy::Unchecked,
1925                        image_load: naga::proc::BoundsCheckPolicy::Unchecked,
1926                        binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
1927                    };
1928                }
1929                let spv = naga::back::spv::write_vec(
1930                    &naga_shader.module,
1931                    &naga_shader.info,
1932                    &naga_options,
1933                    None,
1934                )
1935                .map_err(|e| crate::ShaderError::Compilation(format!("{e}")))?;
1936                super::ShaderModule::Raw(self.create_shader_module_impl(&spv, &desc.label)?)
1937            }
1938            crate::ShaderInput::SpirV(data) => {
1939                super::ShaderModule::Raw(self.create_shader_module_impl(data, &desc.label)?)
1940            }
1941            crate::ShaderInput::MetalLib { .. }
1942            | crate::ShaderInput::Msl { .. }
1943            | crate::ShaderInput::Dxil { .. }
1944            | crate::ShaderInput::Hlsl { .. }
1945            | crate::ShaderInput::Glsl { .. } => unreachable!(),
1946        };
1947
1948        self.counters.shader_modules.add(1);
1949
1950        Ok(shader_module)
1951    }
1952
1953    unsafe fn destroy_shader_module(&self, module: super::ShaderModule) {
1954        match module {
1955            super::ShaderModule::Raw(raw) => {
1956                unsafe { self.shared.raw.destroy_shader_module(raw, None) };
1957            }
1958            super::ShaderModule::Intermediate { .. } => {}
1959        }
1960
1961        self.counters.shader_modules.sub(1);
1962    }
1963
1964    unsafe fn create_render_pipeline(
1965        &self,
1966        desc: &crate::RenderPipelineDescriptor<
1967            super::PipelineLayout,
1968            super::ShaderModule,
1969            super::PipelineCache,
1970        >,
1971    ) -> Result<super::RenderPipeline, crate::PipelineError> {
1972        let dynamic_states = [
1973            vk::DynamicState::VIEWPORT,
1974            vk::DynamicState::SCISSOR,
1975            vk::DynamicState::BLEND_CONSTANTS,
1976            vk::DynamicState::STENCIL_REFERENCE,
1977        ];
1978        let mut compatible_rp_key = super::RenderPassKey {
1979            sample_count: desc.multisample.count,
1980            multiview_mask: desc.multiview_mask,
1981            ..Default::default()
1982        };
1983        let mut stages = ArrayVec::<_, { crate::MAX_CONCURRENT_SHADER_STAGES }>::new();
1984        let mut vertex_buffers = Vec::new();
1985        let mut vertex_attributes = Vec::new();
1986
1987        if let crate::VertexProcessor::Standard {
1988            vertex_buffers: desc_vertex_buffers,
1989            vertex_stage: _,
1990        } = &desc.vertex_processor
1991        {
1992            vertex_buffers = Vec::with_capacity(desc_vertex_buffers.len());
1993            for (i, vb) in desc_vertex_buffers.iter().enumerate() {
1994                let Some(vb) = vb else {
1995                    continue;
1996                };
1997                vertex_buffers.push(vk::VertexInputBindingDescription {
1998                    binding: i as u32,
1999                    stride: vb.array_stride as u32,
2000                    input_rate: match vb.step_mode {
2001                        wgt::VertexStepMode::Vertex => vk::VertexInputRate::VERTEX,
2002                        wgt::VertexStepMode::Instance => vk::VertexInputRate::INSTANCE,
2003                    },
2004                });
2005                for at in vb.attributes {
2006                    vertex_attributes.push(vk::VertexInputAttributeDescription {
2007                        location: at.shader_location,
2008                        binding: i as u32,
2009                        format: conv::map_vertex_format(at.format),
2010                        offset: at.offset as u32,
2011                    });
2012                }
2013            }
2014        }
2015
2016        let vk_vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
2017            .vertex_binding_descriptions(&vertex_buffers)
2018            .vertex_attribute_descriptions(&vertex_attributes);
2019
2020        let vk_input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
2021            .topology(conv::map_topology(desc.primitive.topology))
2022            .primitive_restart_enable(desc.primitive.strip_index_format.is_some());
2023
2024        let mut compiled_vs = None;
2025        let mut compiled_ms = None;
2026        let mut compiled_ts = None;
2027        match &desc.vertex_processor {
2028            crate::VertexProcessor::Standard {
2029                vertex_buffers: _,
2030                vertex_stage,
2031            } => {
2032                compiled_vs = Some(self.compile_stage(
2033                    vertex_stage,
2034                    naga::ShaderStage::Vertex,
2035                    &desc.layout.binding_map,
2036                )?);
2037                stages.push(compiled_vs.as_ref().unwrap().create_info);
2038            }
2039            crate::VertexProcessor::Mesh {
2040                task_stage,
2041                mesh_stage,
2042            } => {
2043                if let Some(t) = task_stage.as_ref() {
2044                    compiled_ts = Some(self.compile_stage(
2045                        t,
2046                        naga::ShaderStage::Task,
2047                        &desc.layout.binding_map,
2048                    )?);
2049                    stages.push(compiled_ts.as_ref().unwrap().create_info);
2050                }
2051                compiled_ms = Some(self.compile_stage(
2052                    mesh_stage,
2053                    naga::ShaderStage::Mesh,
2054                    &desc.layout.binding_map,
2055                )?);
2056                stages.push(compiled_ms.as_ref().unwrap().create_info);
2057            }
2058        }
2059        let compiled_fs = match desc.fragment_stage {
2060            Some(ref stage) => {
2061                let compiled = self.compile_stage(
2062                    stage,
2063                    naga::ShaderStage::Fragment,
2064                    &desc.layout.binding_map,
2065                )?;
2066                stages.push(compiled.create_info);
2067                Some(compiled)
2068            }
2069            None => None,
2070        };
2071
2072        let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::default()
2073            .polygon_mode(conv::map_polygon_mode(desc.primitive.polygon_mode))
2074            .front_face(conv::map_front_face(desc.primitive.front_face))
2075            .line_width(1.0)
2076            .depth_clamp_enable(desc.primitive.unclipped_depth);
2077        if let Some(face) = desc.primitive.cull_mode {
2078            vk_rasterization = vk_rasterization.cull_mode(conv::map_cull_face(face))
2079        }
2080        let mut vk_rasterization_conservative_state =
2081            vk::PipelineRasterizationConservativeStateCreateInfoEXT::default()
2082                .conservative_rasterization_mode(
2083                    vk::ConservativeRasterizationModeEXT::OVERESTIMATE,
2084                );
2085        if desc.primitive.conservative {
2086            vk_rasterization = vk_rasterization.push_next(&mut vk_rasterization_conservative_state);
2087        }
2088
2089        let mut vk_depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default();
2090        if let Some(ref ds) = desc.depth_stencil {
2091            (
2092                compatible_rp_key.depth_read_only,
2093                compatible_rp_key.stencil_read_only,
2094            ) = (
2095                ds.is_depth_read_only(),
2096                ds.is_stencil_read_only(desc.primitive.cull_mode),
2097            );
2098            let vk_format = self.shared.private_caps.map_texture_format(ds.format);
2099            let vk_layout = match (
2100                compatible_rp_key.depth_read_only,
2101                compatible_rp_key.stencil_read_only,
2102            ) {
2103                (true, true) => vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL,
2104                (true, false) => vk::ImageLayout::DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL,
2105                (false, true) => vk::ImageLayout::DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL,
2106                (false, false) => vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
2107            };
2108            compatible_rp_key.depth_stencil = Some(super::DepthStencilAttachmentKey {
2109                base: super::AttachmentKey::compatible(vk_format, vk_layout),
2110                stencil_ops: crate::AttachmentOps::all(),
2111            });
2112
2113            if ds.is_depth_enabled() {
2114                vk_depth_stencil = vk_depth_stencil
2115                    .depth_test_enable(true)
2116                    .depth_write_enable(ds.depth_write_enabled.unwrap_or_default())
2117                    .depth_compare_op(conv::map_comparison(ds.depth_compare.unwrap_or_default()));
2118            }
2119            if ds.stencil.is_enabled() {
2120                let s = &ds.stencil;
2121                let front = conv::map_stencil_face(&s.front, s.read_mask, s.write_mask);
2122                let back = conv::map_stencil_face(&s.back, s.read_mask, s.write_mask);
2123                vk_depth_stencil = vk_depth_stencil
2124                    .stencil_test_enable(true)
2125                    .front(front)
2126                    .back(back);
2127            }
2128
2129            if ds.bias.is_enabled() {
2130                vk_rasterization = vk_rasterization
2131                    .depth_bias_enable(true)
2132                    .depth_bias_constant_factor(ds.bias.constant as f32)
2133                    .depth_bias_clamp(ds.bias.clamp)
2134                    .depth_bias_slope_factor(ds.bias.slope_scale);
2135            }
2136        }
2137
2138        let vk_viewport = vk::PipelineViewportStateCreateInfo::default()
2139            .flags(vk::PipelineViewportStateCreateFlags::empty())
2140            .scissor_count(1)
2141            .viewport_count(1);
2142
2143        let vk_sample_mask = [
2144            desc.multisample.mask as u32,
2145            (desc.multisample.mask >> 32) as u32,
2146        ];
2147        let vk_multisample = vk::PipelineMultisampleStateCreateInfo::default()
2148            .rasterization_samples(vk::SampleCountFlags::from_raw(desc.multisample.count))
2149            .alpha_to_coverage_enable(desc.multisample.alpha_to_coverage_enabled)
2150            .sample_mask(&vk_sample_mask);
2151
2152        let mut vk_attachments = Vec::with_capacity(desc.color_targets.len());
2153        for cat in desc.color_targets {
2154            let (key, attarchment) = if let Some(cat) = cat.as_ref() {
2155                let mut vk_attachment = vk::PipelineColorBlendAttachmentState::default()
2156                    .color_write_mask(vk::ColorComponentFlags::from_raw(cat.write_mask.bits()));
2157                if let Some(ref blend) = cat.blend {
2158                    let (color_op, color_src, color_dst) = conv::map_blend_component(&blend.color);
2159                    let (alpha_op, alpha_src, alpha_dst) = conv::map_blend_component(&blend.alpha);
2160                    vk_attachment = vk_attachment
2161                        .blend_enable(true)
2162                        .color_blend_op(color_op)
2163                        .src_color_blend_factor(color_src)
2164                        .dst_color_blend_factor(color_dst)
2165                        .alpha_blend_op(alpha_op)
2166                        .src_alpha_blend_factor(alpha_src)
2167                        .dst_alpha_blend_factor(alpha_dst);
2168                }
2169
2170                let vk_format = self.shared.private_caps.map_texture_format(cat.format);
2171                (
2172                    Some(super::ColorAttachmentKey {
2173                        base: super::AttachmentKey::compatible(
2174                            vk_format,
2175                            vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
2176                        ),
2177                        resolve: None,
2178                    }),
2179                    vk_attachment,
2180                )
2181            } else {
2182                (None, vk::PipelineColorBlendAttachmentState::default())
2183            };
2184
2185            compatible_rp_key.colors.push(key);
2186            vk_attachments.push(attarchment);
2187        }
2188
2189        let vk_color_blend =
2190            vk::PipelineColorBlendStateCreateInfo::default().attachments(&vk_attachments);
2191
2192        let vk_dynamic_state =
2193            vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
2194
2195        let raw_pass = self.shared.make_render_pass(compatible_rp_key)?;
2196
2197        let vk_infos = [{
2198            vk::GraphicsPipelineCreateInfo::default()
2199                .layout(desc.layout.raw)
2200                .stages(&stages)
2201                .vertex_input_state(&vk_vertex_input)
2202                .input_assembly_state(&vk_input_assembly)
2203                .rasterization_state(&vk_rasterization)
2204                .viewport_state(&vk_viewport)
2205                .multisample_state(&vk_multisample)
2206                .depth_stencil_state(&vk_depth_stencil)
2207                .color_blend_state(&vk_color_blend)
2208                .dynamic_state(&vk_dynamic_state)
2209                .render_pass(raw_pass)
2210        }];
2211
2212        let pipeline_cache = desc
2213            .cache
2214            .map(|it| it.raw)
2215            .unwrap_or(vk::PipelineCache::null());
2216
2217        let mut raw_vec = {
2218            profiling::scope!("vkCreateGraphicsPipelines");
2219            unsafe {
2220                self.shared
2221                    .raw
2222                    .create_graphics_pipelines(pipeline_cache, &vk_infos, None)
2223                    .map_err(|(_, e)| super::map_pipeline_err(e))
2224            }?
2225        };
2226
2227        let raw = raw_vec.pop().unwrap();
2228        if let Some(label) = desc.label {
2229            unsafe { self.shared.set_object_name(raw, label) };
2230        }
2231
2232        if let Some(CompiledStage {
2233            temp_raw_module: Some(raw_module),
2234            ..
2235        }) = compiled_vs
2236        {
2237            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2238        }
2239        if let Some(CompiledStage {
2240            temp_raw_module: Some(raw_module),
2241            ..
2242        }) = compiled_ts
2243        {
2244            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2245        }
2246        if let Some(CompiledStage {
2247            temp_raw_module: Some(raw_module),
2248            ..
2249        }) = compiled_ms
2250        {
2251            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2252        }
2253        if let Some(CompiledStage {
2254            temp_raw_module: Some(raw_module),
2255            ..
2256        }) = compiled_fs
2257        {
2258            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2259        }
2260
2261        self.counters.render_pipelines.add(1);
2262
2263        Ok(super::RenderPipeline {
2264            raw,
2265            is_multiview: desc.multiview_mask.is_some(),
2266        })
2267    }
2268
2269    unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) {
2270        unsafe { self.shared.raw.destroy_pipeline(pipeline.raw, None) };
2271
2272        self.counters.render_pipelines.sub(1);
2273    }
2274
2275    unsafe fn create_compute_pipeline(
2276        &self,
2277        desc: &crate::ComputePipelineDescriptor<
2278            super::PipelineLayout,
2279            super::ShaderModule,
2280            super::PipelineCache,
2281        >,
2282    ) -> Result<super::ComputePipeline, crate::PipelineError> {
2283        let compiled = self.compile_stage(
2284            &desc.stage,
2285            naga::ShaderStage::Compute,
2286            &desc.layout.binding_map,
2287        )?;
2288
2289        let vk_infos = [{
2290            vk::ComputePipelineCreateInfo::default()
2291                .layout(desc.layout.raw)
2292                .stage(compiled.create_info)
2293        }];
2294
2295        let pipeline_cache = desc
2296            .cache
2297            .map(|it| it.raw)
2298            .unwrap_or(vk::PipelineCache::null());
2299
2300        let mut raw_vec = {
2301            profiling::scope!("vkCreateComputePipelines");
2302            unsafe {
2303                self.shared
2304                    .raw
2305                    .create_compute_pipelines(pipeline_cache, &vk_infos, None)
2306                    .map_err(|(_, e)| super::map_pipeline_err(e))
2307            }?
2308        };
2309
2310        let raw = raw_vec.pop().unwrap();
2311        if let Some(label) = desc.label {
2312            unsafe { self.shared.set_object_name(raw, label) };
2313        }
2314
2315        if let Some(raw_module) = compiled.temp_raw_module {
2316            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2317        }
2318
2319        self.counters.compute_pipelines.add(1);
2320
2321        Ok(super::ComputePipeline { raw })
2322    }
2323
2324    unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) {
2325        unsafe { self.shared.raw.destroy_pipeline(pipeline.raw, None) };
2326
2327        self.counters.compute_pipelines.sub(1);
2328    }
2329
2330    unsafe fn create_ray_tracing_pipeline(
2331        &self,
2332        desc: &crate::RayTracingPipelineDescriptor<
2333            super::PipelineLayout,
2334            super::ShaderModule,
2335            super::PipelineCache,
2336        >,
2337    ) -> Result<super::RayTracingPipeline, crate::PipelineError> {
2338        let mut stages = Vec::new();
2339        let mut groups = Vec::new();
2340
2341        let compiled_ray_gen = self.compile_stage(
2342            &desc.ray_generation,
2343            naga::ShaderStage::RayGeneration,
2344            &desc.layout.binding_map,
2345        )?;
2346
2347        groups.push(
2348            vk::RayTracingShaderGroupCreateInfoKHR::default()
2349                .closest_hit_shader(vk::SHADER_UNUSED_KHR)
2350                .any_hit_shader(vk::SHADER_UNUSED_KHR)
2351                .intersection_shader(vk::SHADER_UNUSED_KHR)
2352                .general_shader(0) // stages is empty so next index is 0.
2353                .ty(vk::RayTracingShaderGroupTypeKHR::GENERAL),
2354        );
2355
2356        stages.push(compiled_ray_gen.create_info);
2357
2358        let compiled_miss = self.compile_stage(
2359            &desc.miss,
2360            naga::ShaderStage::Miss,
2361            &desc.layout.binding_map,
2362        )?;
2363
2364        groups.push(
2365            vk::RayTracingShaderGroupCreateInfoKHR::default()
2366                .closest_hit_shader(vk::SHADER_UNUSED_KHR)
2367                .any_hit_shader(vk::SHADER_UNUSED_KHR)
2368                .intersection_shader(vk::SHADER_UNUSED_KHR)
2369                .general_shader(1) // stages always has one element so next index is 1.
2370                .ty(vk::RayTracingShaderGroupTypeKHR::GENERAL),
2371        );
2372
2373        stages.push(compiled_miss.create_info);
2374
2375        // This is to keep alive the CStrings, as the ones in the loop would be deallocated
2376        // causing UB otherwise.
2377        let mut compiled_stages = Vec::new();
2378
2379        for group in desc.intersection {
2380            let compiled_closest_hits = self.compile_stage(
2381                &group.closest_hit,
2382                naga::ShaderStage::ClosestHit,
2383                &desc.layout.binding_map,
2384            )?;
2385
2386            let closest_idx = stages.len();
2387
2388            stages.push(compiled_closest_hits.create_info);
2389
2390            compiled_stages.push(compiled_closest_hits);
2391
2392            let mut raw_hit: vk::RayTracingShaderGroupCreateInfoKHR<'_> =
2393                vk::RayTracingShaderGroupCreateInfoKHR::default()
2394                    .closest_hit_shader(closest_idx as _)
2395                    .any_hit_shader(vk::SHADER_UNUSED_KHR)
2396                    .intersection_shader(vk::SHADER_UNUSED_KHR)
2397                    .general_shader(vk::SHADER_UNUSED_KHR)
2398                    .ty(vk::RayTracingShaderGroupTypeKHR::TRIANGLES_HIT_GROUP);
2399
2400            if let Some(any_hit) = &group.any_hit {
2401                let compiled_any_hit = self.compile_stage(
2402                    any_hit,
2403                    naga::ShaderStage::AnyHit,
2404                    &desc.layout.binding_map,
2405                )?;
2406
2407                let any_idx = stages.len();
2408
2409                stages.push(compiled_any_hit.create_info);
2410
2411                compiled_stages.push(compiled_any_hit);
2412
2413                raw_hit = raw_hit.any_hit_shader(any_idx as _);
2414            }
2415
2416            groups.push(raw_hit);
2417        }
2418
2419        let create_infos = [{
2420            vk::RayTracingPipelineCreateInfoKHR::default()
2421                .layout(desc.layout.raw)
2422                .max_pipeline_ray_recursion_depth(desc.max_recursion_depth)
2423                .stages(&stages)
2424                .groups(&groups)
2425        }];
2426
2427        let pipeline_cache = desc
2428            .cache
2429            .map(|it| it.raw)
2430            .unwrap_or(vk::PipelineCache::null());
2431
2432        let fns = self
2433            .shared
2434            .extension_fns
2435            .ray_tracing_pipelines
2436            .as_ref()
2437            .unwrap();
2438        let pipelines = unsafe {
2439            fns.create_ray_tracing_pipelines(
2440                vk::DeferredOperationKHR::null(),
2441                pipeline_cache,
2442                &create_infos,
2443                None,
2444            )
2445            .map_err(|(_, e)| super::map_pipeline_err(e))
2446        }?;
2447
2448        if let Some(raw_module) = compiled_ray_gen.temp_raw_module {
2449            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2450        }
2451
2452        if let Some(raw_module) = compiled_miss.temp_raw_module {
2453            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2454        }
2455
2456        for raw_module in compiled_stages
2457            .into_iter()
2458            .flat_map(|stage| stage.temp_raw_module)
2459        {
2460            unsafe { self.shared.raw.destroy_shader_module(raw_module, None) };
2461        }
2462
2463        self.counters.ray_tracing_pipelines.add(1);
2464
2465        Ok(super::RayTracingPipeline { raw: pipelines[0] })
2466    }
2467
2468    unsafe fn destroy_ray_tracing_pipeline(&self, pipeline: super::RayTracingPipeline) {
2469        unsafe { self.shared.raw.destroy_pipeline(pipeline.raw, None) };
2470
2471        self.counters.ray_tracing_pipelines.sub(1);
2472    }
2473
2474    unsafe fn get_raytracing_pipeline_group_data(
2475        &self,
2476        pipeline: &super::RayTracingPipeline,
2477        groups: core::ops::Range<u32>,
2478    ) -> Result<Vec<u8>, crate::DeviceError> {
2479        let fns = self
2480            .shared
2481            .extension_fns
2482            .ray_tracing_pipelines
2483            .as_ref()
2484            .unwrap();
2485
2486        let num = groups.end - groups.start;
2487
2488        unsafe {
2489            fns.get_ray_tracing_shader_group_handles(
2490                pipeline.raw,
2491                groups.start,
2492                num,
2493                (num * self
2494                    .shared
2495                    .private_caps
2496                    .ray_tracing_pipeline_group_data_size) as usize,
2497            )
2498        }
2499        .map_err(super::map_host_device_oom_err)
2500    }
2501
2502    unsafe fn create_pipeline_cache(
2503        &self,
2504        desc: &crate::PipelineCacheDescriptor<'_>,
2505    ) -> Result<super::PipelineCache, crate::PipelineCacheError> {
2506        let mut info = vk::PipelineCacheCreateInfo::default();
2507        if let Some(data) = desc.data {
2508            info = info.initial_data(data)
2509        }
2510        profiling::scope!("vkCreatePipelineCache");
2511        let raw = unsafe { self.shared.raw.create_pipeline_cache(&info, None) }
2512            .map_err(super::map_host_device_oom_err)?;
2513
2514        Ok(super::PipelineCache { raw })
2515    }
2516    fn pipeline_cache_validation_key(&self) -> Option<[u8; 16]> {
2517        Some(self.shared.pipeline_cache_validation_key)
2518    }
2519    unsafe fn destroy_pipeline_cache(&self, cache: super::PipelineCache) {
2520        unsafe { self.shared.raw.destroy_pipeline_cache(cache.raw, None) }
2521    }
2522    unsafe fn create_query_set(
2523        &self,
2524        desc: &wgt::QuerySetDescriptor<crate::Label>,
2525    ) -> Result<super::QuerySet, crate::DeviceError> {
2526        // Assume each query is 256 bytes. This is the case for occlusion
2527        // queries on an AMD W6800 with driver version 32.0.12030.9. The
2528        // size and allocation policy may vary; this is an approximate
2529        // check only.
2530        self.error_if_would_oom_on_resource_allocation(
2531            gpu_allocator::MemoryLocation::GpuToCpu,
2532            &vk::MemoryRequirements {
2533                size: desc.count as u64 * 256,
2534                alignment: 256,
2535                memory_type_bits: self.valid_ash_memory_types,
2536            },
2537        )?;
2538
2539        let (vk_type, pipeline_statistics) = match desc.ty {
2540            wgt::QueryType::Occlusion => (
2541                vk::QueryType::OCCLUSION,
2542                vk::QueryPipelineStatisticFlags::empty(),
2543            ),
2544            wgt::QueryType::PipelineStatistics(statistics) => (
2545                vk::QueryType::PIPELINE_STATISTICS,
2546                conv::map_pipeline_statistics(statistics),
2547            ),
2548            wgt::QueryType::Timestamp => (
2549                vk::QueryType::TIMESTAMP,
2550                vk::QueryPipelineStatisticFlags::empty(),
2551            ),
2552        };
2553
2554        let vk_info = vk::QueryPoolCreateInfo::default()
2555            .query_type(vk_type)
2556            .query_count(desc.count)
2557            .pipeline_statistics(pipeline_statistics);
2558
2559        let raw = unsafe { self.shared.raw.create_query_pool(&vk_info, None) }
2560            .map_err(super::map_host_device_oom_err)?;
2561        if let Some(label) = desc.label {
2562            unsafe { self.shared.set_object_name(raw, label) };
2563        }
2564
2565        self.counters.query_sets.add(1);
2566
2567        Ok(super::QuerySet { raw })
2568    }
2569
2570    unsafe fn destroy_query_set(&self, set: super::QuerySet) {
2571        unsafe { self.shared.raw.destroy_query_pool(set.raw, None) };
2572
2573        self.counters.query_sets.sub(1);
2574    }
2575
2576    unsafe fn create_fence(&self) -> Result<super::Fence, crate::DeviceError> {
2577        self.counters.fences.add(1);
2578
2579        Ok(if self.shared.private_caps.timeline_semaphores {
2580            let mut sem_type_info =
2581                vk::SemaphoreTypeCreateInfo::default().semaphore_type(vk::SemaphoreType::TIMELINE);
2582            let vk_info = vk::SemaphoreCreateInfo::default().push_next(&mut sem_type_info);
2583            let raw = unsafe { self.shared.raw.create_semaphore(&vk_info, None) }
2584                .map_err(super::map_host_device_oom_err)?;
2585
2586            super::Fence::TimelineSemaphore(raw)
2587        } else {
2588            super::Fence::FencePool(RwLock::new(super::FencePool {
2589                last_completed: 0,
2590                active: Vec::new(),
2591                free: Vec::new(),
2592            }))
2593        })
2594    }
2595    unsafe fn destroy_fence(&self, fence: super::Fence) {
2596        match fence {
2597            super::Fence::TimelineSemaphore(raw) => {
2598                unsafe { self.shared.raw.destroy_semaphore(raw, None) };
2599            }
2600            super::Fence::FencePool(pool) => {
2601                let super::FencePool {
2602                    active,
2603                    free,
2604                    last_completed: _,
2605                } = pool.into_inner();
2606
2607                for (_, raw) in active {
2608                    unsafe {
2609                        self.shared.raw.destroy_fence(Arc::into_inner(raw).expect("Fence should have its reference count be one by the end of each function"), None)
2610                    };
2611                }
2612                for raw in free {
2613                    unsafe { self.shared.raw.destroy_fence(raw, None) };
2614                }
2615            }
2616        }
2617
2618        self.counters.fences.sub(1);
2619    }
2620    unsafe fn get_fence_value(
2621        &self,
2622        fence: &super::Fence,
2623    ) -> Result<crate::FenceValue, crate::DeviceError> {
2624        fence.get_latest(
2625            &self.shared.raw,
2626            self.shared.extension_fns.timeline_semaphore.as_ref(),
2627        )
2628    }
2629    unsafe fn wait(
2630        &self,
2631        fence: &super::Fence,
2632        wait_value: crate::FenceValue,
2633        timeout: Option<Duration>,
2634    ) -> Result<bool, crate::DeviceError> {
2635        let timeout_ns = timeout
2636            .unwrap_or(Duration::MAX)
2637            .as_nanos()
2638            .min(u64::MAX as _) as u64;
2639        self.shared.wait_for_fence(fence, wait_value, timeout_ns)
2640    }
2641
2642    unsafe fn start_graphics_debugger_capture(&self) -> bool {
2643        #[cfg(feature = "renderdoc")]
2644        {
2645            // Renderdoc requires us to give us the pointer that vkInstance _points to_.
2646            let raw_vk_instance =
2647                vk::Handle::as_raw(self.shared.instance.raw.handle()) as *mut *mut _;
2648            let raw_vk_instance_dispatch_table = unsafe { *raw_vk_instance };
2649            unsafe {
2650                self.render_doc
2651                    .start_frame_capture(raw_vk_instance_dispatch_table, ptr::null_mut())
2652            }
2653        }
2654        #[cfg(not(feature = "renderdoc"))]
2655        false
2656    }
2657    unsafe fn stop_graphics_debugger_capture(&self) {
2658        #[cfg(feature = "renderdoc")]
2659        {
2660            // Renderdoc requires us to give us the pointer that vkInstance _points to_.
2661            let raw_vk_instance =
2662                vk::Handle::as_raw(self.shared.instance.raw.handle()) as *mut *mut _;
2663            let raw_vk_instance_dispatch_table = unsafe { *raw_vk_instance };
2664
2665            unsafe {
2666                self.render_doc
2667                    .end_frame_capture(raw_vk_instance_dispatch_table, ptr::null_mut())
2668            }
2669        }
2670    }
2671
2672    unsafe fn pipeline_cache_get_data(&self, cache: &super::PipelineCache) -> Option<Vec<u8>> {
2673        let data = unsafe { self.raw_device().get_pipeline_cache_data(cache.raw) };
2674        data.ok()
2675    }
2676
2677    unsafe fn get_acceleration_structure_build_sizes<'a>(
2678        &self,
2679        desc: &crate::GetAccelerationStructureBuildSizesDescriptor<'a, super::Buffer>,
2680    ) -> crate::AccelerationStructureBuildSizes {
2681        const CAPACITY: usize = 8;
2682
2683        let ray_tracing_functions = self
2684            .shared
2685            .extension_fns
2686            .ray_tracing
2687            .as_ref()
2688            .expect("Feature `RAY_TRACING` not enabled");
2689
2690        let (geometries, primitive_counts) = match *desc.entries {
2691            crate::AccelerationStructureEntries::Instances(ref instances) => {
2692                let instance_data = vk::AccelerationStructureGeometryInstancesDataKHR::default();
2693
2694                let geometry = vk::AccelerationStructureGeometryKHR::default()
2695                    .geometry_type(vk::GeometryTypeKHR::INSTANCES)
2696                    .geometry(vk::AccelerationStructureGeometryDataKHR {
2697                        instances: instance_data,
2698                    });
2699
2700                (
2701                    smallvec::smallvec![geometry],
2702                    smallvec::smallvec![instances.count],
2703                )
2704            }
2705            crate::AccelerationStructureEntries::Triangles(ref in_geometries) => {
2706                let mut primitive_counts =
2707                    smallvec::SmallVec::<[u32; CAPACITY]>::with_capacity(in_geometries.len());
2708                let mut geometries = smallvec::SmallVec::<
2709                    [vk::AccelerationStructureGeometryKHR; CAPACITY],
2710                >::with_capacity(in_geometries.len());
2711
2712                for triangles in in_geometries {
2713                    let mut triangle_data =
2714                        vk::AccelerationStructureGeometryTrianglesDataKHR::default()
2715                            .index_type(vk::IndexType::NONE_KHR)
2716                            .vertex_format(conv::map_vertex_format(triangles.vertex_format))
2717                            .max_vertex(triangles.vertex_count)
2718                            .vertex_stride(triangles.vertex_stride)
2719                            // The vulkan spec suggests we could pass a non-zero invalid address here if fetching
2720                            // the real address has significant overhead, but we pass the real one to be on the
2721                            // safe side for now.
2722                            // from https://registry.khronos.org/vulkan/specs/latest/man/html/vkGetAccelerationStructureBuildSizesKHR.html
2723                            // > The srcAccelerationStructure, dstAccelerationStructure, and mode members
2724                            // > of pBuildInfo are ignored. Any VkDeviceOrHostAddressKHR or VkDeviceOrHostAddressConstKHR
2725                            // > members of pBuildInfo are ignored by this command, except that the hostAddress
2726                            // > member of VkAccelerationStructureGeometryTrianglesDataKHR::transformData will
2727                            // > be examined to check if it is NULL.
2728                            .transform_data(vk::DeviceOrHostAddressConstKHR {
2729                                device_address: if desc
2730                                    .flags
2731                                    .contains(wgt::AccelerationStructureFlags::USE_TRANSFORM)
2732                                {
2733                                    unsafe {
2734                                        ray_tracing_functions
2735                                            .buffer_device_address
2736                                            .get_buffer_device_address(
2737                                                &vk::BufferDeviceAddressInfo::default().buffer(
2738                                                    triangles
2739                                                        .transform
2740                                                        .as_ref()
2741                                                        .unwrap()
2742                                                        .buffer
2743                                                        .raw,
2744                                                ),
2745                                            )
2746                                    }
2747                                } else {
2748                                    0
2749                                },
2750                            });
2751
2752                    let pritive_count = if let Some(ref indices) = triangles.indices {
2753                        triangle_data =
2754                            triangle_data.index_type(conv::map_index_format(indices.format));
2755                        indices.count / 3
2756                    } else {
2757                        triangles.vertex_count / 3
2758                    };
2759
2760                    let geometry = vk::AccelerationStructureGeometryKHR::default()
2761                        .geometry_type(vk::GeometryTypeKHR::TRIANGLES)
2762                        .geometry(vk::AccelerationStructureGeometryDataKHR {
2763                            triangles: triangle_data,
2764                        })
2765                        .flags(conv::map_acceleration_structure_geometry_flags(
2766                            triangles.flags,
2767                        ));
2768
2769                    geometries.push(geometry);
2770                    primitive_counts.push(pritive_count);
2771                }
2772                (geometries, primitive_counts)
2773            }
2774            crate::AccelerationStructureEntries::AABBs(ref in_geometries) => {
2775                let mut primitive_counts =
2776                    smallvec::SmallVec::<[u32; CAPACITY]>::with_capacity(in_geometries.len());
2777                let mut geometries = smallvec::SmallVec::<
2778                    [vk::AccelerationStructureGeometryKHR; CAPACITY],
2779                >::with_capacity(in_geometries.len());
2780                for aabb in in_geometries {
2781                    let aabbs_data = vk::AccelerationStructureGeometryAabbsDataKHR::default()
2782                        .stride(aabb.stride);
2783
2784                    let geometry = vk::AccelerationStructureGeometryKHR::default()
2785                        .geometry_type(vk::GeometryTypeKHR::AABBS)
2786                        .geometry(vk::AccelerationStructureGeometryDataKHR { aabbs: aabbs_data })
2787                        .flags(conv::map_acceleration_structure_geometry_flags(aabb.flags));
2788
2789                    geometries.push(geometry);
2790                    primitive_counts.push(aabb.count);
2791                }
2792                (geometries, primitive_counts)
2793            }
2794        };
2795
2796        let ty = match *desc.entries {
2797            crate::AccelerationStructureEntries::Instances(_) => {
2798                vk::AccelerationStructureTypeKHR::TOP_LEVEL
2799            }
2800            _ => vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL,
2801        };
2802
2803        let geometry_info = vk::AccelerationStructureBuildGeometryInfoKHR::default()
2804            .ty(ty)
2805            .flags(conv::map_acceleration_structure_flags(desc.flags))
2806            .geometries(&geometries);
2807
2808        let mut raw = Default::default();
2809        unsafe {
2810            ray_tracing_functions
2811                .acceleration_structure
2812                .get_acceleration_structure_build_sizes(
2813                    vk::AccelerationStructureBuildTypeKHR::DEVICE,
2814                    &geometry_info,
2815                    &primitive_counts,
2816                    &mut raw,
2817                )
2818        }
2819
2820        crate::AccelerationStructureBuildSizes {
2821            acceleration_structure_size: raw.acceleration_structure_size,
2822            update_scratch_size: raw.update_scratch_size,
2823            build_scratch_size: raw.build_scratch_size,
2824        }
2825    }
2826
2827    unsafe fn get_acceleration_structure_device_address(
2828        &self,
2829        acceleration_structure: &super::AccelerationStructure,
2830    ) -> wgt::BufferAddress {
2831        let ray_tracing_functions = self
2832            .shared
2833            .extension_fns
2834            .ray_tracing
2835            .as_ref()
2836            .expect("Feature `RAY_TRACING` not enabled");
2837
2838        unsafe {
2839            ray_tracing_functions
2840                .acceleration_structure
2841                .get_acceleration_structure_device_address(
2842                    &vk::AccelerationStructureDeviceAddressInfoKHR::default()
2843                        .acceleration_structure(acceleration_structure.raw),
2844                )
2845        }
2846    }
2847
2848    unsafe fn create_acceleration_structure(
2849        &self,
2850        desc: &crate::AccelerationStructureDescriptor,
2851    ) -> Result<super::AccelerationStructure, crate::DeviceError> {
2852        let ray_tracing_functions = self
2853            .shared
2854            .extension_fns
2855            .ray_tracing
2856            .as_ref()
2857            .expect("Feature `RAY_TRACING` not enabled");
2858
2859        let vk_buffer_info = vk::BufferCreateInfo::default()
2860            .size(desc.size)
2861            .usage(
2862                vk::BufferUsageFlags::ACCELERATION_STRUCTURE_STORAGE_KHR
2863                    | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
2864            )
2865            .sharing_mode(vk::SharingMode::EXCLUSIVE);
2866
2867        unsafe {
2868            let raw_buffer = self
2869                .shared
2870                .raw
2871                .create_buffer(&vk_buffer_info, None)
2872                .map_err(super::map_host_device_oom_and_ioca_err)?;
2873
2874            let requirements = self.shared.raw.get_buffer_memory_requirements(raw_buffer);
2875
2876            self.error_if_would_oom_on_resource_allocation(
2877                gpu_allocator::MemoryLocation::GpuOnly,
2878                &requirements,
2879            )
2880            .inspect_err(|_| {
2881                self.shared.raw.destroy_buffer(raw_buffer, None);
2882            })?;
2883
2884            let name = desc
2885                .label
2886                .unwrap_or("Unlabeled acceleration structure buffer");
2887
2888            let allocation = self
2889                .mem_allocator
2890                .lock()
2891                .allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
2892                    name,
2893                    requirements,
2894                    location: gpu_allocator::MemoryLocation::GpuOnly,
2895                    linear: true, // Buffers are always linear
2896                    allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
2897                })
2898                .inspect_err(|_| {
2899                    self.shared.raw.destroy_buffer(raw_buffer, None);
2900                })?;
2901
2902            self.shared
2903                .raw
2904                .bind_buffer_memory(raw_buffer, allocation.memory(), allocation.offset())
2905                .map_err(super::map_host_device_oom_and_ioca_err)
2906                .inspect_err(|_| {
2907                    self.shared.raw.destroy_buffer(raw_buffer, None);
2908                })?;
2909
2910            if let Some(label) = desc.label {
2911                self.shared.set_object_name(raw_buffer, label);
2912            }
2913
2914            let vk_info = vk::AccelerationStructureCreateInfoKHR::default()
2915                .buffer(raw_buffer)
2916                .offset(0)
2917                .size(desc.size)
2918                .ty(conv::map_acceleration_structure_format(desc.format));
2919
2920            let raw_acceleration_structure = ray_tracing_functions
2921                .acceleration_structure
2922                .create_acceleration_structure(&vk_info, None)
2923                .map_err(super::map_host_oom_and_ioca_err)
2924                .inspect_err(|_| {
2925                    self.shared.raw.destroy_buffer(raw_buffer, None);
2926                })?;
2927
2928            if let Some(label) = desc.label {
2929                self.shared
2930                    .set_object_name(raw_acceleration_structure, label);
2931            }
2932
2933            let pool = if desc.allow_compaction {
2934                let vk_info = vk::QueryPoolCreateInfo::default()
2935                    .query_type(vk::QueryType::ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR)
2936                    .query_count(1);
2937
2938                let raw = self
2939                    .shared
2940                    .raw
2941                    .create_query_pool(&vk_info, None)
2942                    .map_err(super::map_host_device_oom_err)
2943                    .inspect_err(|_| {
2944                        ray_tracing_functions
2945                            .acceleration_structure
2946                            .destroy_acceleration_structure(raw_acceleration_structure, None);
2947                        self.shared.raw.destroy_buffer(raw_buffer, None);
2948                    })?;
2949                Some(raw)
2950            } else {
2951                None
2952            };
2953
2954            Ok(super::AccelerationStructure {
2955                raw: raw_acceleration_structure,
2956                buffer: raw_buffer,
2957                allocation,
2958                compacted_size_query: pool,
2959            })
2960        }
2961    }
2962
2963    unsafe fn destroy_acceleration_structure(
2964        &self,
2965        acceleration_structure: super::AccelerationStructure,
2966    ) {
2967        let ray_tracing_functions = self
2968            .shared
2969            .extension_fns
2970            .ray_tracing
2971            .as_ref()
2972            .expect("Feature `RAY_TRACING` not enabled");
2973
2974        unsafe {
2975            ray_tracing_functions
2976                .acceleration_structure
2977                .destroy_acceleration_structure(acceleration_structure.raw, None);
2978            self.shared
2979                .raw
2980                .destroy_buffer(acceleration_structure.buffer, None);
2981            let result = self
2982                .mem_allocator
2983                .lock()
2984                .free(acceleration_structure.allocation);
2985            if let Err(err) = result {
2986                log::warn!("Failed to free buffer acceleration structure: {err}");
2987            }
2988            if let Some(query) = acceleration_structure.compacted_size_query {
2989                self.shared.raw.destroy_query_pool(query, None)
2990            }
2991        }
2992    }
2993
2994    fn get_internal_counters(&self) -> wgt::HalCounters {
2995        self.counters
2996            .memory_allocations
2997            .set(self.shared.memory_allocations_counter.read());
2998
2999        self.counters.as_ref().clone()
3000    }
3001
3002    fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
3003        let gpu_allocator::AllocatorReport {
3004            allocations,
3005            blocks,
3006            total_allocated_bytes,
3007            total_capacity_bytes,
3008        } = self.mem_allocator.lock().generate_report();
3009
3010        let allocations = allocations
3011            .into_iter()
3012            .map(|alloc| wgt::AllocationReport {
3013                name: alloc.name,
3014                offset: alloc.offset,
3015                size: alloc.size,
3016            })
3017            .collect();
3018
3019        let blocks = blocks
3020            .into_iter()
3021            .map(|block| wgt::MemoryBlockReport {
3022                size: block.size,
3023                allocations: block.allocations.clone(),
3024            })
3025            .collect();
3026
3027        Some(wgt::AllocatorReport {
3028            allocations,
3029            blocks,
3030            total_allocated_bytes,
3031            total_reserved_bytes: total_capacity_bytes,
3032        })
3033    }
3034
3035    fn tlas_instance_to_bytes(&self, instance: TlasInstance, to_extend: &mut Vec<u8>) {
3036        const MAX_U24: u32 = (1u32 << 24u32) - 1u32;
3037        let temp = RawTlasInstance {
3038            transform: instance.transform,
3039            custom_data_and_mask: (instance.custom_data & MAX_U24)
3040                | (u32::from(instance.mask) << 24),
3041            shader_binding_table_record_offset_and_flags: (instance
3042                .pipeline_intersection_data_offset
3043                & MAX_U24),
3044            acceleration_structure_reference: instance.blas_address,
3045        };
3046        to_extend.extend_from_slice(bytemuck::bytes_of(&temp))
3047    }
3048
3049    fn check_if_oom(&self) -> Result<(), crate::DeviceError> {
3050        let Some(threshold) = self
3051            .shared
3052            .instance
3053            .memory_budget_thresholds
3054            .for_device_loss
3055        else {
3056            return Ok(());
3057        };
3058
3059        let Some(memory_properties) = self.get_memory_properties() else {
3060            return Ok(());
3061        };
3062
3063        for i in 0..memory_properties.heaps().len() {
3064            let heap_usage = memory_properties.heap_usage()[i];
3065            let heap_budget = memory_properties.heap_budget()[i];
3066
3067            if heap_usage >= heap_budget / 100 * threshold as u64 {
3068                return Err(crate::DeviceError::OutOfMemory);
3069            }
3070        }
3071
3072        Ok(())
3073    }
3074}
3075
3076impl super::DeviceShared {
3077    pub(super) fn new_binary_semaphore(
3078        &self,
3079        name: &str,
3080    ) -> Result<vk::Semaphore, crate::DeviceError> {
3081        unsafe {
3082            let semaphore = self
3083                .raw
3084                .create_semaphore(&vk::SemaphoreCreateInfo::default(), None)
3085                .map_err(super::map_host_device_oom_err)?;
3086
3087            if !self
3088                .instance
3089                .flags
3090                .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
3091            {
3092                self.set_object_name(semaphore, name);
3093            }
3094
3095            Ok(semaphore)
3096        }
3097    }
3098
3099    pub(super) fn wait_for_fence(
3100        &self,
3101        fence: &super::Fence,
3102        wait_value: crate::FenceValue,
3103        timeout_ns: u64,
3104    ) -> Result<bool, crate::DeviceError> {
3105        profiling::scope!("Device::wait");
3106        match *fence {
3107            super::Fence::TimelineSemaphore(raw) => {
3108                let semaphores = [raw];
3109                let values = [wait_value];
3110                let vk_info = vk::SemaphoreWaitInfo::default()
3111                    .semaphores(&semaphores)
3112                    .values(&values);
3113                let result = match self.extension_fns.timeline_semaphore {
3114                    Some(super::ExtensionFn::Extension(ref ext)) => unsafe {
3115                        ext.wait_semaphores(&vk_info, timeout_ns)
3116                    },
3117                    Some(super::ExtensionFn::Promoted) => unsafe {
3118                        self.raw.wait_semaphores(&vk_info, timeout_ns)
3119                    },
3120                    None => unreachable!(),
3121                };
3122                match result {
3123                    Ok(()) => Ok(true),
3124                    Err(vk::Result::TIMEOUT) => Ok(false),
3125                    Err(other) => Err(super::map_host_device_oom_and_lost_err(other)),
3126                }
3127            }
3128            super::Fence::FencePool(ref pool) => {
3129                let pool = pool.read();
3130                let super::FencePool {
3131                    last_completed,
3132                    ref active,
3133                    free: _,
3134                } = *pool;
3135                if wait_value <= last_completed {
3136                    Ok(true)
3137                } else {
3138                    match active.iter().find(|&&(value, _)| value >= wait_value) {
3139                        Some((_, fence)) => {
3140                            // clone to show we are using this fence while the pool is unlocked.
3141                            let fence = fence.clone();
3142                            drop(pool);
3143                            match unsafe {
3144                                self.raw.wait_for_fences(
3145                                    core::slice::from_ref(&fence),
3146                                    true,
3147                                    timeout_ns,
3148                                )
3149                            } {
3150                                Ok(()) => Ok(true),
3151                                Err(vk::Result::TIMEOUT) => Ok(false),
3152                                Err(other) => Err(super::map_host_device_oom_and_lost_err(other)),
3153                            }
3154                        }
3155                        None => {
3156                            crate::hal_usage_error(format!(
3157                                "no signals reached value {wait_value}"
3158                            ));
3159                        }
3160                    }
3161                }
3162            }
3163        }
3164    }
3165}
3166
3167struct ImageWithoutMemory {
3168    raw: vk::Image,
3169    requirements: vk::MemoryRequirements,
3170}