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