/// 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_imageless_framebuffer`, promoted to Vulkan 1.2.
imageless_framebuffer: Option<vk::PhysicalDeviceImagelessFramebufferFeaturesKHR<'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) and `VK_KHR_16bit_storage` (promoted to Vulkan 1.1). We use these /// features together, or not at all.
shader_float16: Option<(
vk::PhysicalDeviceShaderFloat16Int8Features<'static>,
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_TRACING_ACCELERATION_STRUCTURE`], 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_TRACING_ACCELERATION_STRUCTURE`] is requested. /// /// [`Instance::expose_adapter`]: super::Instance::expose_adapter /// [`Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE`]: wgt::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE
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>>,
/// 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_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>>,
}
impl PhysicalDeviceFeatures { /// Add the members of `self` into `info.enabled_features` and its `p_next` chain. pubfn add_to_device_create<'a>(
&'a mut self, mut info: vk::DeviceCreateInfo<'a>,
) -> vk::DeviceCreateInfo<'a> {
info = info.enabled_features(&self.core); iflet Some(refmut feature) = self.descriptor_indexing {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.imageless_framebuffer {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.timeline_semaphore {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.image_robustness {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.robustness2 {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.astc_hdr {
info = info.push_next(feature);
} iflet Some((refmut f16_i8_feature, refmut _16bit_feature)) = self.shader_float16 {
info = info.push_next(f16_i8_feature);
info = info.push_next(_16bit_feature);
} iflet Some(refmut feature) = self.zero_initialize_workgroup_memory {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.acceleration_structure {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.buffer_device_address {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.ray_query {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.shader_atomic_int64 {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.shader_atomic_float {
info = info.push_next(feature);
} iflet Some(refmut feature) = self.subgroup_size_control {
info = info.push_next(feature);
}
info
}
/// 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(
device_api_version: u32,
enabled_extensions: &[&'static CStr],
requested_features: wgt::Features,
downlevel_flags: wgt::DownlevelFlags,
private_caps: &super::PrivateCapabilities,
) -> Self { let needs_sampled_image_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
); let needs_storage_buffer_non_uniform = requested_features.contains(
wgt::Features::BUFFER_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
); let needs_uniform_buffer_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
); let needs_storage_image_non_uniform = requested_features.contains(
wgt::Features::TEXTURE_BINDING_ARRAY
| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_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,
) -> (wgt::Features, wgt::DownlevelFlags) { usecrate::auxil::db; use wgt::{DownlevelFlags as Df, Features as F}; letmut features = F::empty()
| F::SPIRV_SHADER_PASSTHROUGH
| F::MAPPABLE_PRIMARY_BUFFERS
| F::PUSH_CONSTANTS
| F::ADDRESS_MODE_CLAMP_TO_BORDER
| F::ADDRESS_MODE_CLAMP_TO_ZERO
| F::TIMESTAMP_QUERY
| F::TIMESTAMP_QUERY_INSIDE_ENCODERS
| F::TIMESTAMP_QUERY_INSIDE_PASSES
| F::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| F::CLEAR_TEXTURE
| F::PIPELINE_CACHE
| F::TEXTURE_ATOMIC;
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;
/// 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_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>>,
/// 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,
}
/// 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).
ifself.device_api_version < vk::API_VERSION_1_1 { // Require either `VK_KHR_maintenance1` or `VK_AMD_negative_viewport_height` ifself.supports_extension(khr::maintenance1::NAME) {
extensions.push(khr::maintenance1::NAME);
} else { // `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside it
extensions.push(amd::negative_viewport_height::NAME);
}
// 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);
}
}
// Optional `VK_KHR_imageless_framebuffer` ifself.supports_extension(khr::imageless_framebuffer::NAME) {
extensions.push(khr::imageless_framebuffer::NAME); // Require `VK_KHR_maintenance2` due to it being a dependency ifself.device_api_version < vk::API_VERSION_1_1 {
extensions.push(khr::maintenance2::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);
}
// Require `VK_KHR_shader_float16_int8` and `VK_KHR_16bit_storage` if the associated feature was requested if requested_features.contains(wgt::Features::SHADER_F16) {
extensions.push(khr::shader_float16_int8::NAME); // `VK_KHR_16bit_storage` requires `VK_KHR_storage_buffer_storage_class`, however we require that one already ifself.device_api_version < vk::API_VERSION_1_1 {
extensions.push(khr::_16bit_storage::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);
}
}
// 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` and `VK_KHR_buffer_device_address` if the feature `RAY_TRACING` was requested if requested_features
.contains(wgt::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE)
{
extensions.push(khr::deferred_host_operations::NAME);
extensions.push(khr::acceleration_structure::NAME);
extensions.push(khr::buffer_device_address::NAME);
}
// Require `VK_KHR_ray_query` if the associated feature was requested if requested_features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
extensions.push(khr::ray_query::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_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);
}
extensions
}
fn to_wgpu_limits(&self) -> wgt::Limits { let limits = &self.properties.limits;
let max_compute_workgroup_sizes = limits.max_compute_work_group_size; let max_compute_workgroups_per_dimension = limits.max_compute_work_group_count[0]
.min(limits.max_compute_work_group_count[1])
.min(limits.max_compute_work_group_count[2]);
// Prevent very large buffers on mesa and most android devices. let is_nvidia = self.properties.vendor_id == crate::auxil::db::nvidia::VENDOR; let max_buffer_size = if (cfg!(target_os = "linux") || cfg!(target_os = "android")) && !is_nvidia {
i32::MAX as u64
} else {
u64::MAX
};
// 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. // // 16 bytes per sample is the maximum size for a color attachment. let max_color_attachment_bytes_per_sample =
limits.max_color_attachments * wgt::TextureFormat::MAX_TARGET_PIXEL_BYTE_COST;
/// 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
},
),
}
}
}
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_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_acceleration_structure =
capabilities.supports_extension(khr::acceleration_structure::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_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);
}
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_imageless_framebuffer` is promoted to 1.2, but has no // changes, so we can keep using the extension unconditionally. if capabilities.supports_extension(khr::imageless_framebuffer::NAME) { let next = features
.imageless_framebuffer
.insert(vk::PhysicalDeviceImagelessFramebufferFeaturesKHR::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_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);
} if capabilities.supports_extension(khr::shader_float16_int8::NAME)
&& capabilities.supports_extension(khr::_16bit_storage::NAME)
{ let next = features.shader_float16.insert((
vk::PhysicalDeviceShaderFloat16Int8FeaturesKHR::default(),
vk::PhysicalDevice16BitStorageFeaturesKHR::default(),
));
features2 = features2.push_next(&mut next.0);
features2 = features2.push_next(&mut next.1);
} if capabilities.supports_extension(khr::acceleration_structure::NAME) { let next = features
.acceleration_structure
.insert(vk::PhysicalDeviceAccelerationStructureFeaturesKHR::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);
}
/// 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.device_api_version,
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,
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
}
});
let swapchain_fn = khr::swapchain::Device::new(&self.instance.raw, &raw_device);
// 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 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::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
| wgt::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
) {
capabilities.push(spv::Capability::ShaderNonUniform);
} if features.contains(wgt::Features::BGRA8UNORM_STORAGE) {
capabilities.push(spv::Capability::StorageImageWriteWithoutFormat);
}
if features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
capabilities.push(spv::Capability::RayQueryKHR);
}
if features.contains(wgt::Features::SHADER_INT64) {
capabilities.push(spv::Capability::Int64);
}
if features.intersects(
wgt::Features::SHADER_INT64_ATOMIC_ALL_OPS
| wgt::Features::SHADER_INT64_ATOMIC_MIN_MAX,
) {
capabilities.push(spv::Capability::Int64Atomics);
}
if features.contains(wgt::Features::SHADER_FLOAT32_ATOMIC) {
capabilities.push(spv::Capability::AtomicFloat32AddEXT);
}
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`
); if features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY) {
capabilities.push(spv::Capability::RayQueryKHR);
}
spv::Options {
lang_version: if features
.intersects(wgt::Features::SUBGROUP | wgt::Features::SUBGROUP_VERTEX)
{
(1, 3)
} else {
(1, 0)
},
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
}, // We need to build this separately for each invocation, so just default it out here
binding_map: BTreeMap::default(),
debug_info: None,
}
};
let mem_allocator = { let limits = self.phd_capabilities.properties.limits;
// Note: the parameters here are not set in stone nor where they picked with // strong confidence. // `final_free_list_chunk` should be bigger than starting_free_list_chunk if // we want the behavior of starting with smaller block sizes and using larger // ones only after we observe that the small ones aren't enough, which I think // is a good "I don't know what the workload is going to be like" approach. // // For reference, `VMA`, and `gpu_allocator` both start with 256 MB blocks // (then VMA doubles the block size each time it needs a new block). // At some point it would be good to experiment with real workloads // // TODO(#5925): The plan is to switch the Vulkan backend from `gpu_alloc` to // `gpu_allocator` which has a different (simpler) set of configuration options. // // TODO: These parameters should take hardware capabilities into account. let mb = 1024 * 1024; let perf_cfg = gpu_alloc::Config {
starting_free_list_chunk: 128 * mb,
final_free_list_chunk: 512 * mb,
minimal_buddy_size: 1,
initial_buddy_dedicated_size: 8 * mb,
dedicated_threshold: 32 * mb,
preferred_dedicated_threshold: mb,
transient_dedicated_threshold: 128 * mb,
}; let mem_usage_cfg = gpu_alloc::Config {
starting_free_list_chunk: 8 * mb,
final_free_list_chunk: 64 * mb,
minimal_buddy_size: 1,
initial_buddy_dedicated_size: 8 * mb,
dedicated_threshold: 8 * mb,
preferred_dedicated_threshold: mb,
transient_dedicated_threshold: 16 * mb,
}; let config = match memory_hints {
wgt::MemoryHints::Performance => perf_cfg,
wgt::MemoryHints::MemoryUsage => mem_usage_cfg,
wgt::MemoryHints::Manual {
suballocated_device_memory_block_size,
} => gpu_alloc::Config {
starting_free_list_chunk: suballocated_device_memory_block_size.start,
final_free_list_chunk: suballocated_device_memory_block_size.end,
initial_buddy_dedicated_size: suballocated_device_memory_block_size.start,
..perf_cfg
},
};
let family_index = 0; //TODO let family_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(family_index)
.queue_priorities(&[1.0]); let family_infos = [family_info];
let str_pointers = enabled_extensions
.iter()
.map(|&s| { // Safe because `enabled_extensions` entries have static lifetime.
s.as_ptr()
})
.collect::<Vec<_>>();
let pre_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&family_infos)
.enabled_extension_names(&str_pointers); let info = enabled_phd_features.add_to_device_create(pre_info); let raw_device = {
profiling::scope!("vkCreateDevice"); unsafe { self.instance
.raw
.create_device(self.raw, &info, None)
.map_err(map_err)?
}
}; fn map_err(err: vk::Result) -> crate::DeviceError { match err {
vk::Result::ERROR_TOO_MANY_OBJECTS => crate::DeviceError::OutOfMemory,
vk::Result::ERROR_INITIALIZATION_FAILED => crate::DeviceError::Lost,
vk::Result::ERROR_EXTENSION_NOT_PRESENT | vk::Result::ERROR_FEATURE_NOT_PRESENT => { crate::hal_usage_error(err)
}
other => super::map_host_device_oom_and_lost_err(other),
}
}
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);
// If image count is 0, the support number of images is unlimited. let max_image_count = if caps.max_image_count == 0 {
!0
} else {
caps.max_image_count
};
// `0xFFFFFFFF` indicates that the extent depends on the created swapchain. let current_extent = if caps.current_extent.width != !0 && caps.current_extent.height != !0
{
Some(wgt::Extent3d {
width: caps.current_extent.width,
height: caps.current_extent.height,
depth_or_array_layers: 1,
})
} else {
None
};
let raw_present_modes = {
profiling::scope!("vkGetPhysicalDeviceSurfacePresentModesKHR"); matchunsafe {
surface
.functor
.get_physical_device_surface_present_modes(self.raw, surface.raw)
} {
Ok(present_modes) => present_modes,
Err(e) => {
log::error!("get_physical_device_surface_present_modes: {}", e); // Per definition of `SurfaceCapabilities`, there must be at least one present mode. return None;
}
}
};
let raw_surface_formats = {
profiling::scope!("vkGetPhysicalDeviceSurfaceFormatsKHR"); matchunsafe {
surface
.functor
.get_physical_device_surface_formats(self.raw, surface.raw)
} {
Ok(formats) => formats,
Err(e) => {
log::error!("get_physical_device_surface_formats: {}", e); // Per definition of `SurfaceCapabilities`, there must be at least one present format. return None;
}
}
};
let formats = raw_surface_formats
.into_iter()
.filter_map(conv::map_vk_surface_formats)
.collect();
Some(crate::SurfaceCapabilities {
formats, // TODO: Right now we're always trunkating the swap chain // (presumably - we're actually setting the min image count which isn't necessarily the swap chain size) // Instead, we should use extensions when available to wait in present. // See https://github.com/gfx-rs/wgpu/issues/2869
maximum_frame_latency: (caps.min_image_count - 1)..=(max_image_count - 1), // Note this can't underflow since both `min_image_count` is at least one and we already patched `max_image_count`.
current_extent,
usage: conv::map_vk_image_usage(caps.supported_usage_flags),
present_modes: raw_present_modes
.into_iter()
.flat_map(conv::map_vk_present_mode)
.collect(),
composite_alpha_modes: conv::map_vk_composite_alpha(caps.supported_composite_alpha),
})
}
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
}
}
}
fn is_format_16bit_norm_supported(instance: &ash::Instance, phd: vk::PhysicalDevice) -> bool { let tiling = vk::ImageTiling::OPTIMAL; let features = vk::FormatFeatureFlags::SAMPLED_IMAGE
| vk::FormatFeatureFlags::STORAGE_IMAGE
| vk::FormatFeatureFlags::TRANSFER_SRC
| vk::FormatFeatureFlags::TRANSFER_DST; let r16unorm = supports_format(instance, phd, vk::Format::R16_UNORM, tiling, features); let r16snorm = supports_format(instance, phd, vk::Format::R16_SNORM, tiling, features); let rg16unorm = supports_format(instance, phd, vk::Format::R16G16_UNORM, tiling, features); let rg16snorm = supports_format(instance, phd, vk::Format::R16G16_SNORM, tiling, features); let rgba16unorm = supports_format(
instance,
phd,
vk::Format::R16G16B16A16_UNORM,
tiling,
features,
); let rgba16snorm = supports_format(
instance,
phd,
vk::Format::R16G16B16A16_SNORM,
tiling,
features,
);
// 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::warn!( "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
}
Messung V0.5 in Prozent
¤ 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.0.63Bemerkung:
(vorverarbeitet am 2026-06-18)
¤
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.