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