Skip to main content

wgpu_hal/vulkan/
device.rs

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