const INDEXING_FEATURES: wgt::Features = wgt::Features::TEXTURE_BINDING_ARRAY
.union(wgt::Features::BUFFER_BINDING_ARRAY)
.union(wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY)
.union(wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING)
.union(wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
.union(wgt::Features::UNIFORM_BUFFER_BINDING_ARRAYS)
.union(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY); #[expect(rustdoc::private_intra_doc_links)] /// Features supported by a [`vk::PhysicalDevice`] and its extensions. /// /// This is used in two phases: /// /// - When enumerating adapters, this represents the features offered by the /// adapter. [`Instance::expose_adapter`] calls `vkGetPhysicalDeviceFeatures2` /// (or `vkGetPhysicalDeviceFeatures` if that is not available) to collect /// this information about the `VkPhysicalDevice` represented by the /// `wgpu_hal::ExposedAdapter`. /// /// - When opening a device, this represents the features we would like to /// enable. At `wgpu_hal::Device` construction time, /// [`PhysicalDeviceFeatures::from_extensions_and_requested_features`] /// constructs an value of this type indicating which Vulkan features to /// enable, based on the `wgpu_types::Features` requested. /// /// [`Instance::expose_adapter`]: super::Instance::expose_adapter #[derive(Debug, Default)] pubstruct PhysicalDeviceFeatures { /// Basic Vulkan 1.0 features.
core: vk::PhysicalDeviceFeatures,
/// Features provided by `VK_EXT_descriptor_indexing`, promoted to Vulkan 1.2. pub(super) descriptor_indexing:
Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT<'static>>,
/// Features provided by `VK_KHR_timeline_semaphore`, promoted to Vulkan 1.2
timeline_semaphore: Option<vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR<'static>>,
/// Features provided by `VK_EXT_image_robustness`, promoted to Vulkan 1.3
image_robustness: Option<vk::PhysicalDeviceImageRobustnessFeaturesEXT<'static>>,
/// Features provided by `VK_EXT_robustness2`.
robustness2: Option<vk::PhysicalDeviceRobustness2FeaturesEXT<'static>>,
/// Features provided by `VK_KHR_multiview`, promoted to Vulkan 1.1.
multiview: Option<vk::PhysicalDeviceMultiviewFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_sampler_ycbcr_conversion`, promoted to Vulkan 1.1.
sampler_ycbcr_conversion: Option<vk::PhysicalDeviceSamplerYcbcrConversionFeatures<'static>>,
/// Features provided by `VK_EXT_texture_compression_astc_hdr`, promoted to Vulkan 1.3.
astc_hdr: Option<vk::PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT<'static>>,
/// Features provided by `VK_KHR_shader_float16_int8`, promoted to Vulkan 1.2
shader_float16_int8: Option<vk::PhysicalDeviceShaderFloat16Int8Features<'static>>,
/// Features provided by `VK_KHR_16bit_storage`, promoted to Vulkan 1.1
_16bit_storage: Option<vk::PhysicalDevice16BitStorageFeatures<'static>>,
/// Features provided by `VK_KHR_acceleration_structure`.
acceleration_structure: Option<vk::PhysicalDeviceAccelerationStructureFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_buffer_device_address`, promoted to Vulkan 1.2. /// /// We only use this feature for /// [`Features::EXPERIMENTAL_RAY_QUERY`], which requires /// `VK_KHR_acceleration_structure`, which depends on /// `VK_KHR_buffer_device_address`, so [`Instance::expose_adapter`] only /// bothers to check if `VK_KHR_acceleration_structure` is available, /// leaving this `None`. /// /// However, we do populate this when creating a device if /// [`Features::EXPERIMENTAL_RAY_QUERY`] is requested. /// /// [`Instance::expose_adapter`]: super::Instance::expose_adapter /// [`Features::EXPERIMENTAL_RAY_QUERY`]: wgt::Features::EXPERIMENTAL_RAY_QUERY
buffer_device_address: Option<vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_ray_query`, /// /// Vulkan requires that the feature be present if the `VK_KHR_ray_query` /// extension is present, so [`Instance::expose_adapter`] doesn't bother retrieving /// this from `vkGetPhysicalDeviceFeatures2`. /// /// However, we do populate this when creating a device if ray tracing is requested. /// /// [`Instance::expose_adapter`]: super::Instance::expose_adapter
ray_query: Option<vk::PhysicalDeviceRayQueryFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_zero_initialize_workgroup_memory`, promoted /// to Vulkan 1.3.
zero_initialize_workgroup_memory:
Option<vk::PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures<'static>>,
position_fetch: Option<vk::PhysicalDeviceRayTracingPositionFetchFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_shader_atomic_int64`, promoted to Vulkan 1.2.
shader_atomic_int64: Option<vk::PhysicalDeviceShaderAtomicInt64Features<'static>>,
/// Features provided by `VK_EXT_shader_image_atomic_int64`
shader_image_atomic_int64: Option<vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT<'static>>,
/// Features provided by `VK_EXT_shader_atomic_float`.
shader_atomic_float: Option<vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT<'static>>,
/// Features provided by `VK_EXT_subgroup_size_control`, promoted to Vulkan 1.3.
subgroup_size_control: Option<vk::PhysicalDeviceSubgroupSizeControlFeatures<'static>>,
/// Features provided by `VK_KHR_maintenance4`, promoted to Vulkan 1.3.
maintenance4: Option<vk::PhysicalDeviceMaintenance4FeaturesKHR<'static>>,
/// Features proved by `VK_EXT_mesh_shader`
mesh_shader: Option<vk::PhysicalDeviceMeshShaderFeaturesEXT<'static>>,
/// Features provided by `VK_KHR_shader_integer_dot_product`, promoted to Vulkan 1.3.
shader_integer_dot_product:
Option<vk::PhysicalDeviceShaderIntegerDotProductFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_fragment_shader_barycentric`
shader_barycentrics: Option<vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_portability_subset`. /// /// Strictly speaking this tells us what features we *don't* have compared to core.
portability_subset: Option<vk::PhysicalDevicePortabilitySubsetFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_cooperative_matrix`
cooperative_matrix: Option<vk::PhysicalDeviceCooperativeMatrixFeaturesKHR<'static>>,
/// Features provided by `VK_KHR_vulkan_memory_model`, promoted to Vulkan 1.2
vulkan_memory_model: Option<vk::PhysicalDeviceVulkanMemoryModelFeaturesKHR<'static>>,
/// Create a `PhysicalDeviceFeatures` that can be used to create a logical /// device. /// /// Return a `PhysicalDeviceFeatures` value capturing all the Vulkan /// features needed for the given [`Features`], [`DownlevelFlags`], and /// [`PrivateCapabilities`]. You can use the returned value's /// [`add_to_device_create`] method to configure a /// [`vk::DeviceCreateInfo`] to build a logical device providing those /// features. /// /// To ensure that the returned value is able to select all the Vulkan /// features needed to express `requested_features`, `downlevel_flags`, and /// `private_caps`: /// /// - The given `enabled_extensions` set must include all the extensions /// selected by [`Adapter::required_device_extensions`] when passed /// `features`. /// /// - The given `device_api_version` must be the Vulkan API version of the /// physical device we will use to create the logical device. /// /// [`Features`]: wgt::Features /// [`DownlevelFlags`]: wgt::DownlevelFlags /// [`PrivateCapabilities`]: super::PrivateCapabilities /// [`add_to_device_create`]: PhysicalDeviceFeatures::add_to_device_create /// [`Adapter::required_device_extensions`]: super::Adapter::required_device_extensions fn from_extensions_and_requested_features(
phd_capabilities: &PhysicalDeviceProperties,
phd_features: &PhysicalDeviceFeatures,
enabled_extensions: &[&'static CStr],
requested_features: wgt::Features,
downlevel_flags: wgt::DownlevelFlags,
private_caps: &super::PrivateCapabilities,
) -> Self { let device_api_version = phd_capabilities.device_api_version; let needs_bindless = requested_features.intersects(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
); let needs_partially_bound =
requested_features.intersects(wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY);
/// Compute the wgpu [`Features`] and [`DownlevelFlags`] supported by a physical device. /// /// Given `self`, together with the instance and physical device it was /// built from, and a `caps` also built from those, determine which wgpu /// features and downlevel flags the device can support. /// /// [`Features`]: wgt::Features /// [`DownlevelFlags`]: wgt::DownlevelFlags fn to_wgpu(
&self,
instance: &ash::Instance,
phd: vk::PhysicalDevice,
caps: &PhysicalDeviceProperties,
queue_props: &vk::QueueFamilyProperties,
) -> (wgt::Features, wgt::DownlevelFlags) { use wgt::{DownlevelFlags as Df, Features as F}; letmut features = F::empty()
| F::MAPPABLE_PRIMARY_BUFFERS
| F::IMMEDIATES
| F::ADDRESS_MODE_CLAMP_TO_BORDER
| F::ADDRESS_MODE_CLAMP_TO_ZERO
| F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| F::CLEAR_TEXTURE
| F::PIPELINE_CACHE
| F::SHADER_EARLY_DEPTH_TEST
| F::TEXTURE_ATOMIC
| F::PASSTHROUGH_SHADERS
| F::MEMORY_DECORATION_COHERENT
| F::MEMORY_DECORATION_VOLATILE;
iflet Some(ref descriptor_indexing) = self.descriptor_indexing { // We use update-after-bind descriptors for all bind groups containing binding arrays. // // In those bind groups, we allow all binding types except uniform buffers to be present. // // As we can only switch between update-after-bind and not on a per bind group basis, // all supported binding types need to be able to be marked update after bind. // // As such, we enable all features as a whole, rather individually. let supports_descriptor_indexing = // Sampled Images
descriptor_indexing.shader_sampled_image_array_non_uniform_indexing != 0
&& descriptor_indexing.descriptor_binding_sampled_image_update_after_bind != 0 // Storage Images
&& descriptor_indexing.shader_storage_image_array_non_uniform_indexing != 0
&& descriptor_indexing.descriptor_binding_storage_image_update_after_bind != 0 // Storage Buffers
&& descriptor_indexing.shader_storage_buffer_array_non_uniform_indexing != 0
&& descriptor_indexing.descriptor_binding_storage_buffer_update_after_bind != 0;
iflet (Some(ref f16_i8), Some(ref bit16)) = (self.shader_float16_int8, self._16bit_storage)
{ // Note `storage_input_output16` is not required, we polyfill `f16` I/O using `f32` // types when this capability is not available
features.set(
F::SHADER_F16,
f16_i8.shader_float16 != 0
&& bit16.storage_buffer16_bit_access != 0
&& bit16.uniform_and_storage_buffer16_bit_access != 0,
);
}
let supports_depth_format = |format| {
supports_format(
instance,
phd,
format,
vk::ImageTiling::OPTIMAL,
depth_stencil_required_flags(),
)
};
let texture_s8 = supports_depth_format(vk::Format::S8_UINT); let texture_d32 = supports_depth_format(vk::Format::D32_SFLOAT); let texture_d24_s8 = supports_depth_format(vk::Format::D24_UNORM_S8_UINT); let texture_d32_s8 = supports_depth_format(vk::Format::D32_SFLOAT_S8_UINT);
let stencil8 = texture_s8 || texture_d24_s8; let depth24_plus_stencil8 = texture_d24_s8 || texture_d32_s8;
let supports_acceleration_structures = caps
.supports_extension(khr::deferred_host_operations::NAME)
&& caps.supports_extension(khr::acceleration_structure::NAME)
&& caps.supports_extension(khr::buffer_device_address::NAME);
let supports_ray_query =
supports_acceleration_structures && caps.supports_extension(khr::ray_query::NAME); let supports_acceleration_structure_binding_array = supports_ray_query
&& self
.acceleration_structure
.as_ref()
.is_some_and(|features| {
features.descriptor_binding_acceleration_structure_update_after_bind != 0
});
features.set(
F::EXPERIMENTAL_RAY_QUERY // Although this doesn't really require ray queries, it does not make sense to be enabled if acceleration structures // aren't enabled.
| F::EXTENDED_ACCELERATION_STRUCTURE_VERTEX_FORMATS,
supports_ray_query,
);
// Binding arrays of TLAS are supported on Vulkan when ray queries are supported. // // Note: this flag is used for shader-side `binding_array<acceleration_structure>` as well as // allowing `BindGroupLayoutEntry::count = Some(...)` for `BindingType::AccelerationStructure`.
features.set(
F::ACCELERATION_STRUCTURE_BINDING_ARRAY,
supports_acceleration_structure_binding_array,
);
// Not supported by default by `VK_KHR_portability_subset`, which we use on apple platforms.
features.set(
F::MULTISAMPLE_ARRAY, self.portability_subset
.map(|p| p.multisample_array_image == vk::TRUE)
.unwrap_or(true),
); // Enable cooperative matrix if any configuration is supported
features.set(
F::EXPERIMENTAL_COOPERATIVE_MATRIX,
!caps.cooperative_matrix_properties.is_empty(),
);
/// Vulkan "properties" structures gathered about a physical device. /// /// This structure holds the properties of a [`vk::PhysicalDevice`]: /// - the standard Vulkan device properties /// - the `VkExtensionProperties` structs for all available extensions, and /// - the per-extension properties structures for the available extensions that /// `wgpu` cares about. /// /// Generally, if you get it from any of these functions, it's stored /// here: /// - `vkEnumerateDeviceExtensionProperties` /// - `vkGetPhysicalDeviceProperties` /// - `vkGetPhysicalDeviceProperties2` /// /// This also includes a copy of the device API version, since we can /// use that as a shortcut for searching for an extension, if the /// extension has been promoted to core in the current version. /// /// This does not include device features; for those, see /// [`PhysicalDeviceFeatures`]. #[derive(Default, Debug)] pubstruct PhysicalDeviceProperties { /// Extensions supported by the `vk::PhysicalDevice`, /// as returned by `vkEnumerateDeviceExtensionProperties`.
supported_extensions: Vec<vk::ExtensionProperties>,
/// Properties of the `vk::PhysicalDevice`, as returned by /// `vkGetPhysicalDeviceProperties`.
properties: vk::PhysicalDeviceProperties,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_KHR_maintenance3` extension, promoted to Vulkan 1.1.
maintenance_3: Option<vk::PhysicalDeviceMaintenance3Properties<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_KHR_maintenance4` extension, promoted to Vulkan 1.3.
maintenance_4: Option<vk::PhysicalDeviceMaintenance4Properties<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_EXT_descriptor_indexing` extension, promoted to Vulkan 1.2.
descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingPropertiesEXT<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_KHR_acceleration_structure` extension.
acceleration_structure: Option<vk::PhysicalDeviceAccelerationStructurePropertiesKHR<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_KHR_driver_properties` extension, promoted to Vulkan 1.2.
driver: Option<vk::PhysicalDeviceDriverPropertiesKHR<'static>>,
/// Additional `vk::PhysicalDevice` properties from Vulkan 1.1.
subgroup: Option<vk::PhysicalDeviceSubgroupProperties<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_EXT_subgroup_size_control` extension, promoted to Vulkan 1.3.
subgroup_size_control: Option<vk::PhysicalDeviceSubgroupSizeControlProperties<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_EXT_robustness2` extension.
robustness2: Option<vk::PhysicalDeviceRobustness2PropertiesEXT<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_EXT_mesh_shader` extension.
mesh_shader: Option<vk::PhysicalDeviceMeshShaderPropertiesEXT<'static>>,
/// Additional `vk::PhysicalDevice` properties from the /// `VK_KHR_multiview` extension.
multiview: Option<vk::PhysicalDeviceMultiviewPropertiesKHR<'static>>,
/// The device API version. /// /// Which is the version of Vulkan supported for device-level functionality. /// /// It is associated with a `VkPhysicalDevice` and its children.
device_api_version: u32,
/// Supported cooperative matrix configurations. /// /// This is determined by querying `vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR`.
cooperative_matrix_properties: Vec<wgt::CooperativeMatrixProperties>,
}
/// Map `requested_features` to the list of Vulkan extension strings required to create the logical device. fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> { letmut extensions = Vec::new();
// Note that quite a few extensions depend on the `VK_KHR_get_physical_device_properties2` instance extension. // We enable `VK_KHR_get_physical_device_properties2` unconditionally (if available).
// Require `VK_KHR_multiview` if the associated feature was requested if requested_features.contains(wgt::Features::MULTIVIEW) {
extensions.push(khr::multiview::NAME);
}
// Require `VK_KHR_sampler_ycbcr_conversion` if the associated feature was requested if requested_features.contains(wgt::Features::TEXTURE_FORMAT_NV12) {
extensions.push(khr::sampler_ycbcr_conversion::NAME);
}
// Require `VK_KHR_16bit_storage` if `SHADER_F16` or `SHADER_I16` was requested if requested_features.intersects(wgt::Features::SHADER_F16 | wgt::Features::SHADER_I16)
{ // - Feature `SHADER_F16` also requires `VK_KHR_shader_float16_int8`, but we always // require that anyway (if it is available) below. // - `VK_KHR_16bit_storage` requires `VK_KHR_storage_buffer_storage_class`, however // we require that one already.
extensions.push(khr::_16bit_storage::NAME);
}
if requested_features.contains(wgt::Features::SHADER_DRAW_INDEX) {
extensions.push(khr::shader_draw_parameters::NAME);
}
}
// Require `VK_EXT_descriptor_indexing` if one of the associated features was requested if requested_features.intersects(INDEXING_FEATURES) {
extensions.push(ext::descriptor_indexing::NAME);
}
// Always require `VK_KHR_shader_float16_int8` if available as it enables // Int8 optimizations. Also require it even if it's not available but // requested so that we get a corresponding error message. if requested_features.contains(wgt::Features::SHADER_F16)
|| self.supports_extension(khr::shader_float16_int8::NAME)
{
extensions.push(khr::shader_float16_int8::NAME);
}
if requested_features.intersects(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
extensions.push(khr::spirv_1_4::NAME);
}
// Require `VK_EXT_subgroup_size_control` if the associated feature was requested if requested_features.contains(wgt::Features::SUBGROUP) {
extensions.push(ext::subgroup_size_control::NAME);
}
// Optional `VK_EXT_memory_budget` ifself.supports_extension(ext::memory_budget::NAME) {
extensions.push(ext::memory_budget::NAME);
} else {
log::debug!("VK_EXT_memory_budget is not available.")
}
// Require `VK_KHR_draw_indirect_count` if the associated feature was requested // Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid // large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features. if requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT) {
extensions.push(khr::draw_indirect_count::NAME);
}
// Require `VK_KHR_deferred_host_operations`, `VK_KHR_acceleration_structure` `VK_KHR_buffer_device_address` (for acceleration structures) and`VK_KHR_ray_query` if `EXPERIMENTAL_RAY_QUERY` was requested if requested_features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
extensions.push(khr::deferred_host_operations::NAME);
extensions.push(khr::acceleration_structure::NAME);
extensions.push(khr::buffer_device_address::NAME);
extensions.push(khr::ray_query::NAME);
}
if requested_features.contains(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN) {
extensions.push(khr::ray_tracing_position_fetch::NAME)
}
// Require `VK_EXT_conservative_rasterization` if the associated feature was requested if requested_features.contains(wgt::Features::CONSERVATIVE_RASTERIZATION) {
extensions.push(ext::conservative_rasterization::NAME);
}
// Require `VK_KHR_portability_subset` on macOS/iOS #[cfg(target_vendor = "apple")]
extensions.push(khr::portability_subset::NAME);
// Require `VK_EXT_texture_compression_astc_hdr` if the associated feature was requested if requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
extensions.push(ext::texture_compression_astc_hdr::NAME);
}
// Require `VK_KHR_shader_atomic_int64` if the associated feature was requested if requested_features.intersects(
wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS | wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
) {
extensions.push(khr::shader_atomic_int64::NAME);
}
// Require `VK_EXT_shader_image_atomic_int64` if the associated feature was requested if requested_features.intersects(wgt::Features::TEXTURE_INT64_ATOMIC) {
extensions.push(ext::shader_image_atomic_int64::NAME);
}
// Require `VK_EXT_shader_atomic_float` if the associated feature was requested if requested_features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC) {
extensions.push(ext::shader_atomic_float::NAME);
}
// Require VK_GOOGLE_display_timing if the associated feature was requested if requested_features.contains(wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING) {
extensions.push(google::display_timing::NAME);
}
if requested_features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
extensions.push(ext::mesh_shader::NAME);
}
// Require `VK_KHR_fragment_shader_barycentric` if an associated feature was requested // Vulkan bundles both barycentrics and per-vertex attributes under the same feature. if requested_features
.intersects(wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX)
{
extensions.push(khr::fragment_shader_barycentric::NAME);
}
// Require `VK_KHR_cooperative_matrix` if the associated feature was requested if requested_features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
extensions.push(khr::cooperative_matrix::NAME);
}
extensions
}
fn to_wgpu_limits(&self) -> wgt::Limits { let limits = &self.properties.limits;
let max_memory_allocation_size = self
.maintenance_3
.map(|maintenance_3| maintenance_3.max_memory_allocation_size)
.unwrap_or(u64::MAX); let max_buffer_size = self
.maintenance_4
.map(|maintenance_4| maintenance_4.max_buffer_size)
.unwrap_or(u64::MAX); let max_buffer_size = max_buffer_size.min(max_memory_allocation_size);
// Prevent very large buffers on mesa and most android devices, and in all cases // don't risk confusing JS by exceeding the range of a double. let is_nvidia = self.properties.vendor_id == crate::auxil::db::nvidia::VENDOR; let max_buffer_size_cap = if (cfg!(target_os = "linux") || cfg!(target_os = "android")) && !is_nvidia {
i32::MAX as u64
} else { 1u64 << 52
};
let max_buffer_size = max_buffer_size.min(max_buffer_size_cap);
// When summed, the 6 limits below must be under Vulkan's // maxPerSetDescriptors / MAX_SHADER_STAGES_PER_PIPELINE. // // - maxUniformBuffersPerShaderStage, WebGPU default: 12 // - maxSampledTexturesPerShaderStage, WebGPU default: 16 // - maxStorageTexturesPerShaderStage, WebGPU default: 4 // - maxStorageBuffersPerShaderStage, WebGPU default: 8 // - maxSamplersPerShaderStage, WebGPU default: 16 // - maxAccelerationStructuresPerShaderStage, Native only // // Note: All Vulkan's descriptor types count towards maxPerSetDescriptors but // we don't use all of them. // See https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#interfaces-resources-limits let max_per_set_descriptors = self
.maintenance_3
.map(|maintenance_3| maintenance_3.max_per_set_descriptors) // The lowest value seen in reports is 312, use 256 as a safe default. // https://vulkan.gpuinfo.org/displayextensionproperty.php?extensionname=VK_KHR_maintenance3&extensionproperty=maxPerSetDescriptors&platform=all // https://vulkan.gpuinfo.org/displaycoreproperty.php?core=1.1&name=maxPerSetDescriptors&platform=all
.unwrap_or(256);
// Use max(default, maxPerSetDescriptors) since the spec requires this // limit to be at least 1000. This is ok because we already lowered // all the other relevant per stage limits so their sum is lower // than maxPerSetDescriptors. let max_bindings_per_bind_group = 1000.max(max_per_set_descriptors);
// TODO: programmatically determine this, if possible. It's unclear whether we can // as of https://github.com/gpuweb/gpuweb/issues/2965#issuecomment-1361315447. // // In theory some tilers may not support this much. We can't tell however, and // the driver will throw a DEVICE_REMOVED if it goes too high in usage. This is fine. let max_color_attachment_bytes_per_sample =
max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
let max_multiview_view_count = self
.multiview
.map(|a| a.max_multiview_view_count.min(32))
.unwrap_or(0);
/// Return a `wgpu_hal::Alignments` structure describing this adapter. /// /// The `using_robustness2` argument says how this adapter will implement /// `wgpu_hal`'s guarantee that shaders can only read the [accessible /// region][ar] of bindgroup's buffer bindings: /// /// - If this adapter will depend on `VK_EXT_robustness2`'s /// `robustBufferAccess2` feature to apply bounds checks to shader buffer /// access, `using_robustness2` must be `true`. /// /// - Otherwise, this adapter must use Naga to inject bounds checks on /// buffer accesses, and `using_robustness2` must be `false`. /// /// [ar]: ../../struct.BufferBinding.html#accessible-region fn to_hal_alignments(&self, using_robustness2: bool) -> crate::Alignments { let limits = &self.properties.limits; crate::Alignments {
buffer_copy_offset: wgt::BufferSize::new(limits.optimal_buffer_copy_offset_alignment)
.unwrap(),
buffer_copy_pitch: wgt::BufferSize::new(limits.optimal_buffer_copy_row_pitch_alignment)
.unwrap(),
uniform_bounds_check_alignment: { let alignment = if using_robustness2 { self.robustness2
.unwrap() // if we're using it, we should have its properties
.robust_uniform_buffer_access_size_alignment
} else { // If the `robustness2` properties are unavailable, then `robustness2` is not available either Naga-injected bounds checks are precise. 1
};
wgt::BufferSize::new(alignment).unwrap()
},
raw_tlas_instance_size: 64,
ray_tracing_scratch_buffer_alignment: self.acceleration_structure.map_or( 0,
|acceleration_structure| {
acceleration_structure.min_acceleration_structure_scratch_offset_alignment
},
),
}
}
}
let supports_multiview = capabilities.device_api_version >= vk::API_VERSION_1_1
|| capabilities.supports_extension(khr::multiview::NAME);
iflet Some(ref get_device_properties) = self.get_physical_device_properties { // Get these now to avoid borrowing conflicts later let supports_maintenance3 = capabilities.device_api_version >= vk::API_VERSION_1_1
|| capabilities.supports_extension(khr::maintenance3::NAME); let supports_maintenance4 = capabilities.device_api_version >= vk::API_VERSION_1_3
|| capabilities.supports_extension(khr::maintenance4::NAME); let supports_descriptor_indexing = capabilities.device_api_version
>= vk::API_VERSION_1_2
|| capabilities.supports_extension(ext::descriptor_indexing::NAME); let supports_driver_properties = capabilities.device_api_version
>= vk::API_VERSION_1_2
|| capabilities.supports_extension(khr::driver_properties::NAME); let supports_subgroup_size_control = capabilities.device_api_version
>= vk::API_VERSION_1_3
|| capabilities.supports_extension(ext::subgroup_size_control::NAME); let supports_robustness2 = capabilities.supports_extension(ext::robustness2::NAME); let supports_pci_bus_info =
capabilities.supports_extension(ext::pci_bus_info::NAME);
let supports_acceleration_structure =
capabilities.supports_extension(khr::acceleration_structure::NAME);
let supports_mesh_shader = capabilities.supports_extension(ext::mesh_shader::NAME);
letmut properties2 = vk::PhysicalDeviceProperties2KHR::default(); if supports_maintenance3 { let next = capabilities
.maintenance_3
.insert(vk::PhysicalDeviceMaintenance3Properties::default());
properties2 = properties2.push_next(next);
}
if supports_maintenance4 { let next = capabilities
.maintenance_4
.insert(vk::PhysicalDeviceMaintenance4Properties::default());
properties2 = properties2.push_next(next);
}
if supports_descriptor_indexing { let next = capabilities
.descriptor_indexing
.insert(vk::PhysicalDeviceDescriptorIndexingPropertiesEXT::default());
properties2 = properties2.push_next(next);
}
if supports_acceleration_structure { let next = capabilities
.acceleration_structure
.insert(vk::PhysicalDeviceAccelerationStructurePropertiesKHR::default());
properties2 = properties2.push_next(next);
}
if supports_driver_properties { let next = capabilities
.driver
.insert(vk::PhysicalDeviceDriverPropertiesKHR::default());
properties2 = properties2.push_next(next);
}
if capabilities.device_api_version >= vk::API_VERSION_1_1 { let next = capabilities
.subgroup
.insert(vk::PhysicalDeviceSubgroupProperties::default());
properties2 = properties2.push_next(next);
}
if supports_subgroup_size_control { let next = capabilities
.subgroup_size_control
.insert(vk::PhysicalDeviceSubgroupSizeControlProperties::default());
properties2 = properties2.push_next(next);
}
if supports_robustness2 { let next = capabilities
.robustness2
.insert(vk::PhysicalDeviceRobustness2PropertiesEXT::default());
properties2 = properties2.push_next(next);
}
if supports_pci_bus_info { let next = capabilities
.pci_bus_info
.insert(vk::PhysicalDevicePCIBusInfoPropertiesEXT::default());
properties2 = properties2.push_next(next);
}
if supports_mesh_shader { let next = capabilities
.mesh_shader
.insert(vk::PhysicalDeviceMeshShaderPropertiesEXT::default());
properties2 = properties2.push_next(next);
}
if supports_multiview { let next = capabilities
.multiview
.insert(vk::PhysicalDeviceMultiviewProperties::default());
properties2 = properties2.push_next(next);
}
letmut features = PhysicalDeviceFeatures::default();
features.core = iflet Some(ref get_device_properties) = self.get_physical_device_properties
{ let core = vk::PhysicalDeviceFeatures::default(); letmut features2 = vk::PhysicalDeviceFeatures2KHR::default().features(core);
// `VK_KHR_multiview` is promoted to 1.1 if capabilities.device_api_version >= vk::API_VERSION_1_1
|| capabilities.supports_extension(khr::multiview::NAME)
{ let next = features
.multiview
.insert(vk::PhysicalDeviceMultiviewFeatures::default());
features2 = features2.push_next(next);
}
// `VK_KHR_sampler_ycbcr_conversion` is promoted to 1.1 if capabilities.device_api_version >= vk::API_VERSION_1_1
|| capabilities.supports_extension(khr::sampler_ycbcr_conversion::NAME)
{ let next = features
.sampler_ycbcr_conversion
.insert(vk::PhysicalDeviceSamplerYcbcrConversionFeatures::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(ext::descriptor_indexing::NAME) { let next = features
.descriptor_indexing
.insert(vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::default());
features2 = features2.push_next(next);
}
// `VK_KHR_timeline_semaphore` is promoted to 1.2, but has no // changes, so we can keep using the extension unconditionally. if capabilities.supports_extension(khr::timeline_semaphore::NAME) { let next = features
.timeline_semaphore
.insert(vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR::default());
features2 = features2.push_next(next);
}
// `VK_KHR_shader_atomic_int64` is promoted to 1.2, but has no // changes, so we can keep using the extension unconditionally. if capabilities.device_api_version >= vk::API_VERSION_1_2
|| capabilities.supports_extension(khr::shader_atomic_int64::NAME)
{ let next = features
.shader_atomic_int64
.insert(vk::PhysicalDeviceShaderAtomicInt64Features::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(ext::shader_image_atomic_int64::NAME) { let next = features
.shader_image_atomic_int64
.insert(vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT::default());
features2 = features2.push_next(next);
} if capabilities.supports_extension(ext::shader_atomic_float::NAME) { let next = features
.shader_atomic_float
.insert(vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT::default());
features2 = features2.push_next(next);
} if capabilities.supports_extension(ext::image_robustness::NAME) { let next = features
.image_robustness
.insert(vk::PhysicalDeviceImageRobustnessFeaturesEXT::default());
features2 = features2.push_next(next);
} if capabilities.supports_extension(ext::robustness2::NAME) { let next = features
.robustness2
.insert(vk::PhysicalDeviceRobustness2FeaturesEXT::default());
features2 = features2.push_next(next);
} if capabilities.supports_extension(ext::texture_compression_astc_hdr::NAME) { let next = features
.astc_hdr
.insert(vk::PhysicalDeviceTextureCompressionASTCHDRFeaturesEXT::default());
features2 = features2.push_next(next);
}
// `VK_KHR_shader_float16_int8` is promoted to 1.2 if capabilities.device_api_version >= vk::API_VERSION_1_2
|| capabilities.supports_extension(khr::shader_float16_int8::NAME)
{ let next = features
.shader_float16_int8
.insert(vk::PhysicalDeviceShaderFloat16Int8FeaturesKHR::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(khr::_16bit_storage::NAME) { let next = features
._16bit_storage
.insert(vk::PhysicalDevice16BitStorageFeaturesKHR::default());
features2 = features2.push_next(next);
} if capabilities.supports_extension(khr::acceleration_structure::NAME) { let next = features
.acceleration_structure
.insert(vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(khr::ray_tracing_position_fetch::NAME) { let next = features
.position_fetch
.insert(vk::PhysicalDeviceRayTracingPositionFetchFeaturesKHR::default());
features2 = features2.push_next(next);
}
// `VK_KHR_maintenance4` is promoted to 1.3 if capabilities.device_api_version >= vk::API_VERSION_1_3
|| capabilities.supports_extension(khr::maintenance4::NAME)
{ let next = features
.maintenance4
.insert(vk::PhysicalDeviceMaintenance4Features::default());
features2 = features2.push_next(next);
}
// `VK_KHR_zero_initialize_workgroup_memory` is promoted to 1.3 if capabilities.device_api_version >= vk::API_VERSION_1_3
|| capabilities.supports_extension(khr::zero_initialize_workgroup_memory::NAME)
{ let next = features
.zero_initialize_workgroup_memory
.insert(vk::PhysicalDeviceZeroInitializeWorkgroupMemoryFeatures::default());
features2 = features2.push_next(next);
}
// `VK_EXT_subgroup_size_control` is promoted to 1.3 if capabilities.device_api_version >= vk::API_VERSION_1_3
|| capabilities.supports_extension(ext::subgroup_size_control::NAME)
{ let next = features
.subgroup_size_control
.insert(vk::PhysicalDeviceSubgroupSizeControlFeatures::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(ext::mesh_shader::NAME) { let next = features
.mesh_shader
.insert(vk::PhysicalDeviceMeshShaderFeaturesEXT::default());
features2 = features2.push_next(next);
}
// `VK_KHR_shader_integer_dot_product` is promoted to 1.3 if capabilities.device_api_version >= vk::API_VERSION_1_3
|| capabilities.supports_extension(khr::shader_integer_dot_product::NAME)
{ let next = features
.shader_integer_dot_product
.insert(vk::PhysicalDeviceShaderIntegerDotProductFeatures::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(khr::fragment_shader_barycentric::NAME) { let next = features
.shader_barycentrics
.insert(vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(khr::portability_subset::NAME) { let next = features
.portability_subset
.insert(vk::PhysicalDevicePortabilitySubsetFeaturesKHR::default());
features2 = features2.push_next(next);
}
if capabilities.supports_extension(khr::cooperative_matrix::NAME) { let next = features
.cooperative_matrix
.insert(vk::PhysicalDeviceCooperativeMatrixFeaturesKHR::default());
features2 = features2.push_next(next);
}
if capabilities.device_api_version >= vk::API_VERSION_1_1 { let next = features
.shader_draw_parameters
.insert(vk::PhysicalDeviceShaderDrawParametersFeatures::default());
features2 = features2.push_next(next);
}
iflet Some(driver) = phd_capabilities.driver { if driver.conformance_version.major == 0 { if driver.driver_id == vk::DriverId::MOLTENVK {
log::debug!("Adapter is not Vulkan compliant, but is MoltenVK, continuing");
} elseifself
.shared
.flags
.contains(wgt::InstanceFlags::ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER)
{
log::debug!("Adapter is not Vulkan compliant: {}", info.name);
} else {
log::debug!( "Adapter is not Vulkan compliant, hiding adapter: {}",
info.name
); return None;
}
}
} if phd_capabilities.device_api_version == vk::API_VERSION_1_0
&& !phd_capabilities.supports_extension(khr::storage_buffer_storage_class::NAME)
{
log::debug!( "SPIR-V storage buffer class is not supported, hiding adapter: {}",
info.name
); return None;
} if !phd_capabilities.supports_extension(khr::maintenance1::NAME)
&& phd_capabilities.device_api_version < vk::API_VERSION_1_1
{
log::debug!( "VK_KHR_maintenance1 is not supported, hiding adapter: {}",
info.name
); return None;
}
let queue_families = unsafe { self.shared
.raw
.get_physical_device_queue_family_properties(phd)
}; let queue_family_properties = queue_families.first()?; let queue_flags = queue_family_properties.queue_flags; if !queue_flags.contains(vk::QueueFlags::GRAPHICS) {
log::debug!("The first queue only exposes {queue_flags:?}"); return None;
}
if info.driver == "llvmpipe" { // The `F16_IN_F32` instructions do not normally require native `F16` support, but on // llvmpipe, they do.
downlevel_flags.set(
wgt::DownlevelFlags::SHADER_F16_IN_F32,
available_features.contains(wgt::Features::SHADER_F16),
);
}
/// Create a `PhysicalDeviceFeatures` for opening a logical device with /// `features` from this adapter. /// /// The given `enabled_extensions` set must include all the extensions /// selected by [`required_device_extensions`] when passed `features`. /// Otherwise, the `PhysicalDeviceFeatures` value may not be able to select /// all the Vulkan features needed to represent `features` and this /// adapter's characteristics. /// /// Typically, you'd simply call `required_device_extensions`, and then pass /// its return value and the feature set you gave it directly to this /// function. But it's fine to add more extensions to the list. /// /// [`required_device_extensions`]: Self::required_device_extensions pubfn physical_device_features(
&self,
enabled_extensions: &[&'static CStr],
features: wgt::Features,
) -> PhysicalDeviceFeatures {
PhysicalDeviceFeatures::from_extensions_and_requested_features(
&self.phd_capabilities,
&self.phd_features,
enabled_extensions,
features, self.downlevel_flags,
&self.private_caps,
)
}
/// # Safety /// /// - `raw_device` must be created from this adapter. /// - `raw_device` must be created using `family_index`, `enabled_extensions` and `physical_device_features()` /// - `enabled_extensions` must be a superset of `required_device_extensions()`. /// - If `drop_callback` is [`None`], wgpu-hal will take ownership of `raw_device`. If /// `drop_callback` is [`Some`], `raw_device` must be valid until the callback is called. #[allow(clippy::too_many_arguments)] pubunsafefn device_from_raw(
&self,
raw_device: ash::Device,
drop_callback: Option<crate::DropCallback>,
enabled_extensions: &[&'static CStr],
features: wgt::Features,
limits: &wgt::Limits,
memory_hints: &wgt::MemoryHints,
family_index: u32,
queue_index: u32,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> { let mem_properties = {
profiling::scope!("vkGetPhysicalDeviceMemoryProperties"); unsafe { self.instance
.raw
.get_physical_device_memory_properties(self.raw)
}
}; let memory_types = &mem_properties.memory_types_as_slice(); let valid_ash_memory_types = memory_types.iter().enumerate().fold(0, |u, (i, mem)| { ifself.known_memory_flags.contains(mem.property_flags) {
u | (1 << i)
} else {
u
}
});
// Note that VK_EXT_debug_utils is an instance extension (enabled at the instance // level) but contains a few functions that can be loaded directly on the Device for a // dispatch-table-less pointer. let debug_utils_fn = ifself.instance.extensions.contains(&ext::debug_utils::NAME) {
Some(ext::debug_utils::Device::new(
&self.instance.raw,
&raw_device,
))
} else {
None
}; let indirect_count_fn = if enabled_extensions.contains(&khr::draw_indirect_count::NAME) {
Some(khr::draw_indirect_count::Device::new(
&self.instance.raw,
&raw_device,
))
} else {
None
}; let timeline_semaphore_fn = if enabled_extensions.contains(&khr::timeline_semaphore::NAME) {
Some(super::ExtensionFn::Extension(
khr::timeline_semaphore::Device::new(&self.instance.raw, &raw_device),
))
} elseifself.phd_capabilities.device_api_version >= vk::API_VERSION_1_2 {
Some(super::ExtensionFn::Promoted)
} else {
None
}; let ray_tracing_fns = if enabled_extensions.contains(&khr::acceleration_structure::NAME)
&& enabled_extensions.contains(&khr::buffer_device_address::NAME)
{
Some(super::RayTracingDeviceExtensionFunctions {
acceleration_structure: khr::acceleration_structure::Device::new(
&self.instance.raw,
&raw_device,
),
buffer_device_address: khr::buffer_device_address::Device::new(
&self.instance.raw,
&raw_device,
),
})
} else {
None
}; let mesh_shading_fns = if enabled_extensions.contains(&ext::mesh_shader::NAME) {
Some(ext::mesh_shader::Device::new(
&self.instance.raw,
&raw_device,
))
} else {
None
}; let external_memory_fd_fn = if enabled_extensions.contains(&khr::external_memory_fd::NAME) {
Some(khr::external_memory_fd::Device::new(
&self.instance.raw,
&raw_device,
))
} else {
None
};
let naga_options = { use naga::back::spv;
// The following capabilities are always available // see https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap52.html#spirvenv-capabilities letmut capabilities = vec![
spv::Capability::Shader,
spv::Capability::Matrix,
spv::Capability::Sampled1D,
spv::Capability::Image1D,
spv::Capability::ImageQuery,
spv::Capability::DerivativeControl,
spv::Capability::StorageImageExtendedFormats,
];
if features.intersects(wgt::Features::TEXTURE_INT64_ATOMIC) {
capabilities.push(spv::Capability::Int64ImageEXT);
}
if features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC) {
capabilities.push(spv::Capability::AtomicFloat32AddEXT);
}
if features.contains(wgt::Features::CLIP_DISTANCES) {
capabilities.push(spv::Capability::ClipDistance);
}
// Vulkan bundles both barycentrics and per-vertex attributes under the same feature. if features
.intersects(wgt::Features::SHADER_BARYCENTRICS | wgt::Features::SHADER_PER_VERTEX)
{
capabilities.push(spv::Capability::FragmentBarycentricKHR);
}
if features.contains(wgt::Features::SHADER_DRAW_INDEX) {
capabilities.push(spv::Capability::DrawParameters);
}
letmut flags = spv::WriterFlags::empty();
flags.set(
spv::WriterFlags::DEBUG, self.instance.flags.contains(wgt::InstanceFlags::DEBUG),
);
flags.set(
spv::WriterFlags::LABEL_VARYINGS, self.phd_capabilities.properties.vendor_id != crate::auxil::db::qualcomm::VENDOR,
);
flags.set(
spv::WriterFlags::FORCE_POINT_SIZE, //Note: we could technically disable this when we are compiling separate entry points, // and we know exactly that the primitive topology is not `PointList`. // But this requires cloning the `spv::Options` struct, which has heap allocations. true, // could check `super::Workarounds::SEPARATE_ENTRY_POINTS`
);
flags.set(
spv::WriterFlags::PRINT_ON_RAY_QUERY_INITIALIZATION_FAIL, self.instance.flags.contains(wgt::InstanceFlags::DEBUG)
&& (self.instance.instance_api_version >= vk::API_VERSION_1_3
|| enabled_extensions.contains(&khr::shader_non_semantic_info::NAME)),
); if features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
capabilities.push(spv::Capability::RayQueryKHR);
} if features.contains(wgt::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN) {
capabilities.push(spv::Capability::RayQueryPositionFetchKHR)
} if features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER) {
capabilities.push(spv::Capability::MeshShadingEXT);
} if features.contains(wgt::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
capabilities.push(spv::Capability::CooperativeMatrixKHR); // TODO: expose this more generally
capabilities.push(spv::Capability::VulkanMemoryModel);
} ifself.private_caps.shader_integer_dot_product { // See <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_integer_dot_product.html#_new_spir_v_capabilities>.
capabilities.extend(&[
spv::Capability::DotProductInputAllKHR,
spv::Capability::DotProductInput4x8BitKHR,
spv::Capability::DotProductInput4x8BitPackedKHR,
spv::Capability::DotProductKHR,
]);
} ifself.private_caps.shader_int8 { // See <https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceShaderFloat16Int8Features.html#extension-features-shaderInt8>.
capabilities.extend(&[spv::Capability::Int8]);
}
spv::Options {
lang_version: matchself.phd_capabilities.device_api_version { // Use maximum supported SPIR-V version according to // <https://github.com/KhronosGroup/Vulkan-Docs/blob/19b7651/appendices/spirvenv.adoc?plain=1#L21-L40>.
vk::API_VERSION_1_0..vk::API_VERSION_1_1 => (1, 0),
vk::API_VERSION_1_1..vk::API_VERSION_1_2 => (1, 3),
vk::API_VERSION_1_2..vk::API_VERSION_1_3 => (1, 5),
vk::API_VERSION_1_3.. => (1, 6),
_ => unreachable!(),
},
flags,
capabilities: Some(capabilities.iter().cloned().collect()),
bounds_check_policies: naga::proc::BoundsCheckPolicies {
index: naga::proc::BoundsCheckPolicy::Restrict,
buffer: ifself.private_caps.robust_buffer_access2 {
naga::proc::BoundsCheckPolicy::Unchecked
} else {
naga::proc::BoundsCheckPolicy::Restrict
},
image_load: ifself.private_caps.robust_image_access {
naga::proc::BoundsCheckPolicy::Unchecked
} else {
naga::proc::BoundsCheckPolicy::Restrict
}, // TODO: support bounds checks on binding arrays
binding_array: naga::proc::BoundsCheckPolicy::Unchecked,
},
zero_initialize_workgroup_memory: ifself
.private_caps
.zero_initialize_workgroup_memory
{
spv::ZeroInitializeWorkgroupMemoryMode::Native
} else {
spv::ZeroInitializeWorkgroupMemoryMode::Polyfill
},
force_loop_bounding: true,
ray_query_initialization_tracking: true,
use_storage_input_output_16: features.contains(wgt::Features::SHADER_F16)
&& self.phd_features.supports_storage_input_output_16(),
fake_missing_bindings: false, // We need to build this separately for each invocation, so just default it out here
binding_map: BTreeMap::default(),
debug_info: None,
task_dispatch_limits: Some(naga::back::TaskDispatchLimits {
max_mesh_workgroups_per_dim: limits.max_mesh_workgroups_per_dimension,
max_mesh_workgroups_total: limits.max_mesh_workgroup_total_count,
}),
mesh_shader_primitive_indices_clamp: true,
trace_ray_argument_validation: true,
}
};
/// # Safety: /// - Same as `open` plus /// - The callback may not change anything that the device does not support. /// - The callback may not remove features. pubunsafefn open_with_callback<'a>(
&self,
features: wgt::Features,
limits: &wgt::Limits,
memory_hints: &wgt::MemoryHints,
callback: Option<Box<super::CreateDeviceCallback<'a>>>,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> { letmut enabled_extensions = self.required_device_extensions(features); letmut enabled_phd_features = self.physical_device_features(&enabled_extensions, features);
let family_index = 0; //TODO let family_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(family_index)
.queue_priorities(&[1.0]); letmut family_infos = Vec::from([family_info]);
let vk_format = self.private_caps.map_texture_format(format); let properties = unsafe { self.instance
.raw
.get_physical_device_format_properties(self.raw, vk_format)
}; let features = properties.optimal_tiling_features;
// get the supported sample counts let format_aspect = crate::FormatAspects::from(format); let limits = self.phd_capabilities.properties.limits;
let sample_flags = if format_aspect.contains(crate::FormatAspects::DEPTH) {
limits
.framebuffer_depth_sample_counts
.min(limits.sampled_image_depth_sample_counts)
} elseif format_aspect.contains(crate::FormatAspects::STENCIL) {
limits
.framebuffer_stencil_sample_counts
.min(limits.sampled_image_stencil_sample_counts)
} else { let first_aspect = format_aspect
.iter()
.next()
.expect("All texture should at least one aspect")
.map();
// We should never get depth or stencil out of this, due to the above.
assert_ne!(first_aspect, wgt::TextureAspect::DepthOnly);
assert_ne!(first_aspect, wgt::TextureAspect::StencilOnly);
unsafefn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp { // VK_GOOGLE_display_timing is the only way to get presentation // timestamps on vulkan right now and it is only ever available // on android and linux. This includes mac, but there's no alternative // on mac, so this is fine. #[cfg(unix)]
{ letmut timespec = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
}; unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut timespec);
}
wgt::PresentationTimestamp(
timespec.tv_sec as u128 * 1_000_000_000 + timespec.tv_nsec as u128,
)
} #[cfg(not(unix))]
{
wgt::PresentationTimestamp::INVALID_TIMESTAMP
}
}
// This check gates the function call and structures used below. // TODO: check for (`VK_KHR_get_physical_device_properties2` or VK1.1) and (`VK_KHR_format_feature_flags2` or VK1.3). // Right now we only check for VK1.3. if device_api_version < vk::API_VERSION_1_3 { returnfalse;
}
if is_outdated {
log::debug!( "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)",
props.driver_version,
DRIVER_VERSION_WORKING
);
}
is_outdated
}
/// Convert Vulkan component type to wgt::CooperativeScalarType. fn map_vk_component_type(ty: vk::ComponentTypeKHR) -> Option<wgt::CooperativeScalarType> { match ty {
vk::ComponentTypeKHR::FLOAT16 => Some(wgt::CooperativeScalarType::F16),
vk::ComponentTypeKHR::FLOAT32 => Some(wgt::CooperativeScalarType::F32),
vk::ComponentTypeKHR::SINT32 => Some(wgt::CooperativeScalarType::I32),
vk::ComponentTypeKHR::UINT32 => Some(wgt::CooperativeScalarType::U32),
_ => None,
}
}
// Only include subgroup-scoped operations (the only scope we support) if prop.scope != vk::ScopeKHR::SUBGROUP {
log::debug!(" Skipped: scope is not SUBGROUP"); continue;
}
// Map sizes - skip configurations with sizes we don't support let m_size = match map_vk_cooperative_size(prop.m_size) {
Some(s) => s,
None => {
log::debug!(" Skipped: M size {} not supported", prop.m_size); continue;
}
}; let n_size = match map_vk_cooperative_size(prop.n_size) {
Some(s) => s,
None => {
log::debug!(" Skipped: N size {} not supported", prop.n_size); continue;
}
}; let k_size = match map_vk_cooperative_size(prop.k_size) {
Some(s) => s,
None => {
log::debug!(" Skipped: K size {} not supported", prop.k_size); continue;
}
};
// Map the component types - A and B must match, C and Result must match let ab_type = match map_vk_component_type(prop.a_type) {
Some(t) if Some(t) == map_vk_component_type(prop.b_type) => t,
_ => {
log::debug!( " Skipped: A/B types {:?}/{:?} not supported or don't match",
prop.a_type,
prop.b_type
); continue;
}
}; let cr_type = match map_vk_component_type(prop.c_type) {
Some(t) if Some(t) == map_vk_component_type(prop.result_type) => t,
_ => {
log::debug!( " Skipped: C/Result types {:?}/{:?} not supported or don't match",
prop.c_type,
prop.result_type
); continue;
}
};
log::info!( "Found {} cooperative matrix configurations supported by wgpu",
result.len()
);
result
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.81Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.