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