Skip to main content

wgpu_hal/vulkan/
adapter.rs

1use alloc::{borrow::ToOwned as _, boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
2use core::{ffi::CStr, marker::PhantomData};
3
4use ash::{ext, google, khr, vk};
5use parking_lot::Mutex;
6
7use crate::{vulkan::semaphore_list::SemaphoreList, AllocationSizes};
8
9use super::semaphore_list::SemaphoreListMode;
10
11fn depth_stencil_required_flags() -> vk::FormatFeatureFlags {
12    vk::FormatFeatureFlags::SAMPLED_IMAGE | vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT
13}
14
15const INDEXING_FEATURES: wgt::Features = wgt::Features::TEXTURE_BINDING_ARRAY
16    .union(wgt::Features::BUFFER_BINDING_ARRAY)
17    .union(wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY)
18    .union(wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING)
19    .union(wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
20    .union(wgt::Features::UNIFORM_BUFFER_BINDING_ARRAYS)
21    .union(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY);
22#[expect(rustdoc::private_intra_doc_links)]
23/// Features supported by a [`vk::PhysicalDevice`] and its extensions.
24///
25/// This is used in two phases:
26///
27/// - When enumerating adapters, this represents the features offered by the
28///   adapter. [`Instance::expose_adapter`] calls `vkGetPhysicalDeviceFeatures2`
29///   (or `vkGetPhysicalDeviceFeatures` if that is not available) to collect
30///   this information about the `VkPhysicalDevice` represented by the
31///   `wgpu_hal::ExposedAdapter`.
32///
33/// - When opening a device, this represents the features we would like to
34///   enable. At `wgpu_hal::Device` construction time,
35///   [`PhysicalDeviceFeatures::from_extensions_and_requested_features`]
36///   constructs an value of this type indicating which Vulkan features to
37///   enable, based on the `wgpu_types::Features` requested.
38///
39/// [`Instance::expose_adapter`]: super::Instance::expose_adapter
40#[derive(Debug, Default)]
41pub struct PhysicalDeviceFeatures {
42    /// Basic Vulkan 1.0 features.
43    core: vk::PhysicalDeviceFeatures,
44
45    /// Features provided by `VK_EXT_descriptor_indexing`, promoted to Vulkan 1.2.
46    pub(super) descriptor_indexing:
47        Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT<'static>>,
48
49    /// Features provided by `VK_KHR_timeline_semaphore`, promoted to Vulkan 1.2
50    timeline_semaphore: Option<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR<'static>>,
51
52    /// Features provided by `VK_EXT_image_robustness`, promoted to Vulkan 1.3
53    image_robustness: Option<vk::PhysicalDeviceImageRobustnessFeaturesEXT<'static>>,
54
55    /// Features provided by `VK_EXT_robustness2`.
56    robustness2: Option<vk::PhysicalDeviceRobustness2FeaturesEXT<'static>>,
57
58    /// Features provided by `VK_KHR_multiview`, promoted to Vulkan 1.1.
59    multiview: Option<vk::PhysicalDeviceMultiviewFeaturesKHR<'static>>,
60
61    /// Features provided by `VK_KHR_sampler_ycbcr_conversion`, promoted to Vulkan 1.1.
62    sampler_ycbcr_conversion: Option<vk::PhysicalDeviceSamplerYcbcrConversionFeatures<'static>>,
63
64    /// Features provided by `VK_EXT_texture_compression_astc_hdr`, promoted to Vulkan 1.3.
65    astc_hdr: Option<vk::PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT<'static>>,
66
67    /// Features provided by `VK_KHR_shader_float16_int8`, promoted to Vulkan 1.2
68    shader_float16_int8: Option<vk::PhysicalDeviceShaderFloat16Int8Features<'static>>,
69
70    /// Features provided by `VK_KHR_16bit_storage`, promoted to Vulkan 1.1
71    _16bit_storage: Option<vk::PhysicalDevice16BitStorageFeatures<'static>>,
72
73    /// Features provided by `VK_KHR_acceleration_structure`.
74    acceleration_structure: Option<vk::PhysicalDeviceAccelerationStructureFeaturesKHR<'static>>,
75
76    /// Features provided by `VK_KHR_buffer_device_address`, promoted to Vulkan 1.2.
77    ///
78    /// We only use this feature for
79    /// [`Features::EXPERIMENTAL_RAY_QUERY`], which requires
80    /// `VK_KHR_acceleration_structure`, which depends on
81    /// `VK_KHR_buffer_device_address`, so [`Instance::expose_adapter`] only
82    /// bothers to check if `VK_KHR_acceleration_structure` is available,
83    /// leaving this `None`.
84    ///
85    /// However, we do populate this when creating a device if
86    /// [`Features::EXPERIMENTAL_RAY_QUERY`] is requested.
87    ///
88    /// [`Instance::expose_adapter`]: super::Instance::expose_adapter
89    /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
90    buffer_device_address: Option<vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR<'static>>,
91
92    /// Features provided by `VK_KHR_ray_query`,
93    ///
94    /// Vulkan requires that the feature be present if the `VK_KHR_ray_query`
95    /// extension is present, so [`Instance::expose_adapter`] doesn't bother retrieving
96    /// this from `vkGetPhysicalDeviceFeatures2`.
97    ///
98    /// However, we do populate this when creating a device if ray tracing is requested.
99    ///
100    /// [`Instance::expose_adapter`]: super::Instance::expose_adapter
101    ray_query: Option<vk::PhysicalDeviceRayQueryFeaturesKHR<'static>>,
102
103    /// Features provided by `VK_KHR_zero_initialize_workgroup_memory`, promoted
104    /// to Vulkan 1.3.
105    zero_initialize_workgroup_memory:
106        Option<vk::PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures<'static>>,
107    position_fetch: Option<vk::PhysicalDeviceRayTracingPositionFetchFeaturesKHR<'static>>,
108
109    /// Features provided by `VK_KHR_shader_atomic_int64`, promoted to Vulkan 1.2.
110    shader_atomic_int64: Option<vk::PhysicalDeviceShaderAtomicInt64Features<'static>>,
111
112    /// Features provided by `VK_EXT_shader_image_atomic_int64`
113    shader_image_atomic_int64: Option<vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT<'static>>,
114
115    /// Features provided by `VK_EXT_shader_atomic_float`.
116    shader_atomic_float: Option<vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT<'static>>,
117
118    /// Features provided by `VK_EXT_subgroup_size_control`, promoted to Vulkan 1.3.
119    subgroup_size_control: Option<vk::PhysicalDeviceSubgroupSizeControlFeatures<'static>>,
120
121    /// Features provided by `VK_KHR_maintenance4`, promoted to Vulkan 1.3.
122    maintenance4: Option<vk::PhysicalDeviceMaintenance4FeaturesKHR<'static>>,
123
124    /// Features proved by `VK_EXT_mesh_shader`
125    mesh_shader: Option<vk::PhysicalDeviceMeshShaderFeaturesEXT<'static>>,
126
127    /// Features provided by `VK_KHR_shader_integer_dot_product`, promoted to Vulkan 1.3.
128    shader_integer_dot_product:
129        Option<vk::PhysicalDeviceShaderIntegerDotProductFeaturesKHR<'static>>,
130
131    /// Features provided by `VK_KHR_fragment_shader_barycentric`
132    shader_barycentrics: Option<vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR<'static>>,
133
134    /// Features provided by `VK_KHR_portability_subset`.
135    ///
136    /// Strictly speaking this tells us what features we *don't* have compared to core.
137    portability_subset: Option<vk::PhysicalDevicePortabilitySubsetFeaturesKHR<'static>>,
138
139    /// Features provided by `VK_KHR_cooperative_matrix`
140    cooperative_matrix: Option<vk::PhysicalDeviceCooperativeMatrixFeaturesKHR<'static>>,
141
142    /// Features provided by `VK_KHR_vulkan_memory_model`, promoted to Vulkan 1.2
143    vulkan_memory_model: Option<vk::PhysicalDeviceVulkanMemoryModelFeaturesKHR<'static>>,
144
145    shader_draw_parameters: Option<vk::PhysicalDeviceShaderDrawParametersFeatures<'static>>,
146}
147
148impl PhysicalDeviceFeatures {
149    pub fn get_core(&self) -> vk::PhysicalDeviceFeatures {
150        self.core
151    }
152
153    /// Add the members of `self` into `info.enabled_features` and its `p_next` chain.
154    pub fn add_to_device_create<'a>(
155        &'a mut self,
156        mut info: vk::DeviceCreateInfo<'a>,
157    ) -> vk::DeviceCreateInfo<'a> {
158        info = info.enabled_features(&self.core);
159        if let Some(ref mut feature) = self.descriptor_indexing {
160            info = info.push_next(feature);
161        }
162        if let Some(ref mut feature) = self.timeline_semaphore {
163            info = info.push_next(feature);
164        }
165        if let Some(ref mut feature) = self.image_robustness {
166            info = info.push_next(feature);
167        }
168        if let Some(ref mut feature) = self.robustness2 {
169            info = info.push_next(feature);
170        }
171        if let Some(ref mut feature) = self.multiview {
172            info = info.push_next(feature);
173        }
174        if let Some(ref mut feature) = self.astc_hdr {
175            info = info.push_next(feature);
176        }
177        if let Some(ref mut feature) = self.shader_float16_int8 {
178            info = info.push_next(feature);
179        }
180        if let Some(ref mut feature) = self._16bit_storage {
181            info = info.push_next(feature);
182        }
183        if let Some(ref mut feature) = self.zero_initialize_workgroup_memory {
184            info = info.push_next(feature);
185        }
186        if let Some(ref mut feature) = self.acceleration_structure {
187            info = info.push_next(feature);
188        }
189        if let Some(ref mut feature) = self.buffer_device_address {
190            info = info.push_next(feature);
191        }
192        if let Some(ref mut feature) = self.ray_query {
193            info = info.push_next(feature);
194        }
195        if let Some(ref mut feature) = self.shader_atomic_int64 {
196            info = info.push_next(feature);
197        }
198        if let Some(ref mut feature) = self.position_fetch {
199            info = info.push_next(feature);
200        }
201        if let Some(ref mut feature) = self.shader_image_atomic_int64 {
202            info = info.push_next(feature);
203        }
204        if let Some(ref mut feature) = self.shader_atomic_float {
205            info = info.push_next(feature);
206        }
207        if let Some(ref mut feature) = self.subgroup_size_control {
208            info = info.push_next(feature);
209        }
210        if let Some(ref mut feature) = self.maintenance4 {
211            info = info.push_next(feature);
212        }
213        if let Some(ref mut feature) = self.mesh_shader {
214            info = info.push_next(feature);
215        }
216        if let Some(ref mut feature) = self.shader_integer_dot_product {
217            info = info.push_next(feature);
218        }
219        if let Some(ref mut feature) = self.shader_barycentrics {
220            info = info.push_next(feature);
221        }
222        if let Some(ref mut feature) = self.portability_subset {
223            info = info.push_next(feature);
224        }
225        if let Some(ref mut feature) = self.cooperative_matrix {
226            info = info.push_next(feature);
227        }
228        if let Some(ref mut feature) = self.vulkan_memory_model {
229            info = info.push_next(feature);
230        }
231        if let Some(ref mut feature) = self.shader_draw_parameters {
232            info = info.push_next(feature);
233        }
234        info
235    }
236
237    fn supports_storage_input_output_16(&self) -> bool {
238        self._16bit_storage
239            .as_ref()
240            .map(|features| features.storage_input_output16 != 0)
241            .unwrap_or(false)
242    }
243
244    /// Create a `PhysicalDeviceFeatures` that can be used to create a logical
245    /// device.
246    ///
247    /// Return a `PhysicalDeviceFeatures` value capturing all the Vulkan
248    /// features needed for the given [`Features`], [`DownlevelFlags`], and
249    /// [`PrivateCapabilities`]. You can use the returned value's
250    /// [`add_to_device_create`] method to configure a
251    /// [`vk::DeviceCreateInfo`] to build a logical device providing those
252    /// features.
253    ///
254    /// To ensure that the returned value is able to select all the Vulkan
255    /// features needed to express `requested_features`, `downlevel_flags`, and
256    /// `private_caps`:
257    ///
258    /// - The given `enabled_extensions` set must include all the extensions
259    ///   selected by [`Adapter::required_device_extensions`] when passed
260    ///   `features`.
261    ///
262    /// - The given `device_api_version` must be the Vulkan API version of the
263    ///   physical device we will use to create the logical device.
264    ///
265    /// [`Features`]: wgt::Features
266    /// [`DownlevelFlags`]: wgt::DownlevelFlags
267    /// [`PrivateCapabilities`]: super::PrivateCapabilities
268    /// [`add_to_device_create`]: PhysicalDeviceFeatures::add_to_device_create
269    /// [`Adapter::required_device_extensions`]: super::Adapter::required_device_extensions
270    fn from_extensions_and_requested_features(
271        phd_capabilities: &PhysicalDeviceProperties,
272        phd_features: &PhysicalDeviceFeatures,
273        enabled_extensions: &[&'static CStr],
274        requested_features: wgt::Features,
275        downlevel_flags: wgt::DownlevelFlags,
276        private_caps: &super::PrivateCapabilities,
277    ) -> Self {
278        let device_api_version = phd_capabilities.device_api_version;
279        let needs_bindless = requested_features.intersects(
280            wgt::Features::TEXTURE_BINDING_ARRAY
281                | wgt::Features::BUFFER_BINDING_ARRAY
282                | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
283                | wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
284                | wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
285        );
286        let needs_partially_bound =
287            requested_features.intersects(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY);
288
289        Self {
290            // vk::PhysicalDeviceFeatures is a struct composed of Bool32's while
291            // Features is a bitfield so we need to map everything manually
292            core: vk::PhysicalDeviceFeatures::default()
293                .robust_buffer_access(private_caps.robust_buffer_access)
294                .independent_blend(downlevel_flags.contains(wgt::DownlevelFlags::INDEPENDENT_BLEND))
295                .sample_rate_shading(
296                    downlevel_flags.contains(wgt::DownlevelFlags::MULTISAMPLED_SHADING),
297                )
298                .image_cube_array(
299                    downlevel_flags.contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES),
300                )
301                .draw_indirect_first_instance(
302                    requested_features.contains(wgt::Features::INDIRECT_FIRST_INSTANCE),
303                )
304                //.dual_src_blend(requested_features.contains(wgt::Features::DUAL_SRC_BLENDING))
305                .multi_draw_indirect(phd_features.core.multi_draw_indirect != 0)
306                .fill_mode_non_solid(requested_features.intersects(
307                    wgt::Features::POLYGON_MODE_LINE | wgt::Features::POLYGON_MODE_POINT,
308                ))
309                //.depth_bounds(requested_features.contains(wgt::Features::DEPTH_BOUNDS))
310                //.alpha_to_one(requested_features.contains(wgt::Features::ALPHA_TO_ONE))
311                //.multi_viewport(requested_features.contains(wgt::Features::MULTI_VIEWPORTS))
312                .sampler_anisotropy(
313                    downlevel_flags.contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING),
314                )
315                .texture_compression_etc2(
316                    requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ETC2),
317                )
318                .texture_compression_astc_ldr(
319                    requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC),
320                )
321                .texture_compression_bc(
322                    requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_BC),
323                    // BC provides formats for Sliced 3D
324                )
325                //.occlusion_query_precise(requested_features.contains(wgt::Features::PRECISE_OCCLUSION_QUERY))
326                .pipeline_statistics_query(
327                    requested_features.contains(wgt::Features::PIPELINE_STATISTICS_QUERY),
328                )
329                .vertex_pipeline_stores_and_atomics(
330                    requested_features.contains(wgt::Features::VERTEX_WRITABLE_STORAGE),
331                )
332                .fragment_stores_and_atomics(
333                    downlevel_flags.contains(wgt::DownlevelFlags::FRAGMENT_WRITABLE_STORAGE),
334                )
335                //.shader_image_gather_extended(
336                //.shader_storage_image_extended_formats(
337                .shader_uniform_buffer_array_dynamic_indexing(
338                    requested_features.contains(wgt::Features::BUFFER_BINDING_ARRAY),
339                )
340                .shader_storage_buffer_array_dynamic_indexing(requested_features.contains(
341                    wgt::Features::BUFFER_BINDING_ARRAY
342                        | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
343                ))
344                .shader_sampled_image_array_dynamic_indexing(
345                    requested_features.contains(wgt::Features::TEXTURE_BINDING_ARRAY),
346                )
347                .shader_storage_buffer_array_dynamic_indexing(requested_features.contains(
348                    wgt::Features::TEXTURE_BINDING_ARRAY
349                        | wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY,
350                ))
351                //.shader_storage_image_array_dynamic_indexing(
352                .shader_clip_distance(requested_features.contains(wgt::Features::CLIP_DISTANCES))
353                //.shader_cull_distance(requested_features.contains(wgt::Features::SHADER_CULL_DISTANCE))
354                .shader_float64(requested_features.contains(wgt::Features::SHADER_F64))
355                .shader_int64(requested_features.contains(wgt::Features::SHADER_INT64))
356                .shader_int16(requested_features.contains(wgt::Features::SHADER_I16))
357                //.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
358                .geometry_shader(requested_features.contains(wgt::Features::PRIMITIVE_INDEX))
359                .depth_clamp(requested_features.contains(wgt::Features::DEPTH_CLIP_CONTROL))
360                .dual_src_blend(requested_features.contains(wgt::Features::DUAL_SOURCE_BLENDING)),
361            descriptor_indexing: if requested_features.intersects(INDEXING_FEATURES) {
362                Some(
363                    vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default()
364                        .shader_sampled_image_array_non_uniform_indexing(needs_bindless)
365                        .shader_storage_image_array_non_uniform_indexing(needs_bindless)
366                        .shader_storage_buffer_array_non_uniform_indexing(needs_bindless)
367                        .descriptor_binding_sampled_image_update_after_bind(needs_bindless)
368                        .descriptor_binding_storage_image_update_after_bind(needs_bindless)
369                        .descriptor_binding_storage_buffer_update_after_bind(needs_bindless)
370                        .descriptor_binding_partially_bound(needs_partially_bound),
371                )
372            } else {
373                None
374            },
375            timeline_semaphore: if device_api_version >= vk::API_VERSION_1_2
376                || enabled_extensions.contains(&khr::timeline_semaphore::NAME)
377            {
378                Some(
379                    vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR::default()
380                        .timeline_semaphore(private_caps.timeline_semaphores),
381                )
382            } else {
383                None
384            },
385            image_robustness: if device_api_version >= vk::API_VERSION_1_3
386                || enabled_extensions.contains(&ext::image_robustness::NAME)
387            {
388                Some(
389                    vk::PhysicalDeviceImageRobustnessFeaturesEXT::default()
390                        .robust_image_access(private_caps.robust_image_access),
391                )
392            } else {
393                None
394            },
395            robustness2: if enabled_extensions.contains(&ext::robustness2::NAME) {
396                Some(
397                    vk::PhysicalDeviceRobustness2FeaturesEXT::default()
398                        .robust_buffer_access2(private_caps.robust_buffer_access2)
399                        .robust_image_access2(private_caps.robust_image_access2),
400                )
401            } else {
402                None
403            },
404            multiview: if device_api_version >= vk::API_VERSION_1_1
405                || enabled_extensions.contains(&khr::multiview::NAME)
406            {
407                Some(
408                    vk::PhysicalDeviceMultiviewFeatures::default()
409                        .multiview(requested_features.contains(wgt::Features::MULTIVIEW)),
410                )
411            } else {
412                None
413            },
414            sampler_ycbcr_conversion: if device_api_version >= vk::API_VERSION_1_1
415                || enabled_extensions.contains(&khr::sampler_ycbcr_conversion::NAME)
416            {
417                Some(
418                    vk::PhysicalDeviceSamplerYcbcrConversionFeatures::default(), // .sampler_ycbcr_conversion(requested_features.contains(wgt::Features::TEXTURE_FORMAT_NV12))
419                )
420            } else {
421                None
422            },
423            astc_hdr: if enabled_extensions.contains(&ext::texture_compression_astc_hdr::NAME) {
424                Some(
425                    vk::PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT::default()
426                        .texture_compression_astc_hdr(true),
427                )
428            } else {
429                None
430            },
431            shader_float16_int8: match requested_features.contains(wgt::Features::SHADER_F16) {
432                shader_float16 if shader_float16 || private_caps.shader_int8 => Some(
433                    vk::PhysicalDeviceShaderFloat16Int8Features::default()
434                        .shader_float16(shader_float16)
435                        .shader_int8(private_caps.shader_int8),
436                ),
437                _ => None,
438            },
439            _16bit_storage: if requested_features.contains(wgt::Features::SHADER_F16) {
440                Some(
441                    vk::PhysicalDevice16BitStorageFeatures::default()
442                        .storage_buffer16_bit_access(true)
443                        .storage_input_output16(phd_features.supports_storage_input_output_16())
444                        .uniform_and_storage_buffer16_bit_access(true),
445                )
446            } else {
447                None
448            },
449            acceleration_structure: if enabled_extensions
450                .contains(&khr::acceleration_structure::NAME)
451            {
452                Some(
453                    vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default()
454                        .acceleration_structure(true)
455                        .descriptor_binding_acceleration_structure_update_after_bind(
456                            requested_features
457                                .contains(wgt::Features::ACCELERATION_STRUCTURE_BINDING_ARRAY),
458                        ),
459                )
460            } else {
461                None
462            },
463            buffer_device_address: if enabled_extensions.contains(&khr::buffer_device_address::NAME)
464            {
465                Some(
466                    vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR::default()
467                        .buffer_device_address(true),
468                )
469            } else {
470                None
471            },
472            ray_query: if enabled_extensions.contains(&khr::ray_query::NAME) {
473                Some(vk::PhysicalDeviceRayQueryFeaturesKHR::default().ray_query(true))
474            } else {
475                None
476            },
477            zero_initialize_workgroup_memory: if device_api_version >= vk::API_VERSION_1_3
478                || enabled_extensions.contains(&khr::zero_initialize_workgroup_memory::NAME)
479            {
480                Some(
481                    vk::PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures::default()
482                        .shader_zero_initialize_workgroup_memory(
483                            private_caps.zero_initialize_workgroup_memory,
484                        ),
485                )
486            } else {
487                None
488            },
489            shader_atomic_int64: if device_api_version >= vk::API_VERSION_1_2
490                || enabled_extensions.contains(&khr::shader_atomic_int64::NAME)
491            {
492                let needed = requested_features.intersects(
493                    wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS
494                        | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
495                );
496                Some(
497                    vk::PhysicalDeviceShaderAtomicInt64Features::default()
498                        .shader_buffer_int64_atomics(needed)
499                        .shader_shared_int64_atomics(needed),
500                )
501            } else {
502                None
503            },
504            shader_image_atomic_int64: if enabled_extensions
505                .contains(&ext::shader_image_atomic_int64::NAME)
506            {
507                let needed = requested_features.intersects(wgt::Features::TEXTURE_INT64_ATOMIC);
508                Some(
509                    vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT::default()
510                        .shader_image_int64_atomics(needed),
511                )
512            } else {
513                None
514            },
515            shader_atomic_float: if enabled_extensions.contains(&ext::shader_atomic_float::NAME) {
516                let needed = requested_features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC);
517                Some(
518                    vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT::default()
519                        .shader_buffer_float32_atomics(needed)
520                        .shader_buffer_float32_atomic_add(needed),
521                )
522            } else {
523                None
524            },
525            subgroup_size_control: if device_api_version >= vk::API_VERSION_1_3
526                || enabled_extensions.contains(&ext::subgroup_size_control::NAME)
527            {
528                Some(
529                    vk::PhysicalDeviceSubgroupSizeControlFeatures::default()
530                        .subgroup_size_control(true),
531                )
532            } else {
533                None
534            },
535            position_fetch: if enabled_extensions.contains(&khr::ray_tracing_position_fetch::NAME) {
536                Some(
537                    vk::PhysicalDeviceRayTracingPositionFetchFeaturesKHR::default()
538                        .ray_tracing_position_fetch(true),
539                )
540            } else {
541                None
542            },
543            mesh_shader: if enabled_extensions.contains(&ext::mesh_shader::NAME) {
544                let needed = requested_features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER);
545                let multiview_needed =
546                    requested_features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW);
547                Some(
548                    vk::PhysicalDeviceMeshShaderFeaturesEXT::default()
549                        .mesh_shader(needed)
550                        .task_shader(needed)
551                        .multiview_mesh_shader(multiview_needed),
552                )
553            } else {
554                None
555            },
556            maintenance4: if device_api_version >= vk::API_VERSION_1_3
557                || enabled_extensions.contains(&khr::maintenance4::NAME)
558            {
559                let needed = requested_features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER);
560                Some(vk::PhysicalDeviceMaintenance4Features::default().maintenance4(needed))
561            } else {
562                None
563            },
564            shader_integer_dot_product: if device_api_version >= vk::API_VERSION_1_3
565                || enabled_extensions.contains(&khr::shader_integer_dot_product::NAME)
566            {
567                Some(
568                    vk::PhysicalDeviceShaderIntegerDotProductFeaturesKHR::default()
569                        .shader_integer_dot_product(private_caps.shader_integer_dot_product),
570                )
571            } else {
572                None
573            },
574            shader_barycentrics: if enabled_extensions
575                .contains(&khr::fragment_shader_barycentric::NAME)
576            {
577                let needed = requested_features.intersects(
578                    wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX,
579                );
580                Some(
581                    vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR::default()
582                        .fragment_shader_barycentric(needed),
583                )
584            } else {
585                None
586            },
587            portability_subset: if enabled_extensions.contains(&khr::portability_subset::NAME) {
588                let multisample_array_needed =
589                    requested_features.intersects(wgt::Features::MULTISAMPLE_ARRAY);
590
591                Some(
592                    vk::PhysicalDevicePortabilitySubsetFeaturesKHR::default()
593                        .multisample_array_image(multisample_array_needed),
594                )
595            } else {
596                None
597            },
598            cooperative_matrix: if enabled_extensions.contains(&khr::cooperative_matrix::NAME) {
599                let needed =
600                    requested_features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX);
601                Some(
602                    vk::PhysicalDeviceCooperativeMatrixFeaturesKHR::default()
603                        .cooperative_matrix(needed),
604                )
605            } else {
606                None
607            },
608            vulkan_memory_model: if device_api_version >= vk::API_VERSION_1_2
609                || enabled_extensions.contains(&khr::vulkan_memory_model::NAME)
610            {
611                let needed =
612                    requested_features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX);
613                Some(
614                    vk::PhysicalDeviceVulkanMemoryModelFeaturesKHR::default()
615                        .vulkan_memory_model(needed)
616                        // The SPIR-V backend emits storage atomics with `Device`
617                        // memory scope, so whenever the Vulkan memory model is
618                        // enabled we must also enable device scope, otherwise the
619                        // validation layers report
620                        // `VUID-RuntimeSpirv-vulkanMemoryModel-06265`.
621                        .vulkan_memory_model_device_scope(needed),
622                )
623            } else {
624                None
625            },
626            shader_draw_parameters: if device_api_version >= vk::API_VERSION_1_1 {
627                let needed = requested_features.contains(wgt::Features::SHADER_DRAW_INDEX);
628                Some(
629                    vk::PhysicalDeviceShaderDrawParametersFeatures::default()
630                        .shader_draw_parameters(needed),
631                )
632            } else {
633                None
634            },
635        }
636    }
637
638    /// Compute the wgpu [`Features`] and [`DownlevelFlags`] supported by a physical device.
639    ///
640    /// Given `self`, together with the instance and physical device it was
641    /// built from, and a `caps` also built from those, determine which wgpu
642    /// features and downlevel flags the device can support.
643    ///
644    /// [`Features`]: wgt::Features
645    /// [`DownlevelFlags`]: wgt::DownlevelFlags
646    fn to_wgpu(
647        &self,
648        instance: &ash::Instance,
649        phd: vk::PhysicalDevice,
650        caps: &PhysicalDeviceProperties,
651        queue_props: &vk::QueueFamilyProperties,
652    ) -> (wgt::Features, wgt::DownlevelFlags) {
653        use wgt::{DownlevelFlags as Df, Features as F};
654        let mut features = F::empty()
655            | F::MAPPABLE_PRIMARY_BUFFERS
656            | F::IMMEDIATES
657            | F::ADDRESS_MODE_CLAMP_TO_BORDER
658            | F::ADDRESS_MODE_CLAMP_TO_ZERO
659            | F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
660            | F::CLEAR_TEXTURE
661            | F::PIPELINE_CACHE
662            | F::SHADER_EARLY_DEPTH_TEST
663            | F::TEXTURE_ATOMIC
664            | F::PASSTHROUGH_SHADERS
665            | F::MEMORY_DECORATION_COHERENT
666            | F::MEMORY_DECORATION_VOLATILE;
667
668        let mut dl_flags = Df::COMPUTE_SHADERS
669            | Df::BASE_VERTEX
670            | Df::READ_ONLY_DEPTH_STENCIL
671            | Df::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES
672            | Df::COMPARISON_SAMPLERS
673            | Df::VERTEX_STORAGE
674            | Df::FRAGMENT_STORAGE
675            | Df::DEPTH_TEXTURE_AND_BUFFER_COPIES
676            | Df::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED
677            | Df::UNRESTRICTED_INDEX_BUFFER
678            | Df::INDIRECT_EXECUTION
679            | Df::VIEW_FORMATS
680            | Df::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES
681            | Df::NONBLOCKING_QUERY_RESOLVE
682            | Df::SHADER_F16_IN_F32;
683
684        dl_flags.set(
685            Df::SURFACE_VIEW_FORMATS,
686            caps.supports_extension(khr::swapchain_mutable_format::NAME),
687        );
688        dl_flags.set(Df::CUBE_ARRAY_TEXTURES, self.core.image_cube_array != 0);
689        dl_flags.set(Df::ANISOTROPIC_FILTERING, self.core.sampler_anisotropy != 0);
690        dl_flags.set(
691            Df::FRAGMENT_WRITABLE_STORAGE,
692            self.core.fragment_stores_and_atomics != 0,
693        );
694        dl_flags.set(Df::MULTISAMPLED_SHADING, self.core.sample_rate_shading != 0);
695        dl_flags.set(Df::INDEPENDENT_BLEND, self.core.independent_blend != 0);
696        dl_flags.set(
697            Df::FULL_DRAW_INDEX_UINT32,
698            self.core.full_draw_index_uint32 != 0,
699        );
700        dl_flags.set(Df::DEPTH_BIAS_CLAMP, self.core.depth_bias_clamp != 0);
701
702        features.set(
703            F::TIMESTAMP_QUERY
704                | F::TIMESTAMP_QUERY_INSIDE_ENCODERS
705                | F::TIMESTAMP_QUERY_INSIDE_PASSES,
706            // Vulkan strictly defines this as either 36-64, or zero.
707            queue_props.timestamp_valid_bits >= 36,
708        );
709        features.set(
710            F::INDIRECT_FIRST_INSTANCE,
711            self.core.draw_indirect_first_instance != 0,
712        );
713        //if self.core.dual_src_blend != 0
714        features.set(F::POLYGON_MODE_LINE, self.core.fill_mode_non_solid != 0);
715        features.set(F::POLYGON_MODE_POINT, self.core.fill_mode_non_solid != 0);
716        //if self.core.depth_bounds != 0 {
717        //if self.core.alpha_to_one != 0 {
718        //if self.core.multi_viewport != 0 {
719        features.set(
720            F::TEXTURE_COMPRESSION_ETC2,
721            self.core.texture_compression_etc2 != 0,
722        );
723        features.set(
724            F::TEXTURE_COMPRESSION_ASTC,
725            self.core.texture_compression_astc_ldr != 0,
726        );
727        features.set(
728            F::TEXTURE_COMPRESSION_BC,
729            self.core.texture_compression_bc != 0,
730        );
731        features.set(
732            F::TEXTURE_COMPRESSION_BC_SLICED_3D,
733            self.core.texture_compression_bc != 0, // BC guarantees Sliced 3D
734        );
735        features.set(
736            F::PIPELINE_STATISTICS_QUERY,
737            self.core.pipeline_statistics_query != 0,
738        );
739        features.set(
740            F::VERTEX_WRITABLE_STORAGE,
741            self.core.vertex_pipeline_stores_and_atomics != 0,
742        );
743
744        features.set(F::SHADER_F64, self.core.shader_float64 != 0);
745        features.set(F::SHADER_INT64, self.core.shader_int64 != 0);
746        features.set(F::SHADER_I16, self.core.shader_int16 != 0);
747
748        features.set(F::PRIMITIVE_INDEX, self.core.geometry_shader != 0);
749
750        if let Some(ref shader_atomic_int64) = self.shader_atomic_int64 {
751            features.set(
752                F::SHADER_INT64_ATOMIC_ALL_OPS | F::SHADER_INT64_ATOMIC_MIN_MAX,
753                shader_atomic_int64.shader_buffer_int64_atomics != 0
754                    && shader_atomic_int64.shader_shared_int64_atomics != 0,
755            );
756        }
757
758        if let Some(ref shader_image_atomic_int64) = self.shader_image_atomic_int64 {
759            features.set(
760                F::TEXTURE_INT64_ATOMIC,
761                shader_image_atomic_int64
762                    .shader_image_int64_atomics(true)
763                    .shader_image_int64_atomics
764                    != 0,
765            );
766        }
767
768        if let Some(ref shader_atomic_float) = self.shader_atomic_float {
769            features.set(
770                F::SHADER_FLOAT32_ATOMIC,
771                shader_atomic_float.shader_buffer_float32_atomics != 0
772                    && shader_atomic_float.shader_buffer_float32_atomic_add != 0,
773            );
774        }
775
776        if let Some(ref shader_barycentrics) = self.shader_barycentrics {
777            features.set(
778                F::SHADER_BARYCENTRICS | F::SHADER_PER_VERTEX,
779                shader_barycentrics.fragment_shader_barycentric != 0,
780            );
781        }
782
783        //if caps.supports_extension(khr::sampler_mirror_clamp_to_edge::NAME) {
784        //if caps.supports_extension(ext::sampler_filter_minmax::NAME) {
785        features.set(
786            F::MULTI_DRAW_INDIRECT_COUNT,
787            caps.supports_extension(khr::draw_indirect_count::NAME),
788        );
789        features.set(
790            F::CONSERVATIVE_RASTERIZATION,
791            caps.supports_extension(ext::conservative_rasterization::NAME),
792        );
793        features.set(
794            F::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN,
795            caps.supports_extension(khr::ray_tracing_position_fetch::NAME),
796        );
797
798        if let Some(ref descriptor_indexing) = self.descriptor_indexing {
799            // We use update-after-bind descriptors for all bind groups containing binding arrays.
800            //
801            // In those bind groups, we allow all binding types except uniform buffers to be present.
802            //
803            // As we can only switch between update-after-bind and not on a per bind group basis,
804            // all supported binding types need to be able to be marked update after bind.
805            //
806            // As such, we enable all features as a whole, rather individually.
807            let supports_descriptor_indexing =
808                // Sampled Images
809                descriptor_indexing.shader_sampled_image_array_non_uniform_indexing != 0
810                    && descriptor_indexing.descriptor_binding_sampled_image_update_after_bind != 0
811                    // Storage Images
812                    && descriptor_indexing.shader_storage_image_array_non_uniform_indexing != 0
813                    && descriptor_indexing.descriptor_binding_storage_image_update_after_bind != 0
814                    // Storage Buffers
815                    && descriptor_indexing.shader_storage_buffer_array_non_uniform_indexing != 0
816                    && descriptor_indexing.descriptor_binding_storage_buffer_update_after_bind != 0;
817
818            let descriptor_indexing_features = F::BUFFER_BINDING_ARRAY
819                | F::TEXTURE_BINDING_ARRAY
820                | F::STORAGE_RESOURCE_BINDING_ARRAY
821                | F::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
822                | F::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING;
823
824            features.set(descriptor_indexing_features, supports_descriptor_indexing);
825
826            let supports_partially_bound =
827                descriptor_indexing.descriptor_binding_partially_bound != 0;
828
829            features.set(F::PARTIALLY_BOUND_BINDING_ARRAY, supports_partially_bound);
830        }
831
832        features.set(F::DEPTH_CLIP_CONTROL, self.core.depth_clamp != 0);
833        features.set(F::DUAL_SOURCE_BLENDING, self.core.dual_src_blend != 0);
834        features.set(F::CLIP_DISTANCES, self.core.shader_clip_distance != 0);
835
836        if let Some(ref multiview) = self.multiview {
837            features.set(F::MULTIVIEW, multiview.multiview != 0);
838            features.set(F::SELECTIVE_MULTIVIEW, multiview.multiview != 0);
839        }
840
841        features.set(
842            F::TEXTURE_FORMAT_16BIT_NORM,
843            is_format_16bit_norm_supported(instance, phd),
844        );
845
846        if let Some(ref astc_hdr) = self.astc_hdr {
847            features.set(
848                F::TEXTURE_COMPRESSION_ASTC_HDR,
849                astc_hdr.texture_compression_astc_hdr != 0,
850            );
851        }
852
853        if self.core.texture_compression_astc_ldr != 0 {
854            features.set(
855                F::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
856                supports_astc_3d(instance, phd),
857            );
858        }
859
860        if let (Some(ref f16_i8), Some(ref bit16)) = (self.shader_float16_int8, self._16bit_storage)
861        {
862            // Note `storage_input_output16` is not required, we polyfill `f16` I/O using `f32`
863            // types when this capability is not available
864            features.set(
865                F::SHADER_F16,
866                f16_i8.shader_float16 != 0
867                    && bit16.storage_buffer16_bit_access != 0
868                    && bit16.uniform_and_storage_buffer16_bit_access != 0,
869            );
870        }
871
872        if let Some(ref subgroup) = caps.subgroup {
873            if (caps.device_api_version >= vk::API_VERSION_1_3
874                || caps.supports_extension(ext::subgroup_size_control::NAME))
875                && subgroup.supported_operations.contains(
876                    vk::SubgroupFeatureFlags::BASIC
877                        | vk::SubgroupFeatureFlags::VOTE
878                        | vk::SubgroupFeatureFlags::ARITHMETIC
879                        | vk::SubgroupFeatureFlags::BALLOT
880                        | vk::SubgroupFeatureFlags::SHUFFLE
881                        | vk::SubgroupFeatureFlags::SHUFFLE_RELATIVE
882                        | vk::SubgroupFeatureFlags::QUAD,
883                )
884            {
885                features.set(
886                    F::SUBGROUP,
887                    subgroup
888                        .supported_stages
889                        .contains(vk::ShaderStageFlags::COMPUTE | vk::ShaderStageFlags::FRAGMENT),
890                );
891                features.set(
892                    F::SUBGROUP_VERTEX,
893                    subgroup
894                        .supported_stages
895                        .contains(vk::ShaderStageFlags::VERTEX),
896                );
897                features.insert(F::SUBGROUP_BARRIER);
898            }
899        }
900
901        let supports_depth_format = |format| {
902            supports_format(
903                instance,
904                phd,
905                format,
906                vk::ImageTiling::OPTIMAL,
907                depth_stencil_required_flags(),
908            )
909        };
910
911        let texture_s8 = supports_depth_format(vk::Format::S8_UINT);
912        let texture_d32 = supports_depth_format(vk::Format::D32_SFLOAT);
913        let texture_d24_s8 = supports_depth_format(vk::Format::D24_UNORM_S8_UINT);
914        let texture_d32_s8 = supports_depth_format(vk::Format::D32_SFLOAT_S8_UINT);
915
916        let stencil8 = texture_s8 || texture_d24_s8;
917        let depth24_plus_stencil8 = texture_d24_s8 || texture_d32_s8;
918
919        dl_flags.set(
920            Df::WEBGPU_TEXTURE_FORMAT_SUPPORT,
921            stencil8 && depth24_plus_stencil8 && texture_d32,
922        );
923
924        features.set(F::DEPTH32FLOAT_STENCIL8, texture_d32_s8);
925
926        let supports_acceleration_structures = caps
927            .supports_extension(khr::deferred_host_operations::NAME)
928            && caps.supports_extension(khr::acceleration_structure::NAME)
929            && caps.supports_extension(khr::buffer_device_address::NAME);
930
931        let supports_ray_query =
932            supports_acceleration_structures && caps.supports_extension(khr::ray_query::NAME);
933        let supports_acceleration_structure_binding_array = supports_ray_query
934            && self
935                .acceleration_structure
936                .as_ref()
937                .is_some_and(|features| {
938                    features.descriptor_binding_acceleration_structure_update_after_bind != 0
939                });
940
941        features.set(
942            F::EXPERIMENTAL_RAY_QUERY
943            // Although this doesn't really require ray queries, it does not make sense to be enabled if acceleration structures
944            // aren't enabled.
945                | F::EXTENDED_ACCELERATION_STRUCTURE_VERTEX_FORMATS,
946            supports_ray_query,
947        );
948
949        // Binding arrays of TLAS are supported on Vulkan when ray queries are supported.
950        //
951        // Note: this flag is used for shader-side `binding_array<acceleration_structure>` as well as
952        // allowing `BindGroupLayoutEntry::count = Some(...)` for `BindingType::AccelerationStructure`.
953        features.set(
954            F::ACCELERATION_STRUCTURE_BINDING_ARRAY,
955            supports_acceleration_structure_binding_array,
956        );
957
958        let rg11b10ufloat_renderable = supports_format(
959            instance,
960            phd,
961            vk::Format::B10G11R11_UFLOAT_PACK32,
962            vk::ImageTiling::OPTIMAL,
963            vk::FormatFeatureFlags::COLOR_ATTACHMENT
964                | vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND,
965        );
966        features.set(F::RG11B10UFLOAT_RENDERABLE, rg11b10ufloat_renderable);
967
968        features.set(
969            F::BGRA8UNORM_STORAGE,
970            supports_bgra8unorm_storage(instance, phd, caps.device_api_version),
971        );
972
973        features.set(
974            F::FLOAT32_FILTERABLE,
975            is_float32_filterable_supported(instance, phd),
976        );
977
978        features.set(
979            F::FLOAT32_BLENDABLE,
980            is_float32_blendable_supported(instance, phd),
981        );
982
983        if let Some(ref _sampler_ycbcr_conversion) = self.sampler_ycbcr_conversion {
984            features.set(
985                F::TEXTURE_FORMAT_NV12,
986                supports_format(
987                    instance,
988                    phd,
989                    vk::Format::G8_B8R8_2PLANE_420_UNORM,
990                    vk::ImageTiling::OPTIMAL,
991                    vk::FormatFeatureFlags::SAMPLED_IMAGE
992                        | vk::FormatFeatureFlags::TRANSFER_SRC
993                        | vk::FormatFeatureFlags::TRANSFER_DST,
994                ) && !caps
995                    .driver
996                    .map(|driver| driver.driver_id == vk::DriverId::MOLTENVK)
997                    .unwrap_or_default(),
998            );
999        }
1000
1001        if let Some(ref _sampler_ycbcr_conversion) = self.sampler_ycbcr_conversion {
1002            features.set(
1003                F::TEXTURE_FORMAT_P010,
1004                supports_format(
1005                    instance,
1006                    phd,
1007                    vk::Format::G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
1008                    vk::ImageTiling::OPTIMAL,
1009                    vk::FormatFeatureFlags::SAMPLED_IMAGE
1010                        | vk::FormatFeatureFlags::TRANSFER_SRC
1011                        | vk::FormatFeatureFlags::TRANSFER_DST,
1012                ) && !caps
1013                    .driver
1014                    .map(|driver| driver.driver_id == vk::DriverId::MOLTENVK)
1015                    .unwrap_or_default(),
1016            );
1017        }
1018
1019        features.set(
1020            F::VULKAN_GOOGLE_DISPLAY_TIMING,
1021            caps.supports_extension(google::display_timing::NAME),
1022        );
1023
1024        features.set(
1025            F::VULKAN_EXTERNAL_MEMORY_WIN32,
1026            caps.supports_extension(khr::external_memory_win32::NAME),
1027        );
1028        features.set(
1029            F::EXPERIMENTAL_MESH_SHADER,
1030            caps.supports_extension(ext::mesh_shader::NAME),
1031        );
1032        features.set(
1033            F::EXPERIMENTAL_MESH_SHADER_POINTS,
1034            caps.supports_extension(ext::mesh_shader::NAME),
1035        );
1036        if let Some(ref mesh_shader) = self.mesh_shader {
1037            features.set(
1038                F::EXPERIMENTAL_MESH_SHADER_MULTIVIEW,
1039                mesh_shader.multiview_mesh_shader != 0,
1040            );
1041        }
1042
1043        // Not supported by default by `VK_KHR_portability_subset`, which we use on apple platforms.
1044        features.set(
1045            F::MULTISAMPLE_ARRAY,
1046            self.portability_subset
1047                .map(|p| p.multisample_array_image == vk::TRUE)
1048                .unwrap_or(true),
1049        );
1050        // Enable cooperative matrix if any configuration is supported. The SPIR-V
1051        // we emit for it uses `Device`-scope atomics under the Vulkan memory model,
1052        // so the device must also support `vulkanMemoryModelDeviceScope`, otherwise
1053        // those shaders trip `VUID-RuntimeSpirv-vulkanMemoryModel-06265`.
1054        features.set(
1055            F::EXPERIMENTAL_COOPERATIVE_MATRIX,
1056            !caps.cooperative_matrix_properties.is_empty()
1057                && self.vulkan_memory_model.is_some_and(|m| {
1058                    m.vulkan_memory_model == vk::TRUE
1059                        && m.vulkan_memory_model_device_scope == vk::TRUE
1060                }),
1061        );
1062
1063        features.set(
1064            F::SHADER_DRAW_INDEX,
1065            self.shader_draw_parameters
1066                .is_some_and(|a| a.shader_draw_parameters != 0)
1067                || caps.supports_extension(c"VK_KHR_shader_draw_parameters"),
1068        );
1069
1070        (features, dl_flags)
1071    }
1072}
1073
1074/// Vulkan "properties" structures gathered about a physical device.
1075///
1076/// This structure holds the properties of a [`vk::PhysicalDevice`]:
1077/// - the standard Vulkan device properties
1078/// - the `VkExtensionProperties` structs for all available extensions, and
1079/// - the per-extension properties structures for the available extensions that
1080///   `wgpu` cares about.
1081///
1082/// Generally, if you get it from any of these functions, it's stored
1083/// here:
1084/// - `vkEnumerateDeviceExtensionProperties`
1085/// - `vkGetPhysicalDeviceProperties`
1086/// - `vkGetPhysicalDeviceProperties2`
1087///
1088/// This also includes a copy of the device API version, since we can
1089/// use that as a shortcut for searching for an extension, if the
1090/// extension has been promoted to core in the current version.
1091///
1092/// This does not include device features; for those, see
1093/// [`PhysicalDeviceFeatures`].
1094#[derive(Default, Debug)]
1095pub struct PhysicalDeviceProperties {
1096    /// Extensions supported by the `vk::PhysicalDevice`,
1097    /// as returned by `vkEnumerateDeviceExtensionProperties`.
1098    supported_extensions: Vec<vk::ExtensionProperties>,
1099
1100    /// Properties of the `vk::PhysicalDevice`, as returned by
1101    /// `vkGetPhysicalDeviceProperties`.
1102    properties: vk::PhysicalDeviceProperties,
1103
1104    /// Additional `vk::PhysicalDevice` properties from the
1105    /// `VK_KHR_maintenance3` extension, promoted to Vulkan 1.1.
1106    maintenance_3: Option<vk::PhysicalDeviceMaintenance3Properties<'static>>,
1107
1108    /// Additional `vk::PhysicalDevice` properties from the
1109    /// `VK_KHR_maintenance4` extension, promoted to Vulkan 1.3.
1110    maintenance_4: Option<vk::PhysicalDeviceMaintenance4Properties<'static>>,
1111
1112    /// Additional `vk::PhysicalDevice` properties from the
1113    /// `VK_EXT_descriptor_indexing` extension, promoted to Vulkan 1.2.
1114    descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingPropertiesEXT<'static>>,
1115
1116    /// Additional `vk::PhysicalDevice` properties from the
1117    /// `VK_KHR_acceleration_structure` extension.
1118    acceleration_structure: Option<vk::PhysicalDeviceAccelerationStructurePropertiesKHR<'static>>,
1119
1120    /// Additional `vk::PhysicalDevice` properties from the
1121    /// `VK_KHR_driver_properties` extension, promoted to Vulkan 1.2.
1122    driver: Option<vk::PhysicalDeviceDriverPropertiesKHR<'static>>,
1123
1124    /// Additional `vk::PhysicalDevice` properties from Vulkan 1.1.
1125    subgroup: Option<vk::PhysicalDeviceSubgroupProperties<'static>>,
1126
1127    /// Additional `vk::PhysicalDevice` properties from the
1128    /// `VK_EXT_subgroup_size_control` extension, promoted to Vulkan 1.3.
1129    subgroup_size_control: Option<vk::PhysicalDeviceSubgroupSizeControlProperties<'static>>,
1130
1131    /// Additional `vk::PhysicalDevice` properties from the
1132    /// `VK_EXT_robustness2` extension.
1133    robustness2: Option<vk::PhysicalDeviceRobustness2PropertiesEXT<'static>>,
1134
1135    /// Additional `vk::PhysicalDevice` properties from the
1136    /// `VK_EXT_mesh_shader` extension.
1137    mesh_shader: Option<vk::PhysicalDeviceMeshShaderPropertiesEXT<'static>>,
1138
1139    /// Additional `vk::PhysicalDevice` properties from the
1140    /// `VK_KHR_multiview` extension.
1141    multiview: Option<vk::PhysicalDeviceMultiviewPropertiesKHR<'static>>,
1142
1143    /// `VK_EXT_pci_bus_info` extension.
1144    pci_bus_info: Option<vk::PhysicalDevicePCIBusInfoPropertiesEXT<'static>>,
1145
1146    /// The device API version.
1147    ///
1148    /// Which is the version of Vulkan supported for device-level functionality.
1149    ///
1150    /// It is associated with a `VkPhysicalDevice` and its children.
1151    device_api_version: u32,
1152
1153    /// Supported cooperative matrix configurations.
1154    ///
1155    /// This is determined by querying `vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR`.
1156    cooperative_matrix_properties: Vec<wgt::CooperativeMatrixProperties>,
1157}
1158
1159impl PhysicalDeviceProperties {
1160    pub fn properties(&self) -> vk::PhysicalDeviceProperties {
1161        self.properties
1162    }
1163
1164    pub fn supports_extension(&self, extension: &CStr) -> bool {
1165        self.supported_extensions
1166            .iter()
1167            .any(|ep| ep.extension_name_as_c_str() == Ok(extension))
1168    }
1169
1170    /// Map `requested_features` to the list of Vulkan extension strings required to create the logical device.
1171    fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> {
1172        let mut extensions = Vec::new();
1173
1174        // Note that quite a few extensions depend on the `VK_KHR_get_physical_device_properties2` instance extension.
1175        // We enable `VK_KHR_get_physical_device_properties2` unconditionally (if available).
1176
1177        // Require `VK_KHR_swapchain`
1178        extensions.push(khr::swapchain::NAME);
1179
1180        if self.device_api_version < vk::API_VERSION_1_1 {
1181            // Require `VK_KHR_maintenance1`
1182            extensions.push(khr::maintenance1::NAME);
1183
1184            // Optional `VK_KHR_maintenance2`
1185            if self.supports_extension(khr::maintenance2::NAME) {
1186                extensions.push(khr::maintenance2::NAME);
1187            }
1188
1189            // Optional `VK_KHR_maintenance3`
1190            if self.supports_extension(khr::maintenance3::NAME) {
1191                extensions.push(khr::maintenance3::NAME);
1192            }
1193
1194            // Require `VK_KHR_storage_buffer_storage_class`
1195            extensions.push(khr::storage_buffer_storage_class::NAME);
1196
1197            // Require `VK_KHR_multiview` if the associated feature was requested
1198            if requested_features.contains(wgt::Features::MULTIVIEW) {
1199                extensions.push(khr::multiview::NAME);
1200            }
1201
1202            // Require `VK_KHR_sampler_ycbcr_conversion` if the associated feature was requested
1203            if requested_features.contains(wgt::Features::TEXTURE_FORMAT_NV12) {
1204                extensions.push(khr::sampler_ycbcr_conversion::NAME);
1205            }
1206
1207            // Require `VK_KHR_16bit_storage` if the feature `SHADER_F16` was requested
1208            if requested_features.contains(wgt::Features::SHADER_F16) {
1209                // - Feature `SHADER_F16` also requires `VK_KHR_shader_float16_int8`, but we always
1210                //   require that anyway (if it is available) below.
1211                // - `VK_KHR_16bit_storage` requires `VK_KHR_storage_buffer_storage_class`, however
1212                //   we require that one already.
1213                extensions.push(khr::_16bit_storage::NAME);
1214            }
1215
1216            if requested_features.contains(wgt::Features::SHADER_DRAW_INDEX) {
1217                extensions.push(khr::shader_draw_parameters::NAME);
1218            }
1219        }
1220
1221        if self.device_api_version < vk::API_VERSION_1_2 {
1222            // Optional `VK_KHR_image_format_list`
1223            if self.supports_extension(khr::image_format_list::NAME) {
1224                extensions.push(khr::image_format_list::NAME);
1225            }
1226
1227            // Optional `VK_KHR_driver_properties`
1228            if self.supports_extension(khr::driver_properties::NAME) {
1229                extensions.push(khr::driver_properties::NAME);
1230            }
1231
1232            // Optional `VK_KHR_timeline_semaphore`
1233            if self.supports_extension(khr::timeline_semaphore::NAME) {
1234                extensions.push(khr::timeline_semaphore::NAME);
1235            }
1236
1237            // Require `VK_EXT_descriptor_indexing` if one of the associated features was requested
1238            if requested_features.intersects(INDEXING_FEATURES) {
1239                extensions.push(ext::descriptor_indexing::NAME);
1240            }
1241
1242            // Always require `VK_KHR_shader_float16_int8` if available as it enables
1243            // Int8 optimizations. Also require it even if it's not available but
1244            // requested so that we get a corresponding error message.
1245            if requested_features.contains(wgt::Features::SHADER_F16)
1246                || self.supports_extension(khr::shader_float16_int8::NAME)
1247            {
1248                extensions.push(khr::shader_float16_int8::NAME);
1249            }
1250
1251            if requested_features.intersects(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
1252                extensions.push(khr::spirv_1_4::NAME);
1253            }
1254
1255            //extensions.push(khr::sampler_mirror_clamp_to_edge::NAME);
1256            //extensions.push(ext::sampler_filter_minmax::NAME);
1257        }
1258
1259        if self.device_api_version < vk::API_VERSION_1_3 {
1260            // Optional `VK_KHR_maintenance4`
1261            if self.supports_extension(khr::maintenance4::NAME) {
1262                extensions.push(khr::maintenance4::NAME);
1263            }
1264
1265            // Optional `VK_EXT_image_robustness`
1266            if self.supports_extension(ext::image_robustness::NAME) {
1267                extensions.push(ext::image_robustness::NAME);
1268            }
1269
1270            // Require `VK_EXT_subgroup_size_control` if the associated feature was requested
1271            if requested_features.contains(wgt::Features::SUBGROUP) {
1272                extensions.push(ext::subgroup_size_control::NAME);
1273            }
1274
1275            // Optional `VK_KHR_shader_integer_dot_product`
1276            if self.supports_extension(khr::shader_integer_dot_product::NAME) {
1277                extensions.push(khr::shader_integer_dot_product::NAME);
1278            }
1279        }
1280
1281        // Optional `VK_KHR_swapchain_mutable_format`
1282        if self.supports_extension(khr::swapchain_mutable_format::NAME) {
1283            extensions.push(khr::swapchain_mutable_format::NAME);
1284        }
1285
1286        // Optional `VK_EXT_robustness2`
1287        if self.supports_extension(ext::robustness2::NAME) {
1288            extensions.push(ext::robustness2::NAME);
1289        }
1290
1291        // Optional `VK_KHR_external_memory_win32`
1292        if self.supports_extension(khr::external_memory_win32::NAME) {
1293            extensions.push(khr::external_memory_win32::NAME);
1294        }
1295
1296        // Optional `VK_KHR_external_memory_fd`
1297        if self.supports_extension(khr::external_memory_fd::NAME) {
1298            extensions.push(khr::external_memory_fd::NAME);
1299        }
1300
1301        // Optional `VK_EXT_external_memory_dma`
1302        if self.supports_extension(ext::external_memory_dma_buf::NAME) {
1303            extensions.push(ext::external_memory_dma_buf::NAME);
1304        }
1305
1306        // Optional `VK_EXT_memory_budget`
1307        if self.supports_extension(ext::memory_budget::NAME) {
1308            extensions.push(ext::memory_budget::NAME);
1309        } else {
1310            log::debug!("VK_EXT_memory_budget is not available.")
1311        }
1312
1313        // Require `VK_KHR_draw_indirect_count` if the associated feature was requested
1314        // Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid
1315        // large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features.
1316        if requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT) {
1317            extensions.push(khr::draw_indirect_count::NAME);
1318        }
1319
1320        // Require `VK_KHR_deferred_host_operations`, `VK_KHR_acceleration_structure` `VK_KHR_buffer_device_address` (for acceleration structures) and`VK_KHR_ray_query` if `EXPERIMENTAL_RAY_QUERY` was requested
1321        if requested_features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
1322            extensions.push(khr::deferred_host_operations::NAME);
1323            extensions.push(khr::acceleration_structure::NAME);
1324            extensions.push(khr::buffer_device_address::NAME);
1325            extensions.push(khr::ray_query::NAME);
1326        }
1327
1328        if requested_features.contains(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN) {
1329            extensions.push(khr::ray_tracing_position_fetch::NAME)
1330        }
1331
1332        // Require `VK_EXT_conservative_rasterization` if the associated feature was requested
1333        if requested_features.contains(wgt::Features::CONSERVATIVE_RASTERIZATION) {
1334            extensions.push(ext::conservative_rasterization::NAME);
1335        }
1336
1337        // Require `VK_KHR_portability_subset` on macOS/iOS
1338        #[cfg(target_vendor = "apple")]
1339        extensions.push(khr::portability_subset::NAME);
1340
1341        // Require `VK_EXT_texture_compression_astc_hdr` if the associated feature was requested
1342        if requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
1343            extensions.push(ext::texture_compression_astc_hdr::NAME);
1344        }
1345
1346        // Require `VK_KHR_shader_atomic_int64` if the associated feature was requested
1347        if requested_features.intersects(
1348            wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
1349        ) {
1350            extensions.push(khr::shader_atomic_int64::NAME);
1351        }
1352
1353        // Require `VK_EXT_shader_image_atomic_int64` if the associated feature was requested
1354        if requested_features.intersects(wgt::Features::TEXTURE_INT64_ATOMIC) {
1355            extensions.push(ext::shader_image_atomic_int64::NAME);
1356        }
1357
1358        // Require `VK_EXT_shader_atomic_float` if the associated feature was requested
1359        if requested_features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC) {
1360            extensions.push(ext::shader_atomic_float::NAME);
1361        }
1362
1363        // Require VK_GOOGLE_display_timing if the associated feature was requested
1364        if requested_features.contains(wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING) {
1365            extensions.push(google::display_timing::NAME);
1366        }
1367
1368        if requested_features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
1369            extensions.push(ext::mesh_shader::NAME);
1370        }
1371
1372        // Require `VK_KHR_fragment_shader_barycentric` if an associated feature was requested
1373        // Vulkan bundles both barycentrics and per-vertex attributes under the same feature.
1374        if requested_features
1375            .intersects(wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX)
1376        {
1377            extensions.push(khr::fragment_shader_barycentric::NAME);
1378        }
1379
1380        // Require `VK_KHR_cooperative_matrix` if the associated feature was requested
1381        if requested_features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
1382            extensions.push(khr::cooperative_matrix::NAME);
1383        }
1384
1385        extensions
1386    }
1387
1388    fn to_wgpu_limits(&self) -> wgt::Limits {
1389        let limits = &self.properties.limits;
1390
1391        let (
1392            mut max_task_mesh_workgroup_total_count,
1393            mut max_task_mesh_workgroups_per_dimension,
1394            mut max_task_invocations_per_workgroup,
1395            mut max_task_invocations_per_dimension,
1396            mut max_mesh_invocations_per_workgroup,
1397            mut max_mesh_invocations_per_dimension,
1398            mut max_task_payload_size,
1399            mut max_mesh_output_vertices,
1400            mut max_mesh_output_primitives,
1401            mut max_mesh_output_layers,
1402            mut max_mesh_multiview_view_count,
1403        ) = Default::default();
1404        if let Some(m) = self.mesh_shader {
1405            max_task_mesh_workgroup_total_count = m
1406                .max_task_work_group_total_count
1407                .min(m.max_mesh_work_group_total_count);
1408            max_task_mesh_workgroups_per_dimension = m
1409                .max_task_work_group_count
1410                .into_iter()
1411                .chain(m.max_mesh_work_group_count)
1412                .min()
1413                .unwrap();
1414            max_task_invocations_per_workgroup = m.max_task_work_group_invocations;
1415            max_task_invocations_per_dimension =
1416                m.max_task_work_group_size.into_iter().min().unwrap();
1417            max_mesh_invocations_per_workgroup = m.max_mesh_work_group_invocations;
1418            max_mesh_invocations_per_dimension =
1419                m.max_mesh_work_group_size.into_iter().min().unwrap();
1420            max_task_payload_size = m.max_task_payload_size;
1421            max_mesh_output_vertices = m.max_mesh_output_vertices;
1422            max_mesh_output_primitives = m.max_mesh_output_primitives;
1423            max_mesh_output_layers = m.max_mesh_output_layers;
1424            max_mesh_multiview_view_count = m.max_mesh_multiview_view_count;
1425        }
1426
1427        let max_memory_allocation_size = self
1428            .maintenance_3
1429            .map(|maintenance_3| maintenance_3.max_memory_allocation_size)
1430            .unwrap_or(u64::MAX);
1431        let max_buffer_size = self
1432            .maintenance_4
1433            .map(|maintenance_4| maintenance_4.max_buffer_size)
1434            .unwrap_or(u64::MAX);
1435        let max_buffer_size = max_buffer_size.min(max_memory_allocation_size);
1436
1437        // Prevent very large buffers on mesa and most android devices, and in all cases
1438        // don't risk confusing JS by exceeding the range of a double.
1439        let is_nvidia = self.properties.vendor_id == crate::auxil::db::nvidia::VENDOR;
1440        let max_buffer_size_cap =
1441            if (cfg!(target_os = "linux") || cfg!(target_os = "android")) && !is_nvidia {
1442                i32::MAX as u64
1443            } else {
1444                1u64 << 52
1445            };
1446
1447        let max_buffer_size = max_buffer_size.min(max_buffer_size_cap);
1448
1449        let mut max_binding_array_elements = 0;
1450        let mut max_sampler_binding_array_elements = 0;
1451        if let Some(ref descriptor_indexing) = self.descriptor_indexing {
1452            max_binding_array_elements = descriptor_indexing
1453                .max_descriptor_set_update_after_bind_sampled_images
1454                .min(descriptor_indexing.max_descriptor_set_update_after_bind_storage_images)
1455                .min(descriptor_indexing.max_descriptor_set_update_after_bind_storage_buffers)
1456                .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_sampled_images)
1457                .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_storage_images)
1458                .min(
1459                    descriptor_indexing.max_per_stage_descriptor_update_after_bind_storage_buffers,
1460                );
1461
1462            max_sampler_binding_array_elements = descriptor_indexing
1463                .max_descriptor_set_update_after_bind_samplers
1464                .min(descriptor_indexing.max_per_stage_descriptor_update_after_bind_samplers);
1465        }
1466
1467        const MAX_SHADER_STAGES_PER_PIPELINE: u32 = 2;
1468
1469        // When summed, the 3 limits below must be under Vulkan's maxFragmentCombinedOutputResources.
1470        // https://gpuweb.github.io/gpuweb/correspondence/#vulkan-maxFragmentCombinedOutputResources
1471        //
1472        // - maxStorageTexturesPerShaderStage, WebGPU default: 4
1473        // - maxStorageBuffersPerShaderStage, WebGPU default: 8
1474        // - maxColorAttachments, WebGPU default: 8
1475        //
1476        // However, maxFragmentCombinedOutputResources should be ignored on
1477        // intel/nvidia/amd/imgtec since it's not reported correctly.
1478        //
1479        // https://github.com/gpuweb/gpuweb/issues/3631#issuecomment-1498747606
1480        // https://github.com/gpuweb/gpuweb/issues/4018
1481        let mut max_storage_textures_per_shader_stage = limits
1482            .max_per_stage_descriptor_storage_images
1483            .min(limits.max_descriptor_set_storage_images / MAX_SHADER_STAGES_PER_PIPELINE);
1484        let mut max_storage_buffers_per_shader_stage = limits
1485            .max_per_stage_descriptor_storage_buffers
1486            .min(limits.max_descriptor_set_storage_buffers / MAX_SHADER_STAGES_PER_PIPELINE);
1487        let mut max_color_attachments = limits
1488            .max_color_attachments
1489            .min(limits.max_fragment_output_attachments);
1490
1491        let ignore_max_fragment_combined_output_resources = [
1492            crate::auxil::db::intel::VENDOR,
1493            crate::auxil::db::nvidia::VENDOR,
1494            crate::auxil::db::amd::VENDOR,
1495            crate::auxil::db::imgtec::VENDOR,
1496        ]
1497        .contains(&self.properties.vendor_id);
1498
1499        if !ignore_max_fragment_combined_output_resources {
1500            crate::auxil::cap_limits_to_be_under_the_sum_limit(
1501                [
1502                    &mut max_storage_textures_per_shader_stage,
1503                    &mut max_storage_buffers_per_shader_stage,
1504                    &mut max_color_attachments,
1505                ],
1506                limits.max_fragment_combined_output_resources,
1507            );
1508        }
1509
1510        // When summed, the 5 limits below must be under Vulkan's maxPerStageResources.
1511        //
1512        // - maxUniformBuffersPerShaderStage, WebGPU default: 12
1513        // - maxSampledTexturesPerShaderStage, WebGPU default: 16
1514        // - maxStorageTexturesPerShaderStage, WebGPU default: 4
1515        // - maxStorageBuffersPerShaderStage, WebGPU default: 8
1516        // - maxColorAttachments, WebGPU default: 8
1517        //
1518        // Note: Vulkan's texel buffers and input attachments also count towards
1519        // maxPerStageResources but we don't make use of them.
1520        let mut max_sampled_textures_per_shader_stage = limits
1521            .max_per_stage_descriptor_sampled_images
1522            .min(limits.max_descriptor_set_sampled_images / MAX_SHADER_STAGES_PER_PIPELINE);
1523        let mut max_uniform_buffers_per_shader_stage = limits
1524            .max_per_stage_descriptor_uniform_buffers
1525            .min(limits.max_descriptor_set_uniform_buffers / MAX_SHADER_STAGES_PER_PIPELINE);
1526
1527        crate::auxil::cap_limits_to_be_under_the_sum_limit(
1528            [
1529                &mut max_sampled_textures_per_shader_stage,
1530                &mut max_uniform_buffers_per_shader_stage,
1531                &mut max_storage_textures_per_shader_stage,
1532                &mut max_storage_buffers_per_shader_stage,
1533                &mut max_color_attachments,
1534            ],
1535            limits.max_per_stage_resources,
1536        );
1537
1538        // Acceleration structure limits
1539        let mut max_blas_geometry_count = 0;
1540        let mut max_blas_primitive_count = 0;
1541        let mut max_tlas_instance_count = 0;
1542        let mut max_acceleration_structures_per_shader_stage = 0;
1543        if let Some(properties) = self.acceleration_structure {
1544            max_blas_geometry_count = properties.max_geometry_count as u32;
1545            max_blas_primitive_count = properties.max_primitive_count as u32;
1546            max_tlas_instance_count = properties.max_instance_count as u32;
1547            max_acceleration_structures_per_shader_stage = properties
1548                .max_per_stage_descriptor_acceleration_structures
1549                .min(
1550                    properties.max_descriptor_set_acceleration_structures
1551                        / MAX_SHADER_STAGES_PER_PIPELINE,
1552                );
1553        }
1554
1555        // When summed, the 6 limits below must be under Vulkan's
1556        // maxPerSetDescriptors / MAX_SHADER_STAGES_PER_PIPELINE.
1557        //
1558        // - maxUniformBuffersPerShaderStage, WebGPU default: 12
1559        // - maxSampledTexturesPerShaderStage, WebGPU default: 16
1560        // - maxStorageTexturesPerShaderStage, WebGPU default: 4
1561        // - maxStorageBuffersPerShaderStage, WebGPU default: 8
1562        // - maxSamplersPerShaderStage, WebGPU default: 16
1563        // - maxAccelerationStructuresPerShaderStage, Native only
1564        //
1565        // Note: All Vulkan's descriptor types count towards maxPerSetDescriptors but
1566        // we don't use all of them.
1567        // See https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#interfaces-resources-limits
1568        let max_per_set_descriptors = self
1569            .maintenance_3
1570            .map(|maintenance_3| maintenance_3.max_per_set_descriptors)
1571            // The lowest value seen in reports is 312, use 256 as a safe default.
1572            // https://vulkan.gpuinfo.org/displayextensionproperty.php?extensionname=VK_KHR_maintenance3&extensionproperty=maxPerSetDescriptors&platform=all
1573            // https://vulkan.gpuinfo.org/displaycoreproperty.php?core=1.1&name=maxPerSetDescriptors&platform=all
1574            .unwrap_or(256);
1575
1576        let mut max_samplers_per_shader_stage = limits
1577            .max_per_stage_descriptor_samplers
1578            .min(limits.max_descriptor_set_samplers / MAX_SHADER_STAGES_PER_PIPELINE);
1579
1580        crate::auxil::cap_limits_to_be_under_the_sum_limit(
1581            [
1582                &mut max_sampled_textures_per_shader_stage,
1583                &mut max_uniform_buffers_per_shader_stage,
1584                &mut max_storage_textures_per_shader_stage,
1585                &mut max_storage_buffers_per_shader_stage,
1586                &mut max_samplers_per_shader_stage,
1587                &mut max_acceleration_structures_per_shader_stage,
1588            ],
1589            max_per_set_descriptors / MAX_SHADER_STAGES_PER_PIPELINE,
1590        );
1591
1592        // Use max(default, maxPerSetDescriptors) since the spec requires this
1593        // limit to be at least 1000. This is ok because we already lowered
1594        // all the other relevant per stage limits so their sum is lower
1595        // than maxPerSetDescriptors.
1596        let max_bindings_per_bind_group = 1000.max(max_per_set_descriptors);
1597
1598        // TODO: programmatically determine this, if possible. It's unclear whether we can
1599        // as of https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447.
1600        //
1601        // In theory some tilers may not support this much. We can't tell however, and
1602        // the driver will throw a DEVICE_REMOVED if it goes too high in usage. This is fine.
1603        let max_color_attachment_bytes_per_sample =
1604            max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
1605
1606        let max_multiview_view_count = self
1607            .multiview
1608            .map(|a| a.max_multiview_view_count.min(32))
1609            .unwrap_or(0);
1610
1611        crate::auxil::adjust_raw_limits(wgt::Limits {
1612            //
1613            // WebGPU LIMITS:
1614            // Based on https://gpuweb.github.io/gpuweb/correspondence/#limits
1615            //
1616            max_texture_dimension_1d: limits.max_image_dimension1_d,
1617            max_texture_dimension_2d: limits
1618                .max_image_dimension2_d
1619                .min(limits.max_image_dimension_cube)
1620                .min(limits.max_framebuffer_width)
1621                .min(limits.max_framebuffer_height),
1622            max_texture_dimension_3d: limits.max_image_dimension3_d,
1623            max_texture_array_layers: limits.max_image_array_layers,
1624            max_bind_groups: limits.max_bound_descriptor_sets,
1625            max_bindings_per_bind_group,
1626            max_dynamic_uniform_buffers_per_pipeline_layout: limits
1627                .max_descriptor_set_uniform_buffers_dynamic,
1628            max_dynamic_storage_buffers_per_pipeline_layout: limits
1629                .max_descriptor_set_storage_buffers_dynamic,
1630            max_samplers_per_shader_stage,
1631            max_sampled_textures_per_shader_stage,
1632            max_storage_textures_per_shader_stage,
1633            max_storage_buffers_per_shader_stage,
1634            max_uniform_buffers_per_shader_stage,
1635            max_vertex_buffers: limits.max_vertex_input_bindings,
1636            max_buffer_size,
1637            max_uniform_buffer_binding_size: limits
1638                .max_uniform_buffer_range
1639                .min(crate::auxil::MAX_I32_BINDING_SIZE)
1640                .into(),
1641            max_storage_buffer_binding_size: limits
1642                .max_storage_buffer_range
1643                .min(crate::auxil::MAX_I32_BINDING_SIZE)
1644                .into(),
1645            min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment as u32,
1646            min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment as u32,
1647            max_vertex_attributes: limits.max_vertex_input_attributes,
1648            max_vertex_buffer_array_stride: limits.max_vertex_input_binding_stride,
1649            max_inter_stage_shader_variables: limits
1650                .max_vertex_output_components
1651                .min(limits.max_fragment_input_components)
1652                / 4
1653                - 1, // -1 for position
1654            max_color_attachments,
1655            max_color_attachment_bytes_per_sample,
1656            max_compute_workgroup_storage_size: limits.max_compute_shared_memory_size,
1657            max_compute_invocations_per_workgroup: limits.max_compute_work_group_invocations,
1658            max_compute_workgroup_size_x: limits.max_compute_work_group_size[0],
1659            max_compute_workgroup_size_y: limits.max_compute_work_group_size[1],
1660            max_compute_workgroup_size_z: limits.max_compute_work_group_size[2],
1661            max_compute_workgroups_per_dimension: limits.max_compute_work_group_count[0]
1662                .min(limits.max_compute_work_group_count[1])
1663                .min(limits.max_compute_work_group_count[2]),
1664            max_immediate_size: limits.max_push_constants_size,
1665            //
1666            // NATIVE (Non-WebGPU) LIMITS:
1667            //
1668            max_non_sampler_bindings: u32::MAX,
1669
1670            max_binding_array_elements_per_shader_stage: max_binding_array_elements,
1671            max_binding_array_sampler_elements_per_shader_stage: max_sampler_binding_array_elements,
1672            max_binding_array_acceleration_structure_elements_per_shader_stage: if self
1673                .descriptor_indexing
1674                .is_some()
1675            {
1676                max_acceleration_structures_per_shader_stage
1677            } else {
1678                0
1679            },
1680
1681            max_task_mesh_workgroup_total_count,
1682            max_task_mesh_workgroups_per_dimension,
1683            max_task_invocations_per_workgroup,
1684            max_task_invocations_per_dimension,
1685
1686            max_mesh_invocations_per_workgroup,
1687            max_mesh_invocations_per_dimension,
1688
1689            max_task_payload_size,
1690            max_mesh_output_vertices,
1691            max_mesh_output_primitives,
1692            max_mesh_output_layers,
1693            max_mesh_multiview_view_count,
1694
1695            max_blas_primitive_count,
1696            max_blas_geometry_count,
1697            max_tlas_instance_count,
1698            max_acceleration_structures_per_shader_stage,
1699
1700            max_multiview_view_count,
1701        })
1702    }
1703
1704    /// Return a `wgpu_hal::Alignments` structure describing this adapter.
1705    ///
1706    /// The `using_robustness2` argument says how this adapter will implement
1707    /// `wgpu_hal`'s guarantee that shaders can only read the [accessible
1708    /// region][ar] of bindgroup's buffer bindings:
1709    ///
1710    /// - If this adapter will depend on `VK_EXT_robustness2`'s
1711    ///   `robustBufferAccess2` feature to apply bounds checks to shader buffer
1712    ///   access, `using_robustness2` must be `true`.
1713    ///
1714    /// - Otherwise, this adapter must use Naga to inject bounds checks on
1715    ///   buffer accesses, and `using_robustness2` must be `false`.
1716    ///
1717    /// [ar]: ../../struct.BufferBinding.html#accessible-region
1718    fn to_hal_alignments(&self, using_robustness2: bool) -> crate::Alignments {
1719        let limits = &self.properties.limits;
1720        crate::Alignments {
1721            buffer_copy_offset: wgt::BufferSize::new(limits.optimal_buffer_copy_offset_alignment)
1722                .unwrap(),
1723            buffer_copy_pitch: wgt::BufferSize::new(limits.optimal_buffer_copy_row_pitch_alignment)
1724                .unwrap(),
1725            uniform_bounds_check_alignment: {
1726                let alignment = if using_robustness2 {
1727                    self.robustness2
1728                        .unwrap() // if we're using it, we should have its properties
1729                        .robust_uniform_buffer_access_size_alignment
1730                } else {
1731                    // If the `robustness2` properties are unavailable, then `robustness2` is not available either Naga-injected bounds checks are precise.
1732                    1
1733                };
1734                wgt::BufferSize::new(alignment).unwrap()
1735            },
1736            raw_tlas_instance_size: 64,
1737            ray_tracing_scratch_buffer_alignment: self.acceleration_structure.map_or(
1738                0,
1739                |acceleration_structure| {
1740                    acceleration_structure.min_acceleration_structure_scratch_offset_alignment
1741                },
1742            ),
1743        }
1744    }
1745}
1746
1747impl super::InstanceShared {
1748    fn inspect(
1749        &self,
1750        phd: vk::PhysicalDevice,
1751    ) -> (PhysicalDeviceProperties, PhysicalDeviceFeatures) {
1752        let capabilities = {
1753            let mut capabilities = PhysicalDeviceProperties::default();
1754            capabilities.supported_extensions =
1755                unsafe { self.raw.enumerate_device_extension_properties(phd).unwrap() };
1756            capabilities.properties = unsafe { self.raw.get_physical_device_properties(phd) };
1757            capabilities.device_api_version = capabilities.properties.api_version;
1758
1759            let supports_multiview = capabilities.device_api_version >= vk::API_VERSION_1_1
1760                || capabilities.supports_extension(khr::multiview::NAME);
1761
1762            if let Some(ref get_device_properties) = self.get_physical_device_properties {
1763                // Get these now to avoid borrowing conflicts later
1764                let supports_maintenance3 = capabilities.device_api_version >= vk::API_VERSION_1_1
1765                    || capabilities.supports_extension(khr::maintenance3::NAME);
1766                let supports_maintenance4 = capabilities.device_api_version >= vk::API_VERSION_1_3
1767                    || capabilities.supports_extension(khr::maintenance4::NAME);
1768                let supports_descriptor_indexing = capabilities.device_api_version
1769                    >= vk::API_VERSION_1_2
1770                    || capabilities.supports_extension(ext::descriptor_indexing::NAME);
1771                let supports_driver_properties = capabilities.device_api_version
1772                    >= vk::API_VERSION_1_2
1773                    || capabilities.supports_extension(khr::driver_properties::NAME);
1774                let supports_subgroup_size_control = capabilities.device_api_version
1775                    >= vk::API_VERSION_1_3
1776                    || capabilities.supports_extension(ext::subgroup_size_control::NAME);
1777                let supports_robustness2 = capabilities.supports_extension(ext::robustness2::NAME);
1778                let supports_pci_bus_info =
1779                    capabilities.supports_extension(ext::pci_bus_info::NAME);
1780
1781                let supports_acceleration_structure =
1782                    capabilities.supports_extension(khr::acceleration_structure::NAME);
1783
1784                let supports_mesh_shader = capabilities.supports_extension(ext::mesh_shader::NAME);
1785
1786                let mut properties2 = vk::PhysicalDeviceProperties2KHR::default();
1787                if supports_maintenance3 {
1788                    let next = capabilities
1789                        .maintenance_3
1790                        .insert(vk::PhysicalDeviceMaintenance3Properties::default());
1791                    properties2 = properties2.push_next(next);
1792                }
1793
1794                if supports_maintenance4 {
1795                    let next = capabilities
1796                        .maintenance_4
1797                        .insert(vk::PhysicalDeviceMaintenance4Properties::default());
1798                    properties2 = properties2.push_next(next);
1799                }
1800
1801                if supports_descriptor_indexing {
1802                    let next = capabilities
1803                        .descriptor_indexing
1804                        .insert(vk::PhysicalDeviceDescriptorIndexingPropertiesEXT::default());
1805                    properties2 = properties2.push_next(next);
1806                }
1807
1808                if supports_acceleration_structure {
1809                    let next = capabilities
1810                        .acceleration_structure
1811                        .insert(vk::PhysicalDeviceAccelerationStructurePropertiesKHR::default());
1812                    properties2 = properties2.push_next(next);
1813                }
1814
1815                if supports_driver_properties {
1816                    let next = capabilities
1817                        .driver
1818                        .insert(vk::PhysicalDeviceDriverPropertiesKHR::default());
1819                    properties2 = properties2.push_next(next);
1820                }
1821
1822                if capabilities.device_api_version >= vk::API_VERSION_1_1 {
1823                    let next = capabilities
1824                        .subgroup
1825                        .insert(vk::PhysicalDeviceSubgroupProperties::default());
1826                    properties2 = properties2.push_next(next);
1827                }
1828
1829                if supports_subgroup_size_control {
1830                    let next = capabilities
1831                        .subgroup_size_control
1832                        .insert(vk::PhysicalDeviceSubgroupSizeControlProperties::default());
1833                    properties2 = properties2.push_next(next);
1834                }
1835
1836                if supports_robustness2 {
1837                    let next = capabilities
1838                        .robustness2
1839                        .insert(vk::PhysicalDeviceRobustness2PropertiesEXT::default());
1840                    properties2 = properties2.push_next(next);
1841                }
1842
1843                if supports_pci_bus_info {
1844                    let next = capabilities
1845                        .pci_bus_info
1846                        .insert(vk::PhysicalDevicePCIBusInfoPropertiesEXT::default());
1847                    properties2 = properties2.push_next(next);
1848                }
1849
1850                if supports_mesh_shader {
1851                    let next = capabilities
1852                        .mesh_shader
1853                        .insert(vk::PhysicalDeviceMeshShaderPropertiesEXT::default());
1854                    properties2 = properties2.push_next(next);
1855                }
1856
1857                if supports_multiview {
1858                    let next = capabilities
1859                        .multiview
1860                        .insert(vk::PhysicalDeviceMultiviewProperties::default());
1861                    properties2 = properties2.push_next(next);
1862                }
1863
1864                unsafe {
1865                    get_device_properties.get_physical_device_properties2(phd, &mut properties2)
1866                };
1867
1868                // Query cooperative matrix properties
1869                if capabilities.supports_extension(khr::cooperative_matrix::NAME) {
1870                    let coop_matrix =
1871                        khr::cooperative_matrix::Instance::new(&self.entry, &self.raw);
1872                    capabilities.cooperative_matrix_properties =
1873                        query_cooperative_matrix_properties(&coop_matrix, phd);
1874                }
1875
1876                if is_intel_igpu_outdated_for_robustness2(
1877                    capabilities.properties,
1878                    capabilities.driver,
1879                ) {
1880                    capabilities
1881                        .supported_extensions
1882                        .retain(|&x| x.extension_name_as_c_str() != Ok(ext::robustness2::NAME));
1883                    capabilities.robustness2 = None;
1884                }
1885            };
1886            capabilities
1887        };
1888
1889        let mut features = PhysicalDeviceFeatures::default();
1890        features.core = if let Some(ref get_device_properties) = self.get_physical_device_properties
1891        {
1892            let core = vk::PhysicalDeviceFeatures::default();
1893            let mut features2 = vk::PhysicalDeviceFeatures2KHR::default().features(core);
1894
1895            // `VK_KHR_multiview` is promoted to 1.1
1896            if capabilities.device_api_version >= vk::API_VERSION_1_1
1897                || capabilities.supports_extension(khr::multiview::NAME)
1898            {
1899                let next = features
1900                    .multiview
1901                    .insert(vk::PhysicalDeviceMultiviewFeatures::default());
1902                features2 = features2.push_next(next);
1903            }
1904
1905            // `VK_KHR_sampler_ycbcr_conversion` is promoted to 1.1
1906            if capabilities.device_api_version >= vk::API_VERSION_1_1
1907                || capabilities.supports_extension(khr::sampler_ycbcr_conversion::NAME)
1908            {
1909                let next = features
1910                    .sampler_ycbcr_conversion
1911                    .insert(vk::PhysicalDeviceSamplerYcbcrConversionFeatures::default());
1912                features2 = features2.push_next(next);
1913            }
1914
1915            if capabilities.supports_extension(ext::descriptor_indexing::NAME) {
1916                let next = features
1917                    .descriptor_indexing
1918                    .insert(vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default());
1919                features2 = features2.push_next(next);
1920            }
1921
1922            // `VK_KHR_timeline_semaphore` is promoted to 1.2, but has no
1923            // changes, so we can keep using the extension unconditionally.
1924            if capabilities.supports_extension(khr::timeline_semaphore::NAME) {
1925                let next = features
1926                    .timeline_semaphore
1927                    .insert(vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR::default());
1928                features2 = features2.push_next(next);
1929            }
1930
1931            // `VK_KHR_shader_atomic_int64` is promoted to 1.2, but has no
1932            // changes, so we can keep using the extension unconditionally.
1933            if capabilities.device_api_version >= vk::API_VERSION_1_2
1934                || capabilities.supports_extension(khr::shader_atomic_int64::NAME)
1935            {
1936                let next = features
1937                    .shader_atomic_int64
1938                    .insert(vk::PhysicalDeviceShaderAtomicInt64Features::default());
1939                features2 = features2.push_next(next);
1940            }
1941
1942            if capabilities.supports_extension(ext::shader_image_atomic_int64::NAME) {
1943                let next = features
1944                    .shader_image_atomic_int64
1945                    .insert(vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT::default());
1946                features2 = features2.push_next(next);
1947            }
1948            if capabilities.supports_extension(ext::shader_atomic_float::NAME) {
1949                let next = features
1950                    .shader_atomic_float
1951                    .insert(vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT::default());
1952                features2 = features2.push_next(next);
1953            }
1954            if capabilities.supports_extension(ext::image_robustness::NAME) {
1955                let next = features
1956                    .image_robustness
1957                    .insert(vk::PhysicalDeviceImageRobustnessFeaturesEXT::default());
1958                features2 = features2.push_next(next);
1959            }
1960            if capabilities.supports_extension(ext::robustness2::NAME) {
1961                let next = features
1962                    .robustness2
1963                    .insert(vk::PhysicalDeviceRobustness2FeaturesEXT::default());
1964                features2 = features2.push_next(next);
1965            }
1966            if capabilities.supports_extension(ext::texture_compression_astc_hdr::NAME) {
1967                let next = features
1968                    .astc_hdr
1969                    .insert(vk::PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT::default());
1970                features2 = features2.push_next(next);
1971            }
1972
1973            // `VK_KHR_shader_float16_int8` is promoted to 1.2
1974            if capabilities.device_api_version >= vk::API_VERSION_1_2
1975                || capabilities.supports_extension(khr::shader_float16_int8::NAME)
1976            {
1977                let next = features
1978                    .shader_float16_int8
1979                    .insert(vk::PhysicalDeviceShaderFloat16Int8FeaturesKHR::default());
1980                features2 = features2.push_next(next);
1981            }
1982
1983            if capabilities.supports_extension(khr::_16bit_storage::NAME) {
1984                let next = features
1985                    ._16bit_storage
1986                    .insert(vk::PhysicalDevice16BitStorageFeaturesKHR::default());
1987                features2 = features2.push_next(next);
1988            }
1989            if capabilities.supports_extension(khr::acceleration_structure::NAME) {
1990                let next = features
1991                    .acceleration_structure
1992                    .insert(vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default());
1993                features2 = features2.push_next(next);
1994            }
1995
1996            if capabilities.supports_extension(khr::ray_tracing_position_fetch::NAME) {
1997                let next = features
1998                    .position_fetch
1999                    .insert(vk::PhysicalDeviceRayTracingPositionFetchFeaturesKHR::default());
2000                features2 = features2.push_next(next);
2001            }
2002
2003            // `VK_KHR_maintenance4` is promoted to 1.3
2004            if capabilities.device_api_version >= vk::API_VERSION_1_3
2005                || capabilities.supports_extension(khr::maintenance4::NAME)
2006            {
2007                let next = features
2008                    .maintenance4
2009                    .insert(vk::PhysicalDeviceMaintenance4Features::default());
2010                features2 = features2.push_next(next);
2011            }
2012
2013            // `VK_KHR_zero_initialize_workgroup_memory` is promoted to 1.3
2014            if capabilities.device_api_version >= vk::API_VERSION_1_3
2015                || capabilities.supports_extension(khr::zero_initialize_workgroup_memory::NAME)
2016            {
2017                let next = features
2018                    .zero_initialize_workgroup_memory
2019                    .insert(vk::PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures::default());
2020                features2 = features2.push_next(next);
2021            }
2022
2023            // `VK_EXT_subgroup_size_control` is promoted to 1.3
2024            if capabilities.device_api_version >= vk::API_VERSION_1_3
2025                || capabilities.supports_extension(ext::subgroup_size_control::NAME)
2026            {
2027                let next = features
2028                    .subgroup_size_control
2029                    .insert(vk::PhysicalDeviceSubgroupSizeControlFeatures::default());
2030                features2 = features2.push_next(next);
2031            }
2032
2033            if capabilities.supports_extension(ext::mesh_shader::NAME) {
2034                let next = features
2035                    .mesh_shader
2036                    .insert(vk::PhysicalDeviceMeshShaderFeaturesEXT::default());
2037                features2 = features2.push_next(next);
2038            }
2039
2040            // `VK_KHR_shader_integer_dot_product` is promoted to 1.3
2041            if capabilities.device_api_version >= vk::API_VERSION_1_3
2042                || capabilities.supports_extension(khr::shader_integer_dot_product::NAME)
2043            {
2044                let next = features
2045                    .shader_integer_dot_product
2046                    .insert(vk::PhysicalDeviceShaderIntegerDotProductFeatures::default());
2047                features2 = features2.push_next(next);
2048            }
2049
2050            if capabilities.supports_extension(khr::fragment_shader_barycentric::NAME) {
2051                let next = features
2052                    .shader_barycentrics
2053                    .insert(vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR::default());
2054                features2 = features2.push_next(next);
2055            }
2056
2057            if capabilities.supports_extension(khr::portability_subset::NAME) {
2058                let next = features
2059                    .portability_subset
2060                    .insert(vk::PhysicalDevicePortabilitySubsetFeaturesKHR::default());
2061                features2 = features2.push_next(next);
2062            }
2063
2064            if capabilities.supports_extension(khr::cooperative_matrix::NAME) {
2065                let next = features
2066                    .cooperative_matrix
2067                    .insert(vk::PhysicalDeviceCooperativeMatrixFeaturesKHR::default());
2068                features2 = features2.push_next(next);
2069            }
2070
2071            if capabilities.device_api_version >= vk::API_VERSION_1_2
2072                || capabilities.supports_extension(khr::vulkan_memory_model::NAME)
2073            {
2074                let next = features
2075                    .vulkan_memory_model
2076                    .insert(vk::PhysicalDeviceVulkanMemoryModelFeaturesKHR::default());
2077                features2 = features2.push_next(next);
2078            }
2079
2080            if capabilities.device_api_version >= vk::API_VERSION_1_1 {
2081                let next = features
2082                    .shader_draw_parameters
2083                    .insert(vk::PhysicalDeviceShaderDrawParametersFeatures::default());
2084                features2 = features2.push_next(next);
2085            }
2086
2087            unsafe { get_device_properties.get_physical_device_features2(phd, &mut features2) };
2088            features2.features
2089        } else {
2090            unsafe { self.raw.get_physical_device_features(phd) }
2091        };
2092
2093        (capabilities, features)
2094    }
2095}
2096
2097impl super::Instance {
2098    pub fn expose_adapter(
2099        &self,
2100        phd: vk::PhysicalDevice,
2101    ) -> Option<crate::ExposedAdapter<super::Api>> {
2102        use crate::auxil::db;
2103
2104        let (phd_capabilities, phd_features) = self.shared.inspect(phd);
2105
2106        let mem_properties = {
2107            profiling::scope!("vkGetPhysicalDeviceMemoryProperties");
2108            unsafe { self.shared.raw.get_physical_device_memory_properties(phd) }
2109        };
2110        let memory_types = &mem_properties.memory_types_as_slice();
2111        let supports_lazily_allocated = memory_types.iter().any(|mem| {
2112            mem.property_flags
2113                .contains(vk::MemoryPropertyFlags::LAZILY_ALLOCATED)
2114        });
2115
2116        let info = wgt::AdapterInfo {
2117            name: {
2118                phd_capabilities
2119                    .properties
2120                    .device_name_as_c_str()
2121                    .ok()
2122                    .and_then(|name| name.to_str().ok())
2123                    .unwrap_or("?")
2124                    .to_owned()
2125            },
2126            vendor: phd_capabilities.properties.vendor_id,
2127            device: phd_capabilities.properties.device_id,
2128            device_type: match phd_capabilities.properties.device_type {
2129                vk::PhysicalDeviceType::OTHER => wgt::DeviceType::Other,
2130                vk::PhysicalDeviceType::INTEGRATED_GPU => wgt::DeviceType::IntegratedGpu,
2131                vk::PhysicalDeviceType::DISCRETE_GPU => wgt::DeviceType::DiscreteGpu,
2132                vk::PhysicalDeviceType::VIRTUAL_GPU => wgt::DeviceType::VirtualGpu,
2133                vk::PhysicalDeviceType::CPU => wgt::DeviceType::Cpu,
2134                _ => wgt::DeviceType::Other,
2135            },
2136            device_pci_bus_id: phd_capabilities
2137                .pci_bus_info
2138                .filter(|info| info.pci_bus != 0 || info.pci_device != 0)
2139                .map(|info| {
2140                    format!(
2141                        "{:04x}:{:02x}:{:02x}.{}",
2142                        info.pci_domain, info.pci_bus, info.pci_device, info.pci_function
2143                    )
2144                })
2145                .unwrap_or_default(),
2146            driver: {
2147                phd_capabilities
2148                    .driver
2149                    .as_ref()
2150                    .and_then(|driver| driver.driver_name_as_c_str().ok())
2151                    .and_then(|name| name.to_str().ok())
2152                    .unwrap_or("?")
2153                    .to_owned()
2154            },
2155            driver_info: {
2156                phd_capabilities
2157                    .driver
2158                    .as_ref()
2159                    .and_then(|driver| driver.driver_info_as_c_str().ok())
2160                    .and_then(|name| name.to_str().ok())
2161                    .unwrap_or("?")
2162                    .to_owned()
2163            },
2164            backend: wgt::Backend::Vulkan,
2165            subgroup_min_size: phd_capabilities
2166                .subgroup_size_control
2167                .map(|subgroup_size| subgroup_size.min_subgroup_size)
2168                .unwrap_or(wgt::MINIMUM_SUBGROUP_MIN_SIZE),
2169            subgroup_max_size: phd_capabilities
2170                .subgroup_size_control
2171                .map(|subgroup_size| subgroup_size.max_subgroup_size)
2172                .unwrap_or(wgt::MAXIMUM_SUBGROUP_MAX_SIZE),
2173            transient_saves_memory: supports_lazily_allocated,
2174        };
2175        let mut workarounds = super::Workarounds::empty();
2176        {
2177            // TODO: only enable for particular devices
2178            workarounds |= super::Workarounds::SEPARATE_ENTRY_POINTS;
2179            workarounds.set(
2180                super::Workarounds::EMPTY_RESOLVE_ATTACHMENT_LISTS,
2181                phd_capabilities.properties.vendor_id == db::qualcomm::VENDOR,
2182            );
2183            workarounds.set(
2184                super::Workarounds::FORCE_FILL_BUFFER_WITH_SIZE_GREATER_4096_ALIGNED_OFFSET_16,
2185                phd_capabilities.properties.vendor_id == db::nvidia::VENDOR,
2186            );
2187        };
2188
2189        if let Some(driver) = phd_capabilities.driver {
2190            if driver.conformance_version.major == 0 {
2191                if driver.driver_id == vk::DriverId::MOLTENVK {
2192                    log::debug!("Adapter is not Vulkan compliant, but is MoltenVK, continuing");
2193                } else if self
2194                    .shared
2195                    .flags
2196                    .contains(wgt::InstanceFlags::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER)
2197                {
2198                    log::debug!("Adapter is not Vulkan compliant: {}", info.name);
2199                } else {
2200                    log::debug!(
2201                        "Adapter is not Vulkan compliant, hiding adapter: {}",
2202                        info.name
2203                    );
2204                    return None;
2205                }
2206            }
2207        }
2208        if phd_capabilities.device_api_version == vk::API_VERSION_1_0
2209            && !phd_capabilities.supports_extension(khr::storage_buffer_storage_class::NAME)
2210        {
2211            log::debug!(
2212                "SPIR-V storage buffer class is not supported, hiding adapter: {}",
2213                info.name
2214            );
2215            return None;
2216        }
2217        if !phd_capabilities.supports_extension(khr::maintenance1::NAME)
2218            && phd_capabilities.device_api_version < vk::API_VERSION_1_1
2219        {
2220            log::debug!(
2221                "VK_KHR_maintenance1 is not supported, hiding adapter: {}",
2222                info.name
2223            );
2224            return None;
2225        }
2226
2227        let queue_families = unsafe {
2228            self.shared
2229                .raw
2230                .get_physical_device_queue_family_properties(phd)
2231        };
2232        let queue_family_properties = queue_families.first()?;
2233        let queue_flags = queue_family_properties.queue_flags;
2234        if !queue_flags.contains(vk::QueueFlags::GRAPHICS) {
2235            log::debug!("The first queue only exposes {queue_flags:?}");
2236            return None;
2237        }
2238
2239        let (available_features, mut downlevel_flags) = phd_features.to_wgpu(
2240            &self.shared.raw,
2241            phd,
2242            &phd_capabilities,
2243            queue_family_properties,
2244        );
2245
2246        if info.driver == "llvmpipe" {
2247            // The `F16_IN_F32` instructions do not normally require native `F16` support, but on
2248            // llvmpipe, they do.
2249            downlevel_flags.set(
2250                wgt::DownlevelFlags::SHADER_F16_IN_F32,
2251                available_features.contains(wgt::Features::SHADER_F16),
2252            );
2253        }
2254
2255        let has_robust_buffer_access2 = phd_features
2256            .robustness2
2257            .as_ref()
2258            .map(|r| r.robust_buffer_access2 == 1)
2259            .unwrap_or_default();
2260
2261        let alignments = phd_capabilities.to_hal_alignments(has_robust_buffer_access2);
2262
2263        let private_caps = super::PrivateCapabilities {
2264            image_view_usage: phd_capabilities.device_api_version >= vk::API_VERSION_1_1
2265                || phd_capabilities.supports_extension(khr::maintenance2::NAME),
2266            timeline_semaphores: match phd_features.timeline_semaphore {
2267                Some(features) => features.timeline_semaphore == vk::TRUE,
2268                None => phd_features
2269                    .timeline_semaphore
2270                    .is_some_and(|ext| ext.timeline_semaphore != 0),
2271            },
2272            texture_d24: supports_format(
2273                &self.shared.raw,
2274                phd,
2275                vk::Format::X8_D24_UNORM_PACK32,
2276                vk::ImageTiling::OPTIMAL,
2277                depth_stencil_required_flags(),
2278            ),
2279            texture_d24_s8: supports_format(
2280                &self.shared.raw,
2281                phd,
2282                vk::Format::D24_UNORM_S8_UINT,
2283                vk::ImageTiling::OPTIMAL,
2284                depth_stencil_required_flags(),
2285            ),
2286            texture_s8: supports_format(
2287                &self.shared.raw,
2288                phd,
2289                vk::Format::S8_UINT,
2290                vk::ImageTiling::OPTIMAL,
2291                depth_stencil_required_flags(),
2292            ),
2293            multi_draw_indirect: phd_features.core.multi_draw_indirect != 0,
2294            max_draw_indirect_count: phd_capabilities.properties.limits.max_draw_indirect_count,
2295            non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1,
2296            can_present: true,
2297            //TODO: make configurable
2298            robust_buffer_access: phd_features.core.robust_buffer_access != 0,
2299            robust_image_access: match phd_features.robustness2 {
2300                Some(ref f) => f.robust_image_access2 != 0,
2301                None => phd_features
2302                    .image_robustness
2303                    .is_some_and(|ext| ext.robust_image_access != 0),
2304            },
2305            robust_buffer_access2: has_robust_buffer_access2,
2306            robust_image_access2: phd_features
2307                .robustness2
2308                .as_ref()
2309                .map(|r| r.robust_image_access2 == 1)
2310                .unwrap_or_default(),
2311            zero_initialize_workgroup_memory: phd_features
2312                .zero_initialize_workgroup_memory
2313                .is_some_and(|ext| ext.shader_zero_initialize_workgroup_memory == vk::TRUE),
2314            image_format_list: phd_capabilities.device_api_version >= vk::API_VERSION_1_2
2315                || phd_capabilities.supports_extension(khr::image_format_list::NAME),
2316            maximum_samplers: phd_capabilities
2317                .properties
2318                .limits
2319                .max_sampler_allocation_count,
2320            shader_integer_dot_product: phd_features
2321                .shader_integer_dot_product
2322                .is_some_and(|ext| ext.shader_integer_dot_product != 0),
2323            shader_int8: phd_features
2324                .shader_float16_int8
2325                .is_some_and(|features| features.shader_int8 != 0),
2326            multiview_instance_index_limit: phd_capabilities
2327                .multiview
2328                .map(|a| a.max_multiview_instance_index)
2329                .unwrap_or(0),
2330            scratch_buffer_alignment: alignments.ray_tracing_scratch_buffer_alignment,
2331        };
2332        let capabilities = crate::Capabilities {
2333            limits: phd_capabilities.to_wgpu_limits(),
2334            alignments,
2335            downlevel: wgt::DownlevelCapabilities {
2336                flags: downlevel_flags,
2337                limits: wgt::DownlevelLimits {},
2338                shader_model: wgt::ShaderModel::Sm5, //TODO?
2339            },
2340            cooperative_matrix_properties: phd_capabilities.cooperative_matrix_properties.clone(),
2341        };
2342
2343        let adapter = super::Adapter {
2344            raw: phd,
2345            instance: Arc::clone(&self.shared),
2346            //queue_families,
2347            known_memory_flags: vk::MemoryPropertyFlags::DEVICE_LOCAL
2348                | vk::MemoryPropertyFlags::HOST_VISIBLE
2349                | vk::MemoryPropertyFlags::HOST_COHERENT
2350                | vk::MemoryPropertyFlags::HOST_CACHED
2351                | vk::MemoryPropertyFlags::LAZILY_ALLOCATED,
2352            phd_capabilities,
2353            phd_features,
2354            downlevel_flags,
2355            private_caps,
2356            workarounds,
2357        };
2358
2359        Some(crate::ExposedAdapter {
2360            adapter,
2361            info,
2362            features: available_features,
2363            capabilities,
2364        })
2365    }
2366}
2367
2368impl super::Adapter {
2369    pub fn raw_physical_device(&self) -> vk::PhysicalDevice {
2370        self.raw
2371    }
2372
2373    pub fn get_physical_device_features(&self) -> &PhysicalDeviceFeatures {
2374        &self.phd_features
2375    }
2376
2377    pub fn physical_device_capabilities(&self) -> &PhysicalDeviceProperties {
2378        &self.phd_capabilities
2379    }
2380
2381    pub fn shared_instance(&self) -> &super::InstanceShared {
2382        &self.instance
2383    }
2384
2385    pub fn required_device_extensions(&self, features: wgt::Features) -> Vec<&'static CStr> {
2386        let (supported_extensions, unsupported_extensions) = self
2387            .phd_capabilities
2388            .get_required_extensions(features)
2389            .iter()
2390            .partition::<Vec<&CStr>, _>(|&&extension| {
2391                self.phd_capabilities.supports_extension(extension)
2392            });
2393
2394        if !unsupported_extensions.is_empty() {
2395            log::debug!("Missing extensions: {unsupported_extensions:?}");
2396        }
2397
2398        log::debug!("Supported extensions: {supported_extensions:?}");
2399        supported_extensions
2400    }
2401
2402    /// Create a `PhysicalDeviceFeatures` for opening a logical device with
2403    /// `features` from this adapter.
2404    ///
2405    /// The given `enabled_extensions` set must include all the extensions
2406    /// selected by [`required_device_extensions`] when passed `features`.
2407    /// Otherwise, the `PhysicalDeviceFeatures` value may not be able to select
2408    /// all the Vulkan features needed to represent `features` and this
2409    /// adapter's characteristics.
2410    ///
2411    /// Typically, you'd simply call `required_device_extensions`, and then pass
2412    /// its return value and the feature set you gave it directly to this
2413    /// function. But it's fine to add more extensions to the list.
2414    ///
2415    /// [`required_device_extensions`]: Self::required_device_extensions
2416    pub fn physical_device_features(
2417        &self,
2418        enabled_extensions: &[&'static CStr],
2419        features: wgt::Features,
2420    ) -> PhysicalDeviceFeatures {
2421        PhysicalDeviceFeatures::from_extensions_and_requested_features(
2422            &self.phd_capabilities,
2423            &self.phd_features,
2424            enabled_extensions,
2425            features,
2426            self.downlevel_flags,
2427            &self.private_caps,
2428        )
2429    }
2430
2431    /// # Safety
2432    ///
2433    /// - `raw_device` must be created from this adapter.
2434    /// - `raw_device` must be created using `family_index`, `enabled_extensions` and `physical_device_features()`
2435    /// - `enabled_extensions` must be a superset of `required_device_extensions()`.
2436    /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_device`. If
2437    ///   `drop_callback` is [`Some`], `raw_device` must be valid until the callback is called.
2438    #[allow(clippy::too_many_arguments)]
2439    pub unsafe fn device_from_raw(
2440        &self,
2441        raw_device: ash::Device,
2442        drop_callback: Option<crate::DropCallback>,
2443        enabled_extensions: &[&'static CStr],
2444        features: wgt::Features,
2445        limits: &wgt::Limits,
2446        memory_hints: &wgt::MemoryHints,
2447        family_index: u32,
2448        queue_index: u32,
2449    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
2450        let mem_properties = {
2451            profiling::scope!("vkGetPhysicalDeviceMemoryProperties");
2452            unsafe {
2453                self.instance
2454                    .raw
2455                    .get_physical_device_memory_properties(self.raw)
2456            }
2457        };
2458        let memory_types = &mem_properties.memory_types_as_slice();
2459        let valid_ash_memory_types = memory_types.iter().enumerate().fold(0, |u, (i, mem)| {
2460            if self.known_memory_flags.contains(mem.property_flags) {
2461                u | (1 << i)
2462            } else {
2463                u
2464            }
2465        });
2466
2467        // Note that VK_EXT_debug_utils is an instance extension (enabled at the instance
2468        // level) but contains a few functions that can be loaded directly on the Device for a
2469        // dispatch-table-less pointer.
2470        let debug_utils_fn = if self.instance.extensions.contains(&ext::debug_utils::NAME) {
2471            Some(ext::debug_utils::Device::new(
2472                &self.instance.raw,
2473                &raw_device,
2474            ))
2475        } else {
2476            None
2477        };
2478        let indirect_count_fn = if enabled_extensions.contains(&khr::draw_indirect_count::NAME) {
2479            Some(khr::draw_indirect_count::Device::new(
2480                &self.instance.raw,
2481                &raw_device,
2482            ))
2483        } else {
2484            None
2485        };
2486        let timeline_semaphore_fn = if enabled_extensions.contains(&khr::timeline_semaphore::NAME) {
2487            Some(super::ExtensionFn::Extension(
2488                khr::timeline_semaphore::Device::new(&self.instance.raw, &raw_device),
2489            ))
2490        } else if self.phd_capabilities.device_api_version >= vk::API_VERSION_1_2 {
2491            Some(super::ExtensionFn::Promoted)
2492        } else {
2493            None
2494        };
2495        let ray_tracing_fns = if enabled_extensions.contains(&khr::acceleration_structure::NAME)
2496            && enabled_extensions.contains(&khr::buffer_device_address::NAME)
2497        {
2498            Some(super::RayTracingDeviceExtensionFunctions {
2499                acceleration_structure: khr::acceleration_structure::Device::new(
2500                    &self.instance.raw,
2501                    &raw_device,
2502                ),
2503                buffer_device_address: khr::buffer_device_address::Device::new(
2504                    &self.instance.raw,
2505                    &raw_device,
2506                ),
2507            })
2508        } else {
2509            None
2510        };
2511        let mesh_shading_fns = if enabled_extensions.contains(&ext::mesh_shader::NAME) {
2512            Some(ext::mesh_shader::Device::new(
2513                &self.instance.raw,
2514                &raw_device,
2515            ))
2516        } else {
2517            None
2518        };
2519
2520        let naga_options = {
2521            use naga::back::spv;
2522
2523            // The following capabilities are always available
2524            // see https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap52.html#spirvenv-capabilities
2525            let mut capabilities = vec![
2526                spv::Capability::Shader,
2527                spv::Capability::Matrix,
2528                spv::Capability::Sampled1D,
2529                spv::Capability::Image1D,
2530                spv::Capability::ImageQuery,
2531                spv::Capability::DerivativeControl,
2532                spv::Capability::StorageImageExtendedFormats,
2533            ];
2534
2535            if self
2536                .downlevel_flags
2537                .contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES)
2538            {
2539                capabilities.push(spv::Capability::SampledCubeArray);
2540            }
2541
2542            if self
2543                .downlevel_flags
2544                .contains(wgt::DownlevelFlags::MULTISAMPLED_SHADING)
2545            {
2546                capabilities.push(spv::Capability::SampleRateShading);
2547            }
2548
2549            if features.contains(wgt::Features::MULTIVIEW) {
2550                capabilities.push(spv::Capability::MultiView);
2551            }
2552
2553            if features.contains(wgt::Features::PRIMITIVE_INDEX) {
2554                capabilities.push(spv::Capability::Geometry);
2555            }
2556
2557            if features.intersects(wgt::Features::SUBGROUP | wgt::Features::SUBGROUP_VERTEX) {
2558                capabilities.push(spv::Capability::GroupNonUniform);
2559                capabilities.push(spv::Capability::GroupNonUniformVote);
2560                capabilities.push(spv::Capability::GroupNonUniformArithmetic);
2561                capabilities.push(spv::Capability::GroupNonUniformBallot);
2562                capabilities.push(spv::Capability::GroupNonUniformShuffle);
2563                capabilities.push(spv::Capability::GroupNonUniformShuffleRelative);
2564                capabilities.push(spv::Capability::GroupNonUniformQuad);
2565            }
2566
2567            if features.intersects(
2568                wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
2569                    | wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
2570                    | wgt::Features::UNIFORM_BUFFER_BINDING_ARRAYS,
2571            ) {
2572                capabilities.push(spv::Capability::ShaderNonUniform);
2573            }
2574            if features.contains(wgt::Features::BGRA8UNORM_STORAGE) {
2575                capabilities.push(spv::Capability::StorageImageWriteWithoutFormat);
2576            }
2577
2578            if features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
2579                capabilities.push(spv::Capability::RayQueryKHR);
2580            }
2581
2582            if features.contains(wgt::Features::SHADER_INT64) {
2583                capabilities.push(spv::Capability::Int64);
2584            }
2585
2586            if features.contains(wgt::Features::SHADER_F16) {
2587                capabilities.push(spv::Capability::Float16);
2588            }
2589
2590            if features.intersects(
2591                wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS
2592                    | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX
2593                    | wgt::Features::TEXTURE_INT64_ATOMIC,
2594            ) {
2595                capabilities.push(spv::Capability::Int64Atomics);
2596            }
2597
2598            if features.intersects(wgt::Features::TEXTURE_INT64_ATOMIC) {
2599                capabilities.push(spv::Capability::Int64ImageEXT);
2600            }
2601
2602            if features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC) {
2603                capabilities.push(spv::Capability::AtomicFloat32AddEXT);
2604            }
2605
2606            if features.contains(wgt::Features::CLIP_DISTANCES) {
2607                capabilities.push(spv::Capability::ClipDistance);
2608            }
2609
2610            // Vulkan bundles both barycentrics and per-vertex attributes under the same feature.
2611            if features
2612                .intersects(wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX)
2613            {
2614                capabilities.push(spv::Capability::FragmentBarycentricKHR);
2615            }
2616
2617            if features.contains(wgt::Features::SHADER_DRAW_INDEX) {
2618                capabilities.push(spv::Capability::DrawParameters);
2619            }
2620
2621            let mut flags = spv::WriterFlags::empty();
2622            flags.set(
2623                spv::WriterFlags::DEBUG,
2624                self.instance.flags.contains(wgt::InstanceFlags::DEBUG),
2625            );
2626            flags.set(
2627                spv::WriterFlags::LABEL_VARYINGS,
2628                self.phd_capabilities.properties.vendor_id != crate::auxil::db::qualcomm::VENDOR,
2629            );
2630            flags.set(
2631                spv::WriterFlags::FORCE_POINT_SIZE,
2632                //Note: we could technically disable this when we are compiling separate entry points,
2633                // and we know exactly that the primitive topology is not `PointList`.
2634                // But this requires cloning the `spv::Options` struct, which has heap allocations.
2635                true, // could check `super::Workarounds::SEPARATE_ENTRY_POINTS`
2636            );
2637            flags.set(
2638                spv::WriterFlags::PRINT_ON_RAY_QUERY_INITIALIZATION_FAIL,
2639                self.instance.flags.contains(wgt::InstanceFlags::DEBUG)
2640                    && (self.instance.instance_api_version >= vk::API_VERSION_1_3
2641                        || enabled_extensions.contains(&khr::shader_non_semantic_info::NAME)),
2642            );
2643            if features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
2644                capabilities.push(spv::Capability::RayQueryKHR);
2645            }
2646            if features.contains(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN) {
2647                capabilities.push(spv::Capability::RayQueryPositionFetchKHR)
2648            }
2649            if features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
2650                capabilities.push(spv::Capability::MeshShadingEXT);
2651            }
2652            if features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
2653                capabilities.push(spv::Capability::CooperativeMatrixKHR);
2654                // TODO: expose this more generally
2655                capabilities.push(spv::Capability::VulkanMemoryModel);
2656            }
2657            if self.private_caps.shader_integer_dot_product {
2658                // See <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_integer_dot_product.html#_new_spir_v_capabilities>.
2659                capabilities.extend(&[
2660                    spv::Capability::DotProductInputAllKHR,
2661                    spv::Capability::DotProductInput4x8BitKHR,
2662                    spv::Capability::DotProductInput4x8BitPackedKHR,
2663                    spv::Capability::DotProductKHR,
2664                ]);
2665            }
2666            if self.private_caps.shader_int8 {
2667                // See <https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceShaderFloat16Int8Features.html#extension-features-shaderInt8>.
2668                capabilities.extend(&[spv::Capability::Int8]);
2669            }
2670            spv::Options {
2671                lang_version: match self.phd_capabilities.device_api_version {
2672                    // Use maximum supported SPIR-V version according to
2673                    // <https://github.com/KhronosGroup/Vulkan-Docs/blob/19b7651/appendices/spirvenv.adoc?plain=1#L21-L40>.
2674                    vk::API_VERSION_1_0..vk::API_VERSION_1_1 => (1, 0),
2675                    vk::API_VERSION_1_1..vk::API_VERSION_1_2 => (1, 3),
2676                    vk::API_VERSION_1_2..vk::API_VERSION_1_3 => (1, 5),
2677                    vk::API_VERSION_1_3.. => (1, 6),
2678                    _ => unreachable!(),
2679                },
2680                flags,
2681                capabilities: Some(capabilities.iter().cloned().collect()),
2682                bounds_check_policies: naga::proc::BoundsCheckPolicies {
2683                    index: naga::proc::BoundsCheckPolicy::Restrict,
2684                    buffer: if self.private_caps.robust_buffer_access2 {
2685                        naga::proc::BoundsCheckPolicy::Unchecked
2686                    } else {
2687                        naga::proc::BoundsCheckPolicy::Restrict
2688                    },
2689                    image_load: if self.private_caps.robust_image_access {
2690                        naga::proc::BoundsCheckPolicy::Unchecked
2691                    } else {
2692                        naga::proc::BoundsCheckPolicy::Restrict
2693                    },
2694                    // TODO: support bounds checks on binding arrays
2695                    binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
2696                },
2697                zero_initialize_workgroup_memory: if self
2698                    .private_caps
2699                    .zero_initialize_workgroup_memory
2700                {
2701                    spv::ZeroInitializeWorkgroupMemoryMode::Native
2702                } else {
2703                    spv::ZeroInitializeWorkgroupMemoryMode::Polyfill
2704                },
2705                force_loop_bounding: true,
2706                ray_query_initialization_tracking: true,
2707                use_storage_input_output_16: features.contains(wgt::Features::SHADER_F16)
2708                    && self.phd_features.supports_storage_input_output_16(),
2709                fake_missing_bindings: false,
2710                // We need to build this separately for each invocation, so just default it out here
2711                binding_map: BTreeMap::default(),
2712                debug_info: None,
2713                task_dispatch_limits: Some(naga::back::TaskDispatchLimits {
2714                    max_mesh_workgroups_per_dim: limits.max_task_mesh_workgroups_per_dimension,
2715                    max_mesh_workgroups_total: limits.max_task_mesh_workgroup_total_count,
2716                }),
2717                mesh_shader_primitive_indices_clamp: true,
2718            }
2719        };
2720
2721        let raw_queue = {
2722            profiling::scope!("vkGetDeviceQueue");
2723            unsafe { raw_device.get_device_queue(family_index, queue_index) }
2724        };
2725
2726        let driver_version = self
2727            .phd_capabilities
2728            .properties
2729            .driver_version
2730            .to_be_bytes();
2731        #[rustfmt::skip]
2732        let pipeline_cache_validation_key = [
2733            driver_version[0], driver_version[1], driver_version[2], driver_version[3],
2734            0, 0, 0, 0,
2735            0, 0, 0, 0,
2736            0, 0, 0, 0,
2737        ];
2738
2739        let drop_guard = crate::DropGuard::from_option(drop_callback);
2740
2741        let empty_descriptor_set_layout = unsafe {
2742            raw_device
2743                .create_descriptor_set_layout(&vk::DescriptorSetLayoutCreateInfo::default(), None)
2744                .map_err(super::map_host_device_oom_err)?
2745        };
2746
2747        let shared = Arc::new(super::DeviceShared {
2748            raw: raw_device,
2749            family_index,
2750            queue_index,
2751            raw_queue,
2752            drop_guard,
2753            instance: Arc::clone(&self.instance),
2754            physical_device: self.raw,
2755            enabled_extensions: enabled_extensions.into(),
2756            extension_fns: super::DeviceExtensionFunctions {
2757                debug_utils: debug_utils_fn,
2758                draw_indirect_count: indirect_count_fn,
2759                timeline_semaphore: timeline_semaphore_fn,
2760                ray_tracing: ray_tracing_fns,
2761                mesh_shading: mesh_shading_fns,
2762            },
2763            pipeline_cache_validation_key,
2764            vendor_id: self.phd_capabilities.properties.vendor_id,
2765            timestamp_period: self.phd_capabilities.properties.limits.timestamp_period,
2766            private_caps: self.private_caps.clone(),
2767            features,
2768            workarounds: self.workarounds,
2769            render_passes: Mutex::new(Default::default()),
2770            sampler_cache: Mutex::new(super::sampler::SamplerCache::new(
2771                self.private_caps.maximum_samplers,
2772            )),
2773            memory_allocations_counter: Default::default(),
2774
2775            texture_identity_factory: super::ResourceIdentityFactory::new(),
2776            texture_view_identity_factory: super::ResourceIdentityFactory::new(),
2777            empty_descriptor_set_layout,
2778        });
2779
2780        let relay_semaphores = super::RelaySemaphores::new(&shared)?;
2781
2782        let queue = super::Queue {
2783            raw: raw_queue,
2784            device: Arc::clone(&shared),
2785            family_index,
2786            relay_semaphores: Mutex::new(relay_semaphores),
2787            signal_semaphores: Mutex::new(SemaphoreList::new(SemaphoreListMode::Signal)),
2788        };
2789
2790        let allocation_sizes = AllocationSizes::from_memory_hints(memory_hints).into();
2791
2792        let buffer_device_address = enabled_extensions.contains(&khr::buffer_device_address::NAME);
2793
2794        let mem_allocator =
2795            gpu_allocator::vulkan::Allocator::new(&gpu_allocator::vulkan::AllocatorCreateDesc {
2796                instance: self.instance.raw.clone(),
2797                device: shared.raw.clone(),
2798                physical_device: self.raw,
2799                debug_settings: Default::default(),
2800                buffer_device_address,
2801                allocation_sizes,
2802            })?;
2803
2804        let desc_allocator = gpu_descriptor::DescriptorAllocator::new(
2805            if let Some(di) = self.phd_capabilities.descriptor_indexing {
2806                di.max_update_after_bind_descriptors_in_all_pools
2807            } else {
2808                0
2809            },
2810        );
2811
2812        let device = super::Device {
2813            shared,
2814            mem_allocator: Mutex::new(mem_allocator),
2815            desc_allocator: Mutex::new(desc_allocator),
2816            valid_ash_memory_types,
2817            naga_options,
2818            #[cfg(feature = "renderdoc")]
2819            render_doc: Default::default(),
2820            counters: Default::default(),
2821        };
2822
2823        Ok(crate::OpenDevice { device, queue })
2824    }
2825
2826    pub fn texture_format_as_raw(&self, texture_format: wgt::TextureFormat) -> vk::Format {
2827        self.private_caps.map_texture_format(texture_format)
2828    }
2829
2830    /// # Safety:
2831    /// - Same as `open` plus
2832    /// - The callback may not change anything that the device does not support.
2833    /// - The callback may not remove features.
2834    pub unsafe fn open_with_callback<'a>(
2835        &self,
2836        features: wgt::Features,
2837        limits: &wgt::Limits,
2838        memory_hints: &wgt::MemoryHints,
2839        callback: Option<Box<super::CreateDeviceCallback<'a>>>,
2840    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
2841        let mut enabled_extensions = self.required_device_extensions(features);
2842        let mut enabled_phd_features = self.physical_device_features(&enabled_extensions, features);
2843
2844        let family_index = 0; //TODO
2845        let family_info = vk::DeviceQueueCreateInfo::default()
2846            .queue_family_index(family_index)
2847            .queue_priorities(&[1.0]);
2848        let mut family_infos = Vec::from([family_info]);
2849
2850        let mut pre_info = vk::DeviceCreateInfo::default();
2851
2852        if let Some(callback) = callback {
2853            callback(super::CreateDeviceCallbackArgs {
2854                extensions: &mut enabled_extensions,
2855                device_features: &mut enabled_phd_features,
2856                queue_create_infos: &mut family_infos,
2857                create_info: &mut pre_info,
2858                _phantom: PhantomData,
2859            })
2860        }
2861
2862        let str_pointers = enabled_extensions
2863            .iter()
2864            .map(|&s| {
2865                // Safe because `enabled_extensions` entries have static lifetime.
2866                s.as_ptr()
2867            })
2868            .collect::<Vec<_>>();
2869
2870        let pre_info = pre_info
2871            .queue_create_infos(&family_infos)
2872            .enabled_extension_names(&str_pointers);
2873        let info = enabled_phd_features.add_to_device_create(pre_info);
2874        let raw_device = {
2875            profiling::scope!("vkCreateDevice");
2876            unsafe {
2877                self.instance
2878                    .raw
2879                    .create_device(self.raw, &info, None)
2880                    .map_err(map_err)?
2881            }
2882        };
2883        fn map_err(err: vk::Result) -> crate::DeviceError {
2884            match err {
2885                vk::Result::ERROR_TOO_MANY_OBJECTS => crate::DeviceError::OutOfMemory,
2886                vk::Result::ERROR_INITIALIZATION_FAILED => crate::DeviceError::Lost,
2887                vk::Result::ERROR_EXTENSION_NOT_PRESENT | vk::Result::ERROR_FEATURE_NOT_PRESENT => {
2888                    crate::hal_usage_error(err)
2889                }
2890                other => super::map_host_device_oom_and_lost_err(other),
2891            }
2892        }
2893
2894        unsafe {
2895            self.device_from_raw(
2896                raw_device,
2897                None,
2898                &enabled_extensions,
2899                features,
2900                limits,
2901                memory_hints,
2902                family_info.queue_family_index,
2903                0,
2904            )
2905        }
2906    }
2907}
2908
2909impl crate::Adapter for super::Adapter {
2910    type A = super::Api;
2911
2912    unsafe fn open(
2913        &self,
2914        features: wgt::Features,
2915        limits: &wgt::Limits,
2916        memory_hints: &wgt::MemoryHints,
2917    ) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
2918        unsafe { self.open_with_callback(features, limits, memory_hints, None) }
2919    }
2920
2921    unsafe fn texture_format_capabilities(
2922        &self,
2923        format: wgt::TextureFormat,
2924    ) -> crate::TextureFormatCapabilities {
2925        use crate::TextureFormatCapabilities as Tfc;
2926
2927        let vk_format = self.private_caps.map_texture_format(format);
2928        let properties = unsafe {
2929            self.instance
2930                .raw
2931                .get_physical_device_format_properties(self.raw, vk_format)
2932        };
2933        let features = properties.optimal_tiling_features;
2934
2935        let mut flags = Tfc::empty();
2936        flags.set(
2937            Tfc::SAMPLED,
2938            features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE),
2939        );
2940        flags.set(
2941            Tfc::SAMPLED_LINEAR,
2942            features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR),
2943        );
2944        // flags.set(
2945        //     Tfc::SAMPLED_MINMAX,
2946        //     features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX),
2947        // );
2948        flags.set(
2949            Tfc::STORAGE_READ_WRITE
2950                | Tfc::STORAGE_WRITE_ONLY
2951                | Tfc::STORAGE_READ_ONLY
2952                | Tfc::STORAGE_ATOMIC,
2953            features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE),
2954        );
2955        flags.set(
2956            Tfc::STORAGE_ATOMIC,
2957            features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE_ATOMIC),
2958        );
2959        flags.set(
2960            Tfc::COLOR_ATTACHMENT,
2961            features.contains(vk::FormatFeatureFlags::COLOR_ATTACHMENT),
2962        );
2963        flags.set(
2964            Tfc::COLOR_ATTACHMENT_BLEND,
2965            features.contains(vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND),
2966        );
2967        flags.set(
2968            Tfc::DEPTH_STENCIL_ATTACHMENT,
2969            features.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT),
2970        );
2971        flags.set(
2972            Tfc::COPY_SRC,
2973            features.intersects(vk::FormatFeatureFlags::TRANSFER_SRC),
2974        );
2975        flags.set(
2976            Tfc::COPY_DST,
2977            features.intersects(vk::FormatFeatureFlags::TRANSFER_DST),
2978        );
2979        flags.set(
2980            Tfc::STORAGE_ATOMIC,
2981            features.intersects(vk::FormatFeatureFlags::STORAGE_IMAGE_ATOMIC),
2982        );
2983        // Vulkan is very permissive about MSAA
2984        flags.set(Tfc::MULTISAMPLE_RESOLVE, !format.is_compressed());
2985
2986        // get the supported sample counts
2987        let format_aspect = crate::FormatAspects::from(format);
2988        let limits = self.phd_capabilities.properties.limits;
2989
2990        let sample_flags = if format_aspect.contains(crate::FormatAspects::DEPTH) {
2991            limits
2992                .framebuffer_depth_sample_counts
2993                .min(limits.sampled_image_depth_sample_counts)
2994        } else if format_aspect.contains(crate::FormatAspects::STENCIL) {
2995            limits
2996                .framebuffer_stencil_sample_counts
2997                .min(limits.sampled_image_stencil_sample_counts)
2998        } else {
2999            let first_aspect = format_aspect
3000                .iter()
3001                .next()
3002                .expect("All texture should at least one aspect")
3003                .map();
3004
3005            // We should never get depth or stencil out of this, due to the above.
3006            assert_ne!(first_aspect, wgt::TextureAspect::DepthOnly);
3007            assert_ne!(first_aspect, wgt::TextureAspect::StencilOnly);
3008
3009            match format.sample_type(Some(first_aspect), None).unwrap() {
3010                wgt::TextureSampleType::Float { .. } => limits
3011                    .framebuffer_color_sample_counts
3012                    .min(limits.sampled_image_color_sample_counts),
3013                wgt::TextureSampleType::Sint | wgt::TextureSampleType::Uint => {
3014                    limits.sampled_image_integer_sample_counts
3015                }
3016                _ => unreachable!(),
3017            }
3018        };
3019
3020        flags.set(
3021            Tfc::MULTISAMPLE_X2,
3022            sample_flags.contains(vk::SampleCountFlags::TYPE_2),
3023        );
3024        flags.set(
3025            Tfc::MULTISAMPLE_X4,
3026            sample_flags.contains(vk::SampleCountFlags::TYPE_4),
3027        );
3028        flags.set(
3029            Tfc::MULTISAMPLE_X8,
3030            sample_flags.contains(vk::SampleCountFlags::TYPE_8),
3031        );
3032        flags.set(
3033            Tfc::MULTISAMPLE_X16,
3034            sample_flags.contains(vk::SampleCountFlags::TYPE_16),
3035        );
3036
3037        flags
3038    }
3039
3040    unsafe fn surface_capabilities(
3041        &self,
3042        surface: &super::Surface,
3043    ) -> Option<crate::SurfaceCapabilities> {
3044        surface.inner.surface_capabilities(self)
3045    }
3046
3047    unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
3048        // VK_GOOGLE_display_timing is the only way to get presentation
3049        // timestamps on vulkan right now and it is only ever available
3050        // on android and linux. This includes mac, but there's no alternative
3051        // on mac, so this is fine.
3052        #[cfg(unix)]
3053        {
3054            let mut timespec = libc::timespec {
3055                tv_sec: 0,
3056                tv_nsec: 0,
3057            };
3058            unsafe {
3059                libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut timespec);
3060            }
3061
3062            wgt::PresentationTimestamp(
3063                timespec.tv_sec as u128 * 1_000_000_000 + timespec.tv_nsec as u128,
3064            )
3065        }
3066        #[cfg(not(unix))]
3067        {
3068            wgt::PresentationTimestamp::INVALID_TIMESTAMP
3069        }
3070    }
3071
3072    fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
3073        wgt::BufferUses::INCLUSIVE | wgt::BufferUses::MAP_WRITE
3074    }
3075
3076    // Vulkan makes very few execution ordering guarantees
3077    // see https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#synchronization-implicit
3078    // We just don't want to insert barriers between inclusive uses
3079    // See https://github.com/gfx-rs/wgpu/issues/8853
3080    fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
3081        wgt::TextureUses::INCLUSIVE
3082    }
3083}
3084
3085fn is_format_16bit_norm_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
3086    [
3087        vk::Format::R16_UNORM,
3088        vk::Format::R16_SNORM,
3089        vk::Format::R16G16_UNORM,
3090        vk::Format::R16G16_SNORM,
3091        vk::Format::R16G16B16A16_UNORM,
3092        vk::Format::R16G16B16A16_SNORM,
3093    ]
3094    .into_iter()
3095    .all(|format| {
3096        supports_format(
3097            instance,
3098            phd,
3099            format,
3100            vk::ImageTiling::OPTIMAL,
3101            vk::FormatFeatureFlags::SAMPLED_IMAGE
3102                | vk::FormatFeatureFlags::STORAGE_IMAGE
3103                | vk::FormatFeatureFlags::TRANSFER_SRC
3104                | vk::FormatFeatureFlags::TRANSFER_DST,
3105        )
3106    })
3107}
3108
3109fn is_float32_filterable_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
3110    [
3111        vk::Format::R32_SFLOAT,
3112        vk::Format::R32G32_SFLOAT,
3113        vk::Format::R32G32B32A32_SFLOAT,
3114    ]
3115    .into_iter()
3116    .all(|format| {
3117        supports_format(
3118            instance,
3119            phd,
3120            format,
3121            vk::ImageTiling::OPTIMAL,
3122            vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR,
3123        )
3124    })
3125}
3126
3127fn is_float32_blendable_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
3128    [
3129        vk::Format::R32_SFLOAT,
3130        vk::Format::R32G32_SFLOAT,
3131        vk::Format::R32G32B32A32_SFLOAT,
3132    ]
3133    .into_iter()
3134    .all(|format| {
3135        supports_format(
3136            instance,
3137            phd,
3138            format,
3139            vk::ImageTiling::OPTIMAL,
3140            vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND,
3141        )
3142    })
3143}
3144
3145fn supports_format(
3146    instance: &ash::Instance,
3147    phd: vk::PhysicalDevice,
3148    format: vk::Format,
3149    tiling: vk::ImageTiling,
3150    features: vk::FormatFeatureFlags,
3151) -> bool {
3152    let properties = unsafe { instance.get_physical_device_format_properties(phd, format) };
3153    match tiling {
3154        vk::ImageTiling::LINEAR => properties.linear_tiling_features.contains(features),
3155        vk::ImageTiling::OPTIMAL => properties.optimal_tiling_features.contains(features),
3156        _ => false,
3157    }
3158}
3159
3160fn supports_astc_3d(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool {
3161    [
3162        vk::Format::ASTC_4X4_UNORM_BLOCK,
3163        vk::Format::ASTC_4X4_SRGB_BLOCK,
3164        vk::Format::ASTC_5X4_UNORM_BLOCK,
3165        vk::Format::ASTC_5X4_SRGB_BLOCK,
3166        vk::Format::ASTC_5X5_UNORM_BLOCK,
3167        vk::Format::ASTC_5X5_SRGB_BLOCK,
3168        vk::Format::ASTC_6X5_UNORM_BLOCK,
3169        vk::Format::ASTC_6X5_SRGB_BLOCK,
3170        vk::Format::ASTC_6X6_UNORM_BLOCK,
3171        vk::Format::ASTC_6X6_SRGB_BLOCK,
3172        vk::Format::ASTC_8X5_UNORM_BLOCK,
3173        vk::Format::ASTC_8X5_SRGB_BLOCK,
3174        vk::Format::ASTC_8X6_UNORM_BLOCK,
3175        vk::Format::ASTC_8X6_SRGB_BLOCK,
3176        vk::Format::ASTC_8X8_UNORM_BLOCK,
3177        vk::Format::ASTC_8X8_SRGB_BLOCK,
3178        vk::Format::ASTC_10X5_UNORM_BLOCK,
3179        vk::Format::ASTC_10X5_SRGB_BLOCK,
3180        vk::Format::ASTC_10X6_UNORM_BLOCK,
3181        vk::Format::ASTC_10X6_SRGB_BLOCK,
3182        vk::Format::ASTC_10X8_UNORM_BLOCK,
3183        vk::Format::ASTC_10X8_SRGB_BLOCK,
3184        vk::Format::ASTC_10X10_UNORM_BLOCK,
3185        vk::Format::ASTC_10X10_SRGB_BLOCK,
3186        vk::Format::ASTC_12X10_UNORM_BLOCK,
3187        vk::Format::ASTC_12X10_SRGB_BLOCK,
3188        vk::Format::ASTC_12X12_UNORM_BLOCK,
3189        vk::Format::ASTC_12X12_SRGB_BLOCK,
3190    ]
3191    .into_iter()
3192    .all(|format| {
3193        unsafe {
3194            instance.get_physical_device_image_format_properties(
3195                phd,
3196                format,
3197                vk::ImageType::TYPE_3D,
3198                vk::ImageTiling::OPTIMAL,
3199                vk::ImageUsageFlags::SAMPLED,
3200                vk::ImageCreateFlags::empty(),
3201            )
3202        }
3203        .is_ok()
3204    })
3205}
3206
3207fn supports_bgra8unorm_storage(
3208    instance: &ash::Instance,
3209    phd: vk::PhysicalDevice,
3210    device_api_version: u32,
3211) -> bool {
3212    // See https://github.com/KhronosGroup/Vulkan-Docs/issues/2027#issuecomment-1380608011
3213
3214    // This check gates the function call and structures used below.
3215    // TODO: check for (`VK_KHR_get_physical_device_properties2` or VK1.1) and (`VK_KHR_format_feature_flags2` or VK1.3).
3216    // Right now we only check for VK1.3.
3217    if device_api_version < vk::API_VERSION_1_3 {
3218        return false;
3219    }
3220
3221    unsafe {
3222        let mut properties3 = vk::FormatProperties3::default();
3223        let mut properties2 = vk::FormatProperties2::default().push_next(&mut properties3);
3224
3225        instance.get_physical_device_format_properties2(
3226            phd,
3227            vk::Format::B8G8R8A8_UNORM,
3228            &mut properties2,
3229        );
3230
3231        let features2 = properties2.format_properties.optimal_tiling_features;
3232        let features3 = properties3.optimal_tiling_features;
3233
3234        features2.contains(vk::FormatFeatureFlags::STORAGE_IMAGE)
3235            && features3.contains(vk::FormatFeatureFlags2::STORAGE_WRITE_WITHOUT_FORMAT)
3236    }
3237}
3238
3239// For https://github.com/gfx-rs/wgpu/issues/4599
3240// Intel iGPUs with outdated drivers can break rendering if `VK_EXT_robustness2` is used.
3241// Driver version 31.0.101.2115 works, but there's probably an earlier functional version.
3242fn is_intel_igpu_outdated_for_robustness2(
3243    props: vk::PhysicalDeviceProperties,
3244    driver: Option<vk::PhysicalDeviceDriverPropertiesKHR>,
3245) -> bool {
3246    const DRIVER_VERSION_WORKING: u32 = (101 << 14) | 2115; // X.X.101.2115
3247
3248    let is_outdated = props.vendor_id == crate::auxil::db::intel::VENDOR
3249        && props.device_type == vk::PhysicalDeviceType::INTEGRATED_GPU
3250        && props.driver_version < DRIVER_VERSION_WORKING
3251        && driver
3252            .map(|driver| driver.driver_id == vk::DriverId::INTEL_PROPRIETARY_WINDOWS)
3253            .unwrap_or_default();
3254
3255    if is_outdated {
3256        log::debug!(
3257            "Disabling robustBufferAccess2 and robustImageAccess2: IntegratedGpu Intel Driver is outdated. Found with version 0x{:X}, less than the known good version 0x{:X} (31.0.101.2115)",
3258            props.driver_version,
3259            DRIVER_VERSION_WORKING
3260        );
3261    }
3262    is_outdated
3263}
3264
3265/// Convert Vulkan component type to wgt::CooperativeScalarType.
3266fn map_vk_component_type(ty: vk::ComponentTypeKHR) -> Option<wgt::CooperativeScalarType> {
3267    match ty {
3268        vk::ComponentTypeKHR::FLOAT16 => Some(wgt::CooperativeScalarType::F16),
3269        vk::ComponentTypeKHR::FLOAT32 => Some(wgt::CooperativeScalarType::F32),
3270        vk::ComponentTypeKHR::SINT32 => Some(wgt::CooperativeScalarType::I32),
3271        vk::ComponentTypeKHR::UINT32 => Some(wgt::CooperativeScalarType::U32),
3272        _ => None,
3273    }
3274}
3275
3276/// Convert Vulkan matrix size.
3277fn map_vk_cooperative_size(size: u32) -> Option<u32> {
3278    match size {
3279        8 | 16 => Some(size),
3280        _ => None,
3281    }
3282}
3283
3284/// Query all supported cooperative matrix configurations from Vulkan.
3285fn query_cooperative_matrix_properties(
3286    coop_matrix: &khr::cooperative_matrix::Instance,
3287    phd: vk::PhysicalDevice,
3288) -> Vec<wgt::CooperativeMatrixProperties> {
3289    let vk_properties =
3290        match unsafe { coop_matrix.get_physical_device_cooperative_matrix_properties(phd) } {
3291            Ok(props) => props,
3292            Err(e) => {
3293                log::warn!("Failed to query cooperative matrix properties: {e:?}");
3294                return Vec::new();
3295            }
3296        };
3297
3298    log::debug!(
3299        "Vulkan reports {} cooperative matrix configurations",
3300        vk_properties.len()
3301    );
3302
3303    let mut result = Vec::new();
3304    for prop in &vk_properties {
3305        log::debug!(
3306            "  Vulkan coop matrix: M={} N={} K={} A={:?} B={:?} C={:?} Result={:?} scope={:?} saturating={}",
3307            prop.m_size,
3308            prop.n_size,
3309            prop.k_size,
3310            prop.a_type,
3311            prop.b_type,
3312            prop.c_type,
3313            prop.result_type,
3314            prop.scope,
3315            prop.saturating_accumulation
3316        );
3317
3318        // Only include subgroup-scoped operations (the only scope we support)
3319        if prop.scope != vk::ScopeKHR::SUBGROUP {
3320            log::debug!("    Skipped: scope is not SUBGROUP");
3321            continue;
3322        }
3323
3324        // Map sizes - skip configurations with sizes we don't support
3325        let m_size = match map_vk_cooperative_size(prop.m_size) {
3326            Some(s) => s,
3327            None => {
3328                log::debug!("    Skipped: M size {} not supported", prop.m_size);
3329                continue;
3330            }
3331        };
3332        let n_size = match map_vk_cooperative_size(prop.n_size) {
3333            Some(s) => s,
3334            None => {
3335                log::debug!("    Skipped: N size {} not supported", prop.n_size);
3336                continue;
3337            }
3338        };
3339        let k_size = match map_vk_cooperative_size(prop.k_size) {
3340            Some(s) => s,
3341            None => {
3342                log::debug!("    Skipped: K size {} not supported", prop.k_size);
3343                continue;
3344            }
3345        };
3346
3347        // Map the component types - A and B must match, C and Result must match
3348        let ab_type = match map_vk_component_type(prop.a_type) {
3349            Some(t) if Some(t) == map_vk_component_type(prop.b_type) => t,
3350            _ => {
3351                log::debug!(
3352                    "    Skipped: A/B types {:?}/{:?} not supported or don't match",
3353                    prop.a_type,
3354                    prop.b_type
3355                );
3356                continue;
3357            }
3358        };
3359        let cr_type = match map_vk_component_type(prop.c_type) {
3360            Some(t) if Some(t) == map_vk_component_type(prop.result_type) => t,
3361            _ => {
3362                log::debug!(
3363                    "    Skipped: C/Result types {:?}/{:?} not supported or don't match",
3364                    prop.c_type,
3365                    prop.result_type
3366                );
3367                continue;
3368            }
3369        };
3370
3371        log::debug!("    Accepted!");
3372        result.push(wgt::CooperativeMatrixProperties {
3373            m_size,
3374            n_size,
3375            k_size,
3376            ab_type,
3377            cr_type,
3378            saturating_accumulation: prop.saturating_accumulation != 0,
3379        });
3380    }
3381
3382    log::info!(
3383        "Found {} cooperative matrix configurations supported by wgpu",
3384        result.len()
3385    );
3386    result
3387}