wgpu_hal/vulkan/
adapter.rs

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