Skip to main content

wgpu_hal/vulkan/
adapter.rs

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