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