// Copyright (c) 2021 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use super::QueueFamilyProperties; use crate::{ buffer::{ExternalBufferInfo, ExternalBufferProperties}, cache::OnceCache, device::{properties::Properties, DeviceExtensions, Features, FeaturesFfi, PropertiesFfi}, format::{Format, FormatProperties}, image::{ ImageAspects, ImageFormatInfo, ImageFormatProperties, ImageUsage, SparseImageFormatInfo, SparseImageFormatProperties, }, instance::Instance, macros::{impl_id_counter, vulkan_bitflags, vulkan_enum}, memory::{ExternalMemoryHandleType, MemoryProperties}, swapchain::{ ColorSpace, FullScreenExclusive, PresentMode, Surface, SurfaceApi, SurfaceCapabilities, SurfaceInfo, SurfaceTransforms, }, sync::{ fence::{ExternalFenceInfo, ExternalFenceProperties}, semaphore::{ExternalSemaphoreInfo, ExternalSemaphoreProperties}, }, ExtensionProperties, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use bytemuck::cast_slice; use std::{ error::Error, fmt::{Debug, Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc, }; /// Represents one of the available physical devices on this machine. /// /// # Examples /// /// ```no_run /// # use vulkano::{ /// # instance::{Instance, InstanceExtensions}, /// # Version, VulkanLibrary, /// # }; /// use vulkano::device::physical::PhysicalDevice; /// /// # let library = VulkanLibrary::new().unwrap(); /// # let instance = Instance::new(library, Default::default()).unwrap(); /// for physical_device in instance.enumerate_physical_devices().unwrap() { /// print_infos(&physical_device); /// } /// /// fn print_infos(dev: &PhysicalDevice) { /// println!("Name: {}", dev.properties().device_name); /// } /// ``` #[derive(Debug)] pub struct PhysicalDevice { handle: ash::vk::PhysicalDevice, instance: Arc, id: NonZeroU64, // Data queried at `PhysicalDevice` creation. api_version: Version, supported_extensions: DeviceExtensions, supported_features: Features, properties: Properties, extension_properties: Vec, memory_properties: MemoryProperties, queue_family_properties: Vec, // Data queried by the user at runtime, cached for faster lookups. external_buffer_properties: OnceCache, external_fence_properties: OnceCache, external_semaphore_properties: OnceCache, format_properties: OnceCache, image_format_properties: OnceCache>, sparse_image_format_properties: OnceCache>, } impl PhysicalDevice { /// Creates a new `PhysicalDevice` from a raw object handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `instance`. pub unsafe fn from_handle( instance: Arc, handle: ash::vk::PhysicalDevice, ) -> Result, VulkanError> { let api_version = Self::get_api_version(handle, &instance); let extension_properties = Self::get_extension_properties(handle, &instance)?; let supported_extensions: DeviceExtensions = extension_properties .iter() .map(|property| property.extension_name.as_str()) .collect(); let supported_features; let properties; let memory_properties; let queue_family_properties; // Get the remaining infos. // If possible, we use VK_KHR_get_physical_device_properties2. if api_version >= Version::V1_1 || instance .enabled_extensions() .khr_get_physical_device_properties2 { supported_features = Self::get_features2(handle, &instance, api_version, &supported_extensions); properties = Self::get_properties2(handle, &instance, api_version, &supported_extensions); memory_properties = Self::get_memory_properties2(handle, &instance); queue_family_properties = Self::get_queue_family_properties2(handle, &instance); } else { supported_features = Self::get_features(handle, &instance); properties = Self::get_properties(handle, &instance, api_version, &supported_extensions); memory_properties = Self::get_memory_properties(handle, &instance); queue_family_properties = Self::get_queue_family_properties(handle, &instance); }; Ok(Arc::new(PhysicalDevice { handle, instance, id: Self::next_id(), api_version, supported_extensions, supported_features, properties, extension_properties, memory_properties, queue_family_properties, external_buffer_properties: OnceCache::new(), external_fence_properties: OnceCache::new(), external_semaphore_properties: OnceCache::new(), format_properties: OnceCache::new(), image_format_properties: OnceCache::new(), sparse_image_format_properties: OnceCache::new(), })) } unsafe fn get_api_version(handle: ash::vk::PhysicalDevice, instance: &Instance) -> Version { let fns = instance.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.get_physical_device_properties)(handle, output.as_mut_ptr()); let api_version = Version::try_from(output.assume_init().api_version).unwrap(); std::cmp::min(instance.max_api_version(), api_version) } unsafe fn get_extension_properties( handle: ash::vk::PhysicalDevice, instance: &Instance, ) -> Result, VulkanError> { let fns = instance.fns(); loop { let mut count = 0; (fns.v1_0.enumerate_device_extension_properties)( handle, ptr::null(), &mut count, ptr::null_mut(), ) .result() .map_err(VulkanError::from)?; let mut output = Vec::with_capacity(count as usize); let result = (fns.v1_0.enumerate_device_extension_properties)( handle, ptr::null(), &mut count, output.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { output.set_len(count as usize); return Ok(output.into_iter().map(Into::into).collect()); } ash::vk::Result::INCOMPLETE => (), err => return Err(VulkanError::from(err)), } } } unsafe fn get_features(handle: ash::vk::PhysicalDevice, instance: &Instance) -> Features { let mut output = FeaturesFfi::default(); let fns = instance.fns(); (fns.v1_0.get_physical_device_features)(handle, &mut output.head_as_mut().features); Features::from(&output) } unsafe fn get_features2( handle: ash::vk::PhysicalDevice, instance: &Instance, api_version: Version, supported_extensions: &DeviceExtensions, ) -> Features { let mut output = FeaturesFfi::default(); output.make_chain( api_version, supported_extensions, instance.enabled_extensions(), ); let fns = instance.fns(); if instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_features2)(handle, output.head_as_mut()); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_features2_khr)(handle, output.head_as_mut()); } Features::from(&output) } unsafe fn get_properties( handle: ash::vk::PhysicalDevice, instance: &Instance, api_version: Version, supported_extensions: &DeviceExtensions, ) -> Properties { let mut output = PropertiesFfi::default(); output.make_chain( api_version, supported_extensions, instance.enabled_extensions(), ); let fns = instance.fns(); (fns.v1_0.get_physical_device_properties)(handle, &mut output.head_as_mut().properties); Properties::from(&output) } unsafe fn get_properties2( handle: ash::vk::PhysicalDevice, instance: &Instance, api_version: Version, supported_extensions: &DeviceExtensions, ) -> Properties { let mut output = PropertiesFfi::default(); output.make_chain( api_version, supported_extensions, instance.enabled_extensions(), ); let fns = instance.fns(); if instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_properties2)(handle, output.head_as_mut()); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_properties2_khr)(handle, output.head_as_mut()); } Properties::from(&output) } unsafe fn get_memory_properties( handle: ash::vk::PhysicalDevice, instance: &Instance, ) -> MemoryProperties { let mut output = MaybeUninit::uninit(); let fns = instance.fns(); (fns.v1_0.get_physical_device_memory_properties)(handle, output.as_mut_ptr()); output.assume_init().into() } unsafe fn get_memory_properties2( handle: ash::vk::PhysicalDevice, instance: &Instance, ) -> MemoryProperties { let mut output = ash::vk::PhysicalDeviceMemoryProperties2KHR::default(); let fns = instance.fns(); if instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_memory_properties2)(handle, &mut output); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_memory_properties2_khr)(handle, &mut output); } output.memory_properties.into() } unsafe fn get_queue_family_properties( handle: ash::vk::PhysicalDevice, instance: &Instance, ) -> Vec { let fns = instance.fns(); let mut num = 0; (fns.v1_0.get_physical_device_queue_family_properties)(handle, &mut num, ptr::null_mut()); let mut output = Vec::with_capacity(num as usize); (fns.v1_0.get_physical_device_queue_family_properties)( handle, &mut num, output.as_mut_ptr(), ); output.set_len(num as usize); output.into_iter().map(Into::into).collect() } unsafe fn get_queue_family_properties2( handle: ash::vk::PhysicalDevice, instance: &Instance, ) -> Vec { let mut num = 0; let fns = instance.fns(); if instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_queue_family_properties2)( handle, &mut num, ptr::null_mut(), ); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_queue_family_properties2_khr)( handle, &mut num, ptr::null_mut(), ); } let mut output = vec![ash::vk::QueueFamilyProperties2::default(); num as usize]; if instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_queue_family_properties2)( handle, &mut num, output.as_mut_ptr(), ); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_queue_family_properties2_khr)( handle, &mut num, output.as_mut_ptr(), ); } output .into_iter() .map(|family| family.queue_family_properties.into()) .collect() } /// Returns the instance that owns the physical device. #[inline] pub fn instance(&self) -> &Arc { &self.instance } /// Returns the version of Vulkan supported by the physical device. /// /// Unlike the `api_version` property, which is the version reported by the device directly, /// this function returns the version the device can actually support, based on the instance's /// `max_api_version`. #[inline] pub fn api_version(&self) -> Version { self.api_version } /// Returns the properties reported by the physical device. #[inline] pub fn properties(&self) -> &Properties { &self.properties } /// Returns the extension properties reported by the physical device. #[inline] pub fn extension_properties(&self) -> &[ExtensionProperties] { &self.extension_properties } /// Returns the extensions that are supported by the physical device. #[inline] pub fn supported_extensions(&self) -> &DeviceExtensions { &self.supported_extensions } /// Returns the features that are supported by the physical device. #[inline] pub fn supported_features(&self) -> &Features { &self.supported_features } /// Returns the memory properties reported by the physical device. #[inline] pub fn memory_properties(&self) -> &MemoryProperties { &self.memory_properties } /// Returns the queue family properties reported by the physical device. #[inline] pub fn queue_family_properties(&self) -> &[QueueFamilyProperties] { &self.queue_family_properties } /// Queries whether the physical device supports presenting to DirectFB surfaces from queues of /// the given queue family. /// /// # Safety /// /// - `dfb` must be a valid DirectFB `IDirectFB` handle. #[inline] pub unsafe fn directfb_presentation_support( &self, queue_family_index: u32, dfb: *const D, ) -> Result { self.validate_directfb_presentation_support(queue_family_index, dfb)?; Ok(self.directfb_presentation_support_unchecked(queue_family_index, dfb)) } fn validate_directfb_presentation_support( &self, queue_family_index: u32, _dfb: *const D, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().ext_directfb_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::directfb_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["ext_directfb_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceDirectFBPresentationSupportEXT-queueFamilyIndex-04119 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } // VUID-vkGetPhysicalDeviceDirectFBPresentationSupportEXT-dfb-parameter // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn directfb_presentation_support_unchecked( &self, queue_family_index: u32, dfb: *const D, ) -> bool { let fns = self.instance.fns(); (fns.ext_directfb_surface .get_physical_device_direct_fb_presentation_support_ext)( self.handle, queue_family_index, dfb as *mut _, ) != 0 } /// Retrieves the external memory properties supported for buffers with a given configuration. /// /// Instance API version must be at least 1.1, or the [`khr_external_memory_capabilities`] /// extension must be enabled on the instance. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// [`khr_external_memory_capabilities`]: crate::instance::InstanceExtensions::khr_external_memory_capabilities #[inline] pub fn external_buffer_properties( &self, info: ExternalBufferInfo, ) -> Result { self.validate_external_buffer_properties(&info)?; unsafe { Ok(self.external_buffer_properties_unchecked(info)) } } fn validate_external_buffer_properties( &self, info: &ExternalBufferInfo, ) -> Result<(), PhysicalDeviceError> { if !(self.instance.api_version() >= Version::V1_1 || self .instance .enabled_extensions() .khr_external_memory_capabilities) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::external_buffer_properties`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), instance_extensions: &["khr_external_memory_capabilities"], ..Default::default() }, }); } let &ExternalBufferInfo { handle_type, usage, sparse: _, _ne: _, } = info; // VUID-VkPhysicalDeviceExternalBufferInfo-usage-parameter usage.validate_physical_device(self)?; // VUID-VkPhysicalDeviceExternalBufferInfo-usage-requiredbitmask assert!(!usage.is_empty()); // VUID-VkPhysicalDeviceExternalBufferInfo-handleType-parameter handle_type.validate_physical_device(self)?; Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn external_buffer_properties_unchecked( &self, info: ExternalBufferInfo, ) -> ExternalBufferProperties { self.external_buffer_properties.get_or_insert(info, |info| { /* Input */ let &ExternalBufferInfo { handle_type, usage, sparse, _ne: _, } = info; let external_buffer_info = ash::vk::PhysicalDeviceExternalBufferInfo { flags: sparse.map(Into::into).unwrap_or_default(), usage: usage.into(), handle_type: handle_type.into(), ..Default::default() }; /* Output */ let mut external_buffer_properties = ash::vk::ExternalBufferProperties::default(); /* Call */ let fns = self.instance.fns(); if self.instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_external_buffer_properties)( self.handle, &external_buffer_info, &mut external_buffer_properties, ) } else { (fns.khr_external_memory_capabilities .get_physical_device_external_buffer_properties_khr)( self.handle, &external_buffer_info, &mut external_buffer_properties, ); } ExternalBufferProperties { external_memory_properties: external_buffer_properties .external_memory_properties .into(), } }) } /// Retrieves the external handle properties supported for fences with a given /// configuration. /// /// The instance API version must be at least 1.1, or the [`khr_external_fence_capabilities`] /// extension must be enabled on the instance. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// [`khr_external_fence_capabilities`]: crate::instance::InstanceExtensions::khr_external_fence_capabilities #[inline] pub fn external_fence_properties( &self, info: ExternalFenceInfo, ) -> Result { self.validate_external_fence_properties(&info)?; unsafe { Ok(self.external_fence_properties_unchecked(info)) } } fn validate_external_fence_properties( &self, info: &ExternalFenceInfo, ) -> Result<(), PhysicalDeviceError> { if !(self.instance.api_version() >= Version::V1_1 || self .instance .enabled_extensions() .khr_external_fence_capabilities) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::external_fence_properties`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), instance_extensions: &["khr_external_fence_capabilities"], ..Default::default() }, }); } let &ExternalFenceInfo { handle_type, _ne: _, } = info; // VUID-VkPhysicalDeviceExternalFenceInfo-handleType-parameter handle_type.validate_physical_device(self)?; Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn external_fence_properties_unchecked( &self, info: ExternalFenceInfo, ) -> ExternalFenceProperties { self.external_fence_properties.get_or_insert(info, |info| { /* Input */ let &ExternalFenceInfo { handle_type, _ne: _, } = info; let external_fence_info = ash::vk::PhysicalDeviceExternalFenceInfo { handle_type: handle_type.into(), ..Default::default() }; /* Output */ let mut external_fence_properties = ash::vk::ExternalFenceProperties::default(); /* Call */ let fns = self.instance.fns(); if self.instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_external_fence_properties)( self.handle, &external_fence_info, &mut external_fence_properties, ) } else { (fns.khr_external_fence_capabilities .get_physical_device_external_fence_properties_khr)( self.handle, &external_fence_info, &mut external_fence_properties, ); } ExternalFenceProperties { exportable: external_fence_properties .external_fence_features .intersects(ash::vk::ExternalFenceFeatureFlags::EXPORTABLE), importable: external_fence_properties .external_fence_features .intersects(ash::vk::ExternalFenceFeatureFlags::IMPORTABLE), export_from_imported_handle_types: external_fence_properties .export_from_imported_handle_types .into(), compatible_handle_types: external_fence_properties.compatible_handle_types.into(), } }) } /// Retrieves the external handle properties supported for semaphores with a given /// configuration. /// /// The instance API version must be at least 1.1, or the /// [`khr_external_semaphore_capabilities`] extension must be enabled on the instance. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// [`khr_external_semaphore_capabilities`]: crate::instance::InstanceExtensions::khr_external_semaphore_capabilities #[inline] pub fn external_semaphore_properties( &self, info: ExternalSemaphoreInfo, ) -> Result { self.validate_external_semaphore_properties(&info)?; unsafe { Ok(self.external_semaphore_properties_unchecked(info)) } } fn validate_external_semaphore_properties( &self, info: &ExternalSemaphoreInfo, ) -> Result<(), PhysicalDeviceError> { if !(self.instance.api_version() >= Version::V1_1 || self .instance .enabled_extensions() .khr_external_semaphore_capabilities) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::external_semaphore_properties`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), instance_extensions: &["khr_external_semaphore_capabilities"], ..Default::default() }, }); } let &ExternalSemaphoreInfo { handle_type, _ne: _, } = info; // VUID-VkPhysicalDeviceExternalSemaphoreInfo-handleType-parameter handle_type.validate_physical_device(self)?; Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn external_semaphore_properties_unchecked( &self, info: ExternalSemaphoreInfo, ) -> ExternalSemaphoreProperties { self.external_semaphore_properties .get_or_insert(info, |info| { /* Input */ let &ExternalSemaphoreInfo { handle_type, _ne: _, } = info; let external_semaphore_info = ash::vk::PhysicalDeviceExternalSemaphoreInfo { handle_type: handle_type.into(), ..Default::default() }; /* Output */ let mut external_semaphore_properties = ash::vk::ExternalSemaphoreProperties::default(); /* Call */ let fns = self.instance.fns(); if self.instance.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_external_semaphore_properties)( self.handle, &external_semaphore_info, &mut external_semaphore_properties, ) } else { (fns.khr_external_semaphore_capabilities .get_physical_device_external_semaphore_properties_khr)( self.handle, &external_semaphore_info, &mut external_semaphore_properties, ); } ExternalSemaphoreProperties { exportable: external_semaphore_properties .external_semaphore_features .intersects(ash::vk::ExternalSemaphoreFeatureFlags::EXPORTABLE), importable: external_semaphore_properties .external_semaphore_features .intersects(ash::vk::ExternalSemaphoreFeatureFlags::IMPORTABLE), export_from_imported_handle_types: external_semaphore_properties .export_from_imported_handle_types .into(), compatible_handle_types: external_semaphore_properties .compatible_handle_types .into(), } }) } /// Retrieves the properties of a format when used by this physical device. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. #[inline] pub fn format_properties( &self, format: Format, ) -> Result { self.validate_format_properties(format)?; unsafe { Ok(self.format_properties_unchecked(format)) } } fn validate_format_properties(&self, format: Format) -> Result<(), PhysicalDeviceError> { // VUID-vkGetPhysicalDeviceFormatProperties2-format-parameter format.validate_physical_device(self)?; Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn format_properties_unchecked(&self, format: Format) -> FormatProperties { self.format_properties.get_or_insert(format, |&format| { let mut format_properties2 = ash::vk::FormatProperties2::default(); let mut format_properties3 = if self.api_version() >= Version::V1_3 || self.supported_extensions().khr_format_feature_flags2 { Some(ash::vk::FormatProperties3KHR::default()) } else { None }; if let Some(next) = format_properties3.as_mut() { next.p_next = format_properties2.p_next; format_properties2.p_next = next as *mut _ as *mut _; } let fns = self.instance.fns(); if self.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_format_properties2)( self.handle, format.into(), &mut format_properties2, ); } else if self .instance .enabled_extensions() .khr_get_physical_device_properties2 { (fns.khr_get_physical_device_properties2 .get_physical_device_format_properties2_khr)( self.handle, format.into(), &mut format_properties2, ); } else { (fns.v1_0.get_physical_device_format_properties)( self.handle(), format.into(), &mut format_properties2.format_properties, ); } match format_properties3 { Some(format_properties3) => FormatProperties { linear_tiling_features: format_properties3.linear_tiling_features.into(), optimal_tiling_features: format_properties3.optimal_tiling_features.into(), buffer_features: format_properties3.buffer_features.into(), _ne: crate::NonExhaustive(()), }, None => FormatProperties { linear_tiling_features: format_properties2 .format_properties .linear_tiling_features .into(), optimal_tiling_features: format_properties2 .format_properties .optimal_tiling_features .into(), buffer_features: format_properties2.format_properties.buffer_features.into(), _ne: crate::NonExhaustive(()), }, } }) } /// Returns the properties supported for images with a given image configuration. /// /// `Some` is returned if the configuration is supported, `None` if it is not. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// # Panics /// /// - Panics if `image_format_info.format` is `None`. #[inline] pub fn image_format_properties( &self, image_format_info: ImageFormatInfo, ) -> Result, PhysicalDeviceError> { self.validate_image_format_properties(&image_format_info)?; unsafe { Ok(self.image_format_properties_unchecked(image_format_info)?) } } // FIXME: Shouldn't this be private? pub fn validate_image_format_properties( &self, image_format_info: &ImageFormatInfo, ) -> Result<(), PhysicalDeviceError> { let &ImageFormatInfo { flags: _, format, image_type, tiling, usage, mut stencil_usage, external_memory_handle_type, image_view_type, _ne: _, } = image_format_info; let format = format.unwrap(); let aspects = format.aspects(); let has_separate_stencil_usage = if stencil_usage.is_empty() || !aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { stencil_usage = usage; false } else { stencil_usage == usage }; // VUID-VkPhysicalDeviceImageFormatInfo2-format-parameter format.validate_physical_device(self)?; // VUID-VkPhysicalDeviceImageFormatInfo2-imageType-parameter image_type.validate_physical_device(self)?; // VUID-VkPhysicalDeviceImageFormatInfo2-tiling-parameter tiling.validate_physical_device(self)?; // VUID-VkPhysicalDeviceImageFormatInfo2-usage-parameter usage.validate_physical_device(self)?; // VUID-VkPhysicalDeviceImageFormatInfo2-usage-requiredbitmask assert!(!usage.is_empty()); if has_separate_stencil_usage { if !(self.api_version() >= Version::V1_2 || self.supported_extensions().ext_separate_stencil_usage) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`image_format_info.stencil_usage` is `Some` and \ `image_format_info.format` has both a depth and a stencil aspect", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_2), device_extensions: &["ext_separate_stencil_usage"], ..Default::default() }, }); } // VUID-VkImageStencilUsageCreateInfo-stencilUsage-parameter stencil_usage.validate_physical_device(self)?; // VUID-VkImageStencilUsageCreateInfo-usage-requiredbitmask assert!(!stencil_usage.is_empty()); } if let Some(handle_type) = external_memory_handle_type { if !(self.api_version() >= Version::V1_1 || self .instance() .enabled_extensions() .khr_external_memory_capabilities) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`image_format_info.external_memory_handle_type` is `Some`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), instance_extensions: &["khr_external_memory_capabilities"], ..Default::default() }, }); } // VUID-VkPhysicalDeviceExternalImageFormatInfo-handleType-parameter handle_type.validate_physical_device(self)?; } if let Some(image_view_type) = image_view_type { if !self.supported_extensions().ext_filter_cubic { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`image_format_info.image_view_type` is `Some`", requires_one_of: RequiresOneOf { device_extensions: &["ext_filter_cubic"], ..Default::default() }, }); } // VUID-VkPhysicalDeviceImageViewImageFormatInfoEXT-imageViewType-parameter image_view_type.validate_physical_device(self)?; } // TODO: VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02313 // Currently there is nothing in Vulkano for for adding a VkImageFormatListCreateInfo. Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn image_format_properties_unchecked( &self, mut image_format_info: ImageFormatInfo, ) -> Result, VulkanError> { { let ImageFormatInfo { format, usage, stencil_usage, .. } = &mut image_format_info; let aspects = format.unwrap().aspects(); if stencil_usage.is_empty() || !aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { *stencil_usage = *usage; } } self.image_format_properties .get_or_try_insert(image_format_info, |image_format_info| { /* Input */ let &ImageFormatInfo { flags, format, image_type, tiling, usage, stencil_usage, external_memory_handle_type, image_view_type, _ne: _, } = image_format_info; let has_separate_stencil_usage = stencil_usage == usage; let mut info2_vk = ash::vk::PhysicalDeviceImageFormatInfo2 { format: format.unwrap().into(), ty: image_type.into(), tiling: tiling.into(), usage: usage.into(), flags: flags.into(), ..Default::default() }; let mut external_info_vk = None; let mut image_view_info_vk = None; let mut stencil_usage_info_vk = None; if let Some(handle_type) = external_memory_handle_type { let next = external_info_vk.insert(ash::vk::PhysicalDeviceExternalImageFormatInfo { handle_type: handle_type.into(), ..Default::default() }); next.p_next = info2_vk.p_next; info2_vk.p_next = next as *const _ as *const _; } if let Some(image_view_type) = image_view_type { let next = image_view_info_vk.insert( ash::vk::PhysicalDeviceImageViewImageFormatInfoEXT { image_view_type: image_view_type.into(), ..Default::default() }, ); next.p_next = info2_vk.p_next as *mut _; info2_vk.p_next = next as *const _ as *const _; } if has_separate_stencil_usage { let next = stencil_usage_info_vk.insert(ash::vk::ImageStencilUsageCreateInfo { stencil_usage: stencil_usage.into(), ..Default::default() }); next.p_next = info2_vk.p_next as *mut _; info2_vk.p_next = next as *const _ as *const _; } /* Output */ let mut properties2_vk = ash::vk::ImageFormatProperties2::default(); let mut external_properties_vk = None; let mut filter_cubic_image_view_properties_vk = None; if external_info_vk.is_some() { let next = external_properties_vk .insert(ash::vk::ExternalImageFormatProperties::default()); next.p_next = properties2_vk.p_next; properties2_vk.p_next = next as *mut _ as *mut _; } if image_view_info_vk.is_some() { let next = filter_cubic_image_view_properties_vk .insert(ash::vk::FilterCubicImageViewImageFormatPropertiesEXT::default()); next.p_next = properties2_vk.p_next; properties2_vk.p_next = next as *mut _ as *mut _; } let result = { let fns = self.instance.fns(); if self.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_image_format_properties2)( self.handle, &info2_vk, &mut properties2_vk, ) } else if self .instance .enabled_extensions() .khr_get_physical_device_properties2 { (fns.khr_get_physical_device_properties2 .get_physical_device_image_format_properties2_khr)( self.handle, &info2_vk, &mut properties2_vk, ) } else { // Can't query this, return unsupported if !info2_vk.p_next.is_null() { return Ok(None); } if let Some(ExternalMemoryHandleType::DmaBuf) = external_memory_handle_type { // VUID-vkGetPhysicalDeviceImageFormatProperties-tiling-02248 // VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249 return Ok(None); } (fns.v1_0.get_physical_device_image_format_properties)( self.handle, info2_vk.format, info2_vk.ty, info2_vk.tiling, info2_vk.usage, info2_vk.flags, &mut properties2_vk.image_format_properties, ) } .result() .map_err(VulkanError::from) }; Ok(match result { Ok(_) => Some(ImageFormatProperties { external_memory_properties: external_properties_vk .map(|properties| properties.external_memory_properties.into()) .unwrap_or_default(), filter_cubic: filter_cubic_image_view_properties_vk .map_or(false, |properties| { properties.filter_cubic != ash::vk::FALSE }), filter_cubic_minmax: filter_cubic_image_view_properties_vk .map_or(false, |properties| { properties.filter_cubic_minmax != ash::vk::FALSE }), ..properties2_vk.image_format_properties.into() }), Err(VulkanError::FormatNotSupported) => None, Err(err) => return Err(err), }) }) } /// Queries whether the physical device supports presenting to QNX Screen surfaces from queues /// of the given queue family. /// /// # Safety /// /// - `window` must be a valid QNX Screen `_screen_window` handle. pub unsafe fn qnx_screen_presentation_support( &self, queue_family_index: u32, window: *const W, ) -> Result { self.validate_qnx_screen_presentation_support(queue_family_index, window)?; Ok(self.qnx_screen_presentation_support_unchecked(queue_family_index, window)) } fn validate_qnx_screen_presentation_support( &self, queue_family_index: u32, _window: *const W, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().qnx_screen_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::qnx_screen_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["qnx_screen_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceScreenPresentationSupportQNX-queueFamilyIndex-04743 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } // VUID-vkGetPhysicalDeviceScreenPresentationSupportQNX-window-parameter // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn qnx_screen_presentation_support_unchecked( &self, queue_family_index: u32, window: *const W, ) -> bool { let fns = self.instance.fns(); (fns.qnx_screen_surface .get_physical_device_screen_presentation_support_qnx)( self.handle, queue_family_index, window as *mut _, ) != 0 } /// Returns the properties of sparse images with a given image configuration. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// # Panics /// /// - Panics if `format_info.format` is `None`. #[inline] pub fn sparse_image_format_properties( &self, format_info: SparseImageFormatInfo, ) -> Result, PhysicalDeviceError> { self.validate_sparse_image_format_properties(&format_info)?; unsafe { Ok(self.sparse_image_format_properties_unchecked(format_info)) } } fn validate_sparse_image_format_properties( &self, format_info: &SparseImageFormatInfo, ) -> Result<(), PhysicalDeviceError> { let &SparseImageFormatInfo { format, image_type, samples, usage, tiling, _ne: _, } = format_info; let format = format.unwrap(); // VUID-VkPhysicalDeviceSparseImageFormatInfo2-format-parameter format.validate_physical_device(self)?; // VUID-VkPhysicalDeviceSparseImageFormatInfo2-type-parameter image_type.validate_physical_device(self)?; // VUID-VkPhysicalDeviceSparseImageFormatInfo2-samples-parameter samples.validate_physical_device(self)?; // VUID-VkPhysicalDeviceSparseImageFormatInfo2-usage-parameter usage.validate_physical_device(self)?; // VUID-VkPhysicalDeviceSparseImageFormatInfo2-usage-requiredbitmask assert!(!usage.is_empty()); // VUID-VkPhysicalDeviceSparseImageFormatInfo2-tiling-parameter tiling.validate_physical_device(self)?; // VUID-VkPhysicalDeviceSparseImageFormatInfo2-samples-01095 // TODO: Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn sparse_image_format_properties_unchecked( &self, format_info: SparseImageFormatInfo, ) -> Vec { self.sparse_image_format_properties .get_or_insert(format_info, |format_info| { let &SparseImageFormatInfo { format, image_type, samples, usage, tiling, _ne: _, } = format_info; let format_info2 = ash::vk::PhysicalDeviceSparseImageFormatInfo2 { format: format.unwrap().into(), ty: image_type.into(), samples: samples.into(), usage: usage.into(), tiling: tiling.into(), ..Default::default() }; let fns = self.instance.fns(); if self.api_version() >= Version::V1_1 || self .instance .enabled_extensions() .khr_get_physical_device_properties2 { let mut count = 0; if self.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_sparse_image_format_properties2)( self.handle, &format_info2, &mut count, ptr::null_mut(), ); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_sparse_image_format_properties2_khr)( self.handle, &format_info2, &mut count, ptr::null_mut(), ); } let mut sparse_image_format_properties2 = vec![ash::vk::SparseImageFormatProperties2::default(); count as usize]; if self.api_version() >= Version::V1_1 { (fns.v1_1.get_physical_device_sparse_image_format_properties2)( self.handle, &format_info2, &mut count, sparse_image_format_properties2.as_mut_ptr(), ); } else { (fns.khr_get_physical_device_properties2 .get_physical_device_sparse_image_format_properties2_khr)( self.handle, &format_info2, &mut count, sparse_image_format_properties2.as_mut_ptr(), ); } sparse_image_format_properties2.set_len(count as usize); sparse_image_format_properties2 .into_iter() .map( |sparse_image_format_properties2| SparseImageFormatProperties { aspects: sparse_image_format_properties2 .properties .aspect_mask .into(), image_granularity: [ sparse_image_format_properties2 .properties .image_granularity .width, sparse_image_format_properties2 .properties .image_granularity .height, sparse_image_format_properties2 .properties .image_granularity .depth, ], flags: sparse_image_format_properties2.properties.flags.into(), }, ) .collect() } else { let mut count = 0; (fns.v1_0.get_physical_device_sparse_image_format_properties)( self.handle, format_info2.format, format_info2.ty, format_info2.samples, format_info2.usage, format_info2.tiling, &mut count, ptr::null_mut(), ); let mut sparse_image_format_properties = vec![ash::vk::SparseImageFormatProperties::default(); count as usize]; (fns.v1_0.get_physical_device_sparse_image_format_properties)( self.handle, format_info2.format, format_info2.ty, format_info2.samples, format_info2.usage, format_info2.tiling, &mut count, sparse_image_format_properties.as_mut_ptr(), ); sparse_image_format_properties.set_len(count as usize); sparse_image_format_properties .into_iter() .map( |sparse_image_format_properties| SparseImageFormatProperties { aspects: sparse_image_format_properties.aspect_mask.into(), image_granularity: [ sparse_image_format_properties.image_granularity.width, sparse_image_format_properties.image_granularity.height, sparse_image_format_properties.image_granularity.depth, ], flags: sparse_image_format_properties.flags.into(), }, ) .collect() } }) } /// Returns the capabilities that are supported by the physical device for the given surface. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// # Panics /// /// - Panics if the physical device and the surface don't belong to the same instance. pub fn surface_capabilities( &self, surface: &Surface, surface_info: SurfaceInfo, ) -> Result { self.validate_surface_capabilities(surface, &surface_info)?; unsafe { Ok(self.surface_capabilities_unchecked(surface, surface_info)?) } } fn validate_surface_capabilities( &self, surface: &Surface, surface_info: &SurfaceInfo, ) -> Result<(), PhysicalDeviceError> { if !(self .instance .enabled_extensions() .khr_get_surface_capabilities2 || self.instance.enabled_extensions().khr_surface) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::surface_capabilities`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_get_surface_capabilities2", "khr_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceSurfaceCapabilities2KHR-commonparent assert_eq!(self.instance(), surface.instance()); // VUID-vkGetPhysicalDeviceSurfaceCapabilities2KHR-pSurfaceInfo-06210 if !(0..self.queue_family_properties.len() as u32).any(|index| unsafe { self.surface_support_unchecked(index, surface) .unwrap_or_default() }) { return Err(PhysicalDeviceError::SurfaceNotSupported); } let &SurfaceInfo { full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; if !self.supported_extensions().ext_full_screen_exclusive && full_screen_exclusive != FullScreenExclusive::Default { return Err(PhysicalDeviceError::NotSupported); } // VUID-VkPhysicalDeviceSurfaceInfo2KHR-pNext-02672 if (surface.api() == SurfaceApi::Win32 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled) != win32_monitor.is_some() { return Err(PhysicalDeviceError::NotSupported); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn surface_capabilities_unchecked( &self, surface: &Surface, surface_info: SurfaceInfo, ) -> Result { /* Input */ let SurfaceInfo { full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; let mut info_vk = ash::vk::PhysicalDeviceSurfaceInfo2KHR { surface: surface.handle(), ..Default::default() }; let mut full_screen_exclusive_info_vk = None; let mut full_screen_exclusive_win32_info_vk = None; if self.supported_extensions().ext_full_screen_exclusive && win32_monitor.is_some() { let next = full_screen_exclusive_info_vk.insert(ash::vk::SurfaceFullScreenExclusiveInfoEXT { full_screen_exclusive: full_screen_exclusive.into(), ..Default::default() }); next.p_next = info_vk.p_next as *mut _; info_vk.p_next = next as *const _ as *const _; } if let Some(win32_monitor) = win32_monitor { let next = full_screen_exclusive_win32_info_vk.insert( ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { hmonitor: win32_monitor.0, ..Default::default() }, ); next.p_next = info_vk.p_next as *mut _; info_vk.p_next = next as *const _ as *const _; } /* Output */ let mut capabilities_vk = ash::vk::SurfaceCapabilities2KHR::default(); let mut capabilities_full_screen_exclusive_vk = None; let mut protected_capabilities_vk = None; if full_screen_exclusive_info_vk.is_some() { let next = capabilities_full_screen_exclusive_vk .insert(ash::vk::SurfaceCapabilitiesFullScreenExclusiveEXT::default()); next.p_next = capabilities_vk.p_next as *mut _; capabilities_vk.p_next = next as *mut _ as *mut _; } if self .instance .enabled_extensions() .khr_surface_protected_capabilities { let next = protected_capabilities_vk .insert(ash::vk::SurfaceProtectedCapabilitiesKHR::default()); next.p_next = capabilities_vk.p_next as *mut _; capabilities_vk.p_next = next as *mut _ as *mut _; } let fns = self.instance.fns(); if self .instance .enabled_extensions() .khr_get_surface_capabilities2 { (fns.khr_get_surface_capabilities2 .get_physical_device_surface_capabilities2_khr)( self.handle(), &info_vk, &mut capabilities_vk, ) .result() .map_err(VulkanError::from)?; } else { (fns.khr_surface.get_physical_device_surface_capabilities_khr)( self.handle(), info_vk.surface, &mut capabilities_vk.surface_capabilities, ) .result() .map_err(VulkanError::from)?; }; Ok(SurfaceCapabilities { min_image_count: capabilities_vk.surface_capabilities.min_image_count, max_image_count: if capabilities_vk.surface_capabilities.max_image_count == 0 { None } else { Some(capabilities_vk.surface_capabilities.max_image_count) }, current_extent: if capabilities_vk.surface_capabilities.current_extent.width == 0xffffffff && capabilities_vk.surface_capabilities.current_extent.height == 0xffffffff { None } else { Some([ capabilities_vk.surface_capabilities.current_extent.width, capabilities_vk.surface_capabilities.current_extent.height, ]) }, min_image_extent: [ capabilities_vk.surface_capabilities.min_image_extent.width, capabilities_vk.surface_capabilities.min_image_extent.height, ], max_image_extent: [ capabilities_vk.surface_capabilities.max_image_extent.width, capabilities_vk.surface_capabilities.max_image_extent.height, ], max_image_array_layers: capabilities_vk.surface_capabilities.max_image_array_layers, supported_transforms: capabilities_vk .surface_capabilities .supported_transforms .into(), current_transform: SurfaceTransforms::from( capabilities_vk.surface_capabilities.current_transform, ) .into_iter() .next() .unwrap(), // TODO: supported_composite_alpha: capabilities_vk .surface_capabilities .supported_composite_alpha .into(), supported_usage_flags: { let usage = ImageUsage::from(capabilities_vk.surface_capabilities.supported_usage_flags); debug_assert!(usage.intersects(ImageUsage::COLOR_ATTACHMENT)); // specs say that this must be true usage }, supports_protected: protected_capabilities_vk .map_or(false, |c| c.supports_protected != 0), full_screen_exclusive_supported: capabilities_full_screen_exclusive_vk .map_or(false, |c| c.full_screen_exclusive_supported != 0), }) } /// Returns the combinations of format and color space that are supported by the physical device /// for the given surface. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// # Panics /// /// - Panics if the physical device and the surface don't belong to the same instance. pub fn surface_formats( &self, surface: &Surface, surface_info: SurfaceInfo, ) -> Result, PhysicalDeviceError> { self.validate_surface_formats(surface, &surface_info)?; unsafe { Ok(self.surface_formats_unchecked(surface, surface_info)?) } } fn validate_surface_formats( &self, surface: &Surface, surface_info: &SurfaceInfo, ) -> Result<(), PhysicalDeviceError> { if !(self .instance .enabled_extensions() .khr_get_surface_capabilities2 || self.instance.enabled_extensions().khr_surface) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::surface_formats`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_get_surface_capabilities2", "khr_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceSurfaceFormats2KHR-commonparent assert_eq!(self.instance(), surface.instance()); // VUID-vkGetPhysicalDeviceSurfaceFormats2KHR-pSurfaceInfo-06522 if !(0..self.queue_family_properties.len() as u32).any(|index| unsafe { self.surface_support_unchecked(index, surface) .unwrap_or_default() }) { return Err(PhysicalDeviceError::SurfaceNotSupported); } let &SurfaceInfo { full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; if self .instance .enabled_extensions() .khr_get_surface_capabilities2 { if !self.supported_extensions().ext_full_screen_exclusive && full_screen_exclusive != FullScreenExclusive::Default { return Err(PhysicalDeviceError::NotSupported); } // VUID-VkPhysicalDeviceSurfaceInfo2KHR-pNext-02672 if (surface.api() == SurfaceApi::Win32 && full_screen_exclusive == FullScreenExclusive::ApplicationControlled) != win32_monitor.is_some() { return Err(PhysicalDeviceError::NotSupported); } } else { if full_screen_exclusive != FullScreenExclusive::Default { return Err(PhysicalDeviceError::NotSupported); } if win32_monitor.is_some() { return Err(PhysicalDeviceError::NotSupported); } } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn surface_formats_unchecked( &self, surface: &Surface, surface_info: SurfaceInfo, ) -> Result, VulkanError> { surface.surface_formats.get_or_try_insert( (self.handle, surface_info), |(_, surface_info)| { let &SurfaceInfo { full_screen_exclusive, win32_monitor, _ne: _, } = surface_info; let mut surface_full_screen_exclusive_info = (full_screen_exclusive != FullScreenExclusive::Default) .then(|| ash::vk::SurfaceFullScreenExclusiveInfoEXT { full_screen_exclusive: full_screen_exclusive.into(), ..Default::default() }); let mut surface_full_screen_exclusive_win32_info = win32_monitor.map(|win32_monitor| { ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT { hmonitor: win32_monitor.0, ..Default::default() } }); let mut surface_info2 = ash::vk::PhysicalDeviceSurfaceInfo2KHR { surface: surface.handle(), ..Default::default() }; if let Some(surface_full_screen_exclusive_info) = surface_full_screen_exclusive_info.as_mut() { surface_full_screen_exclusive_info.p_next = surface_info2.p_next as *mut _; surface_info2.p_next = surface_full_screen_exclusive_info as *const _ as *const _; } if let Some(surface_full_screen_exclusive_win32_info) = surface_full_screen_exclusive_win32_info.as_mut() { surface_full_screen_exclusive_win32_info.p_next = surface_info2.p_next as *mut _; surface_info2.p_next = surface_full_screen_exclusive_win32_info as *const _ as *const _; } let fns = self.instance.fns(); if self .instance .enabled_extensions() .khr_get_surface_capabilities2 { let surface_format2s = loop { let mut count = 0; (fns.khr_get_surface_capabilities2 .get_physical_device_surface_formats2_khr)( self.handle(), &surface_info2, &mut count, ptr::null_mut(), ) .result() .map_err(VulkanError::from)?; let mut surface_format2s = vec![ash::vk::SurfaceFormat2KHR::default(); count as usize]; let result = (fns .khr_get_surface_capabilities2 .get_physical_device_surface_formats2_khr)( self.handle(), &surface_info2, &mut count, surface_format2s.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { surface_format2s.set_len(count as usize); break surface_format2s; } ash::vk::Result::INCOMPLETE => (), err => return Err(VulkanError::from(err)), } }; Ok(surface_format2s .into_iter() .filter_map(|surface_format2| { (surface_format2.surface_format.format.try_into().ok()) .zip(surface_format2.surface_format.color_space.try_into().ok()) }) .collect()) } else { let surface_formats = loop { let mut count = 0; (fns.khr_surface.get_physical_device_surface_formats_khr)( self.handle(), surface.handle(), &mut count, ptr::null_mut(), ) .result() .map_err(VulkanError::from)?; let mut surface_formats = Vec::with_capacity(count as usize); let result = (fns.khr_surface.get_physical_device_surface_formats_khr)( self.handle(), surface.handle(), &mut count, surface_formats.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { surface_formats.set_len(count as usize); break surface_formats; } ash::vk::Result::INCOMPLETE => (), err => return Err(VulkanError::from(err)), } }; Ok(surface_formats .into_iter() .filter_map(|surface_format| { (surface_format.format.try_into().ok()) .zip(surface_format.color_space.try_into().ok()) }) .collect()) } }, ) } /// Returns the present modes that are supported by the physical device for the given surface. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. /// /// # Panics /// /// - Panics if the physical device and the surface don't belong to the same instance. pub fn surface_present_modes( &self, surface: &Surface, ) -> Result, PhysicalDeviceError> { self.validate_surface_present_modes(surface)?; unsafe { Ok(self.surface_present_modes_unchecked(surface)?) } } fn validate_surface_present_modes(&self, surface: &Surface) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::surface_present_modes`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceSurfacePresentModesKHR-commonparent assert_eq!(self.instance(), surface.instance()); // VUID-vkGetPhysicalDeviceSurfacePresentModesKHR-surface-06525 if !(0..self.queue_family_properties.len() as u32).any(|index| unsafe { self.surface_support_unchecked(index, surface) .unwrap_or_default() }) { return Err(PhysicalDeviceError::SurfaceNotSupported); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn surface_present_modes_unchecked( &self, surface: &Surface, ) -> Result, VulkanError> { surface .surface_present_modes .get_or_try_insert(self.handle, |_| { let fns = self.instance.fns(); let modes = loop { let mut count = 0; (fns.khr_surface .get_physical_device_surface_present_modes_khr)( self.handle(), surface.handle(), &mut count, ptr::null_mut(), ) .result() .map_err(VulkanError::from)?; let mut modes = Vec::with_capacity(count as usize); let result = (fns .khr_surface .get_physical_device_surface_present_modes_khr)( self.handle(), surface.handle(), &mut count, modes.as_mut_ptr(), ); match result { ash::vk::Result::SUCCESS => { modes.set_len(count as usize); break modes; } ash::vk::Result::INCOMPLETE => (), err => return Err(VulkanError::from(err)), } }; Ok(modes .into_iter() .filter_map(|mode_vk| mode_vk.try_into().ok()) .collect()) }) .map(IntoIterator::into_iter) } /// Returns whether queues of the given queue family can draw on the given surface. /// /// The results of this function are cached, so that future calls with the same arguments /// do not need to make a call to the Vulkan API again. #[inline] pub fn surface_support( &self, queue_family_index: u32, surface: &Surface, ) -> Result { self.validate_surface_support(queue_family_index, surface)?; unsafe { Ok(self.surface_support_unchecked(queue_family_index, surface)?) } } fn validate_surface_support( &self, queue_family_index: u32, _surface: &Surface, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::surface_support`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceSurfaceSupportKHR-queueFamilyIndex-01269 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn surface_support_unchecked( &self, queue_family_index: u32, surface: &Surface, ) -> Result { surface .surface_support .get_or_try_insert((self.handle, queue_family_index), |_| { let fns = self.instance.fns(); let mut output = MaybeUninit::uninit(); (fns.khr_surface.get_physical_device_surface_support_khr)( self.handle, queue_family_index, surface.handle(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; Ok(output.assume_init() != 0) }) } /// Retrieves the properties of tools that are currently active on the physical device. /// /// These properties may change during runtime, so the result only reflects the current /// situation and is not cached. /// /// The physical device API version must be at least 1.3, or the /// [`ext_tooling_info`](crate::device::DeviceExtensions::ext_tooling_info) /// extension must be supported by the physical device. #[inline] pub fn tool_properties(&self) -> Result, PhysicalDeviceError> { self.validate_tool_properties()?; unsafe { Ok(self.tool_properties_unchecked()?) } } fn validate_tool_properties(&self) -> Result<(), PhysicalDeviceError> { if !(self.api_version() >= Version::V1_3 || self.supported_extensions().ext_tooling_info) { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::tooling_properties`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), device_extensions: &["ext_tooling_info"], ..Default::default() }, }); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn tool_properties_unchecked(&self) -> Result, VulkanError> { let fns = self.instance.fns(); loop { let mut count = 0; if self.api_version() >= Version::V1_3 { (fns.v1_3.get_physical_device_tool_properties)( self.handle(), &mut count, ptr::null_mut(), ) } else { (fns.ext_tooling_info.get_physical_device_tool_properties_ext)( self.handle(), &mut count, ptr::null_mut(), ) } .result() .map_err(VulkanError::from)?; let mut tool_properties = Vec::with_capacity(count as usize); let result = if self.api_version() >= Version::V1_3 { (fns.v1_3.get_physical_device_tool_properties)( self.handle(), &mut count, tool_properties.as_mut_ptr(), ) } else { (fns.ext_tooling_info.get_physical_device_tool_properties_ext)( self.handle(), &mut count, tool_properties.as_mut_ptr(), ) }; match result { ash::vk::Result::INCOMPLETE => (), ash::vk::Result::SUCCESS => { tool_properties.set_len(count as usize); return Ok(tool_properties .into_iter() .map(|tool_properties| ToolProperties { name: { let bytes = cast_slice(tool_properties.name.as_slice()); let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len()); String::from_utf8_lossy(&bytes[0..end]).into() }, version: { let bytes = cast_slice(tool_properties.version.as_slice()); let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len()); String::from_utf8_lossy(&bytes[0..end]).into() }, purposes: tool_properties.purposes.into(), description: { let bytes = cast_slice(tool_properties.description.as_slice()); let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len()); String::from_utf8_lossy(&bytes[0..end]).into() }, layer: { let bytes = cast_slice(tool_properties.layer.as_slice()); let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len()); String::from_utf8_lossy(&bytes[0..end]).into() }, }) .collect()); } err => return Err(VulkanError::from(err)), } } } /// Queries whether the physical device supports presenting to Wayland surfaces from queues of /// the given queue family. /// /// # Safety /// /// - `display` must be a valid Wayland `wl_display` handle. pub unsafe fn wayland_presentation_support( &self, queue_family_index: u32, display: *const D, ) -> Result { self.validate_wayland_presentation_support(queue_family_index, display)?; Ok(self.wayland_presentation_support_unchecked(queue_family_index, display)) } fn validate_wayland_presentation_support( &self, queue_family_index: u32, _display: *const D, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_wayland_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::wayland_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_wayland_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceWaylandPresentationSupportKHR-queueFamilyIndex-01306 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } // VUID-vkGetPhysicalDeviceWaylandPresentationSupportKHR-display-parameter // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn wayland_presentation_support_unchecked( &self, queue_family_index: u32, display: *const D, ) -> bool { let fns = self.instance.fns(); (fns.khr_wayland_surface .get_physical_device_wayland_presentation_support_khr)( self.handle, queue_family_index, display as *mut _, ) != 0 } /// Queries whether the physical device supports presenting to Win32 surfaces from queues of the /// given queue family. #[inline] pub fn win32_presentation_support( &self, queue_family_index: u32, ) -> Result { self.validate_win32_presentation_support(queue_family_index)?; unsafe { Ok(self.win32_presentation_support_unchecked(queue_family_index)) } } fn validate_win32_presentation_support( &self, queue_family_index: u32, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_win32_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::win32_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_win32_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceWin32PresentationSupportKHR-queueFamilyIndex-01309 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn win32_presentation_support_unchecked(&self, queue_family_index: u32) -> bool { let fns = self.instance.fns(); (fns.khr_win32_surface .get_physical_device_win32_presentation_support_khr)( self.handle, queue_family_index ) != 0 } /// Queries whether the physical device supports presenting to XCB surfaces from queues of the /// given queue family. /// /// # Safety /// /// - `connection` must be a valid X11 `xcb_connection_t` handle. pub unsafe fn xcb_presentation_support( &self, queue_family_index: u32, connection: *const C, visual_id: ash::vk::xcb_visualid_t, ) -> Result { self.validate_xcb_presentation_support(queue_family_index, connection, visual_id)?; Ok(self.xcb_presentation_support_unchecked(queue_family_index, connection, visual_id)) } fn validate_xcb_presentation_support( &self, queue_family_index: u32, _connection: *const C, _visual_id: ash::vk::xcb_visualid_t, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_xcb_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::xcb_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_xcb_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceXcbPresentationSupportKHR-queueFamilyIndex-01312 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } // VUID-vkGetPhysicalDeviceXcbPresentationSupportKHR-connection-parameter // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn xcb_presentation_support_unchecked( &self, queue_family_index: u32, connection: *const C, visual_id: ash::vk::VisualID, ) -> bool { let fns = self.instance.fns(); (fns.khr_xcb_surface .get_physical_device_xcb_presentation_support_khr)( self.handle, queue_family_index, connection as *mut _, visual_id, ) != 0 } /// Queries whether the physical device supports presenting to Xlib surfaces from queues of the /// given queue family. /// /// # Safety /// /// - `display` must be a valid Xlib `Display` handle. pub unsafe fn xlib_presentation_support( &self, queue_family_index: u32, display: *const D, visual_id: ash::vk::VisualID, ) -> Result { self.validate_xlib_presentation_support(queue_family_index, display, visual_id)?; Ok(self.xlib_presentation_support_unchecked(queue_family_index, display, visual_id)) } fn validate_xlib_presentation_support( &self, queue_family_index: u32, _display: *const D, _visual_id: ash::vk::VisualID, ) -> Result<(), PhysicalDeviceError> { if !self.instance.enabled_extensions().khr_xlib_surface { return Err(PhysicalDeviceError::RequirementNotMet { required_for: "`PhysicalDevice::xlib_presentation_support`", requires_one_of: RequiresOneOf { instance_extensions: &["khr_xlib_surface"], ..Default::default() }, }); } // VUID-vkGetPhysicalDeviceXlibPresentationSupportKHR-queueFamilyIndex-01315 if queue_family_index >= self.queue_family_properties.len() as u32 { return Err(PhysicalDeviceError::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count: self.queue_family_properties.len() as u32, }); } // VUID-vkGetPhysicalDeviceXlibPresentationSupportKHR-dpy-parameter // Can't validate, therefore unsafe Ok(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn xlib_presentation_support_unchecked( &self, queue_family_index: u32, display: *const D, visual_id: ash::vk::VisualID, ) -> bool { let fns = self.instance.fns(); (fns.khr_xlib_surface .get_physical_device_xlib_presentation_support_khr)( self.handle, queue_family_index, display as *mut _, visual_id, ) != 0 } } unsafe impl VulkanObject for PhysicalDevice { type Handle = ash::vk::PhysicalDevice; #[inline] fn handle(&self) -> Self::Handle { self.handle } } impl_id_counter!(PhysicalDevice); vulkan_enum! { #[non_exhaustive] /// Type of a physical device. PhysicalDeviceType = PhysicalDeviceType(i32); /// The device is an integrated GPU. IntegratedGpu = INTEGRATED_GPU, /// The device is a discrete GPU. DiscreteGpu = DISCRETE_GPU, /// The device is a virtual GPU. VirtualGpu = VIRTUAL_GPU, /// The device is a CPU. Cpu = CPU, /// The device is something else. Other = OTHER, } impl Default for PhysicalDeviceType { #[inline] fn default() -> Self { PhysicalDeviceType::Other } } /// The version of the Vulkan conformance test that a driver is conformant against. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ConformanceVersion { pub major: u8, pub minor: u8, pub subminor: u8, pub patch: u8, } impl From for ConformanceVersion { #[inline] fn from(val: ash::vk::ConformanceVersion) -> Self { ConformanceVersion { major: val.major, minor: val.minor, subminor: val.subminor, patch: val.patch, } } } impl Debug for ConformanceVersion { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!(f, "{}.{}.{}", self.major, self.minor, self.patch) } } impl Display for ConformanceVersion { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { Debug::fmt(self, f) } } vulkan_enum! { #[non_exhaustive] /// An identifier for the driver of a physical device. DriverId = DriverId(i32); // TODO: document AMDProprietary = AMD_PROPRIETARY, // TODO: document AMDOpenSource = AMD_OPEN_SOURCE, // TODO: document MesaRADV = MESA_RADV, // TODO: document NvidiaProprietary = NVIDIA_PROPRIETARY, // TODO: document IntelProprietaryWindows = INTEL_PROPRIETARY_WINDOWS, // TODO: document IntelOpenSourceMesa = INTEL_OPEN_SOURCE_MESA, // TODO: document ImaginationProprietary = IMAGINATION_PROPRIETARY, // TODO: document QualcommProprietary = QUALCOMM_PROPRIETARY, // TODO: document ARMProprietary = ARM_PROPRIETARY, // TODO: document GoogleSwiftshader = GOOGLE_SWIFTSHADER, // TODO: document GGPProprietary = GGP_PROPRIETARY, // TODO: document BroadcomProprietary = BROADCOM_PROPRIETARY, // TODO: document MesaLLVMpipe = MESA_LLVMPIPE, // TODO: document MoltenVK = MOLTENVK, // TODO: document CoreAVIProprietary = COREAVI_PROPRIETARY, // TODO: document JuiceProprietary = JUICE_PROPRIETARY, // TODO: document VeriSiliconPropertary = VERISILICON_PROPRIETARY, // TODO: document MesaTurnip = MESA_TURNIP, // TODO: document MesaV3DV = MESA_V3DV, // TODO: document MesaPanVK = MESA_PANVK, // TODO: document SamsungProprietary = SAMSUNG_PROPRIETARY, // TODO: document MesaVenus = MESA_VENUS, // TODO: document MesaDozen = MESA_DOZEN, } /// Information provided about an active tool. #[derive(Clone, Debug)] #[non_exhaustive] pub struct ToolProperties { /// The name of the tool. pub name: String, /// The version of the tool. pub version: String, /// The purposes supported by the tool. pub purposes: ToolPurposes, /// A description of the tool. pub description: String, /// The layer implementing the tool, or empty if it is not implemented by a layer. pub layer: String, } vulkan_bitflags! { #[non_exhaustive] /// The purpose of an active tool. ToolPurposes = ToolPurposeFlags(u32); /// The tool provides validation of API usage. VALIDATION = VALIDATION, /// The tool provides profiling of API usage. PROFILING = PROFILING, /// The tool is capturing data about the application's API usage. TRACING = TRACING, /// The tool provides additional API features or extensions on top of the underlying /// implementation. ADDITIONAL_FEATURES = ADDITIONAL_FEATURES, /// The tool modifies the API features, limits or extensions presented to the application. MODIFYING_FEATURES = MODIFYING_FEATURES, /// The tool reports information to the user via a /// [`DebugUtilsMessenger`](crate::instance::debug::DebugUtilsMessenger). DEBUG_REPORTING = DEBUG_REPORTING_EXT { instance_extensions: [ext_debug_utils, ext_debug_report], }, /// The tool consumes debug markers or object debug annotation, queue labels or command buffer /// labels. DEBUG_MARKERS = DEBUG_MARKERS_EXT { device_extensions: [ext_debug_marker], instance_extensions: [ext_debug_utils], }, } vulkan_bitflags! { #[non_exhaustive] /// Specifies which subgroup operations are supported. SubgroupFeatures = SubgroupFeatureFlags(u32); // TODO: document BASIC = BASIC, // TODO: document VOTE = VOTE, // TODO: document ARITHMETIC = ARITHMETIC, // TODO: document BALLOT = BALLOT, // TODO: document SHUFFLE = SHUFFLE, // TODO: document SHUFFLE_RELATIVE = SHUFFLE_RELATIVE, // TODO: document CLUSTERED = CLUSTERED, // TODO: document QUAD = QUAD, // TODO: document PARTITIONED = PARTITIONED_NV { device_extensions: [nv_shader_subgroup_partitioned], }, } vulkan_enum! { #[non_exhaustive] /// Specifies how the device clips single point primitives. PointClippingBehavior = PointClippingBehavior(i32); /// Points are clipped if they lie outside any clip plane, both those bounding the view volume /// and user-defined clip planes. AllClipPlanes = ALL_CLIP_PLANES, /// Points are clipped only if they lie outside a user-defined clip plane. UserClipPlanesOnly = USER_CLIP_PLANES_ONLY, } vulkan_enum! { #[non_exhaustive] /// Specifies whether, and how, shader float controls can be set independently. ShaderFloatControlsIndependence = ShaderFloatControlsIndependence(i32); // TODO: document Float32Only = TYPE_32_ONLY, // TODO: document All = ALL, // TODO: document None = NONE, } /// Specifies shader core properties. #[derive(Clone, Copy, Debug)] pub struct ShaderCoreProperties {} impl From for ShaderCoreProperties { #[inline] fn from(_val: ash::vk::ShaderCorePropertiesFlagsAMD) -> Self { Self {} } } vulkan_bitflags! { #[non_exhaustive] // TODO: document MemoryDecompressionMethods = MemoryDecompressionMethodFlagsNV(u64); // TODO: document GDEFLATE_1_0 = GDEFLATE_1_0, } vulkan_bitflags! { #[non_exhaustive] // TODO: document OpticalFlowGridSizes = OpticalFlowGridSizeFlagsNV(u32); // TODO: document SIZE_1X1 = TYPE_1X1, // TODO: document SIZE_2X2 = TYPE_2X2, // TODO: document SIZE_4X4 = TYPE_4X4, // TODO: document SIZE_8X8 = TYPE_8X8, } vulkan_enum! { #[non_exhaustive] // TODO: document PipelineRobustnessBufferBehavior = PipelineRobustnessBufferBehaviorEXT(i32); // TODO: document DeviceDefault = DEVICE_DEFAULT, // TODO: document Disabled = DISABLED, // TODO: document RobustBufferAccess = ROBUST_BUFFER_ACCESS, // TODO: document RobustBufferAccess2 = ROBUST_BUFFER_ACCESS_2, } vulkan_enum! { #[non_exhaustive] // TODO: document PipelineRobustnessImageBehavior = PipelineRobustnessImageBehaviorEXT(i32); // TODO: document DeviceDefault = DEVICE_DEFAULT, // TODO: document Disabled = DISABLED, // TODO: document RobustImageAccess = ROBUST_IMAGE_ACCESS, // TODO: document RobustImageAccess2 = ROBUST_IMAGE_ACCESS_2, } vulkan_enum! { #[non_exhaustive] // TODO: document RayTracingInvocationReorderMode = RayTracingInvocationReorderModeNV(i32); // TODO: document None = NONE, // TODO: document Reorder = REORDER, } /// Error that can happen when using a physical device. #[derive(Clone, Debug, PartialEq, Eq)] pub enum PhysicalDeviceError { VulkanError(VulkanError), RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, // The given `SurfaceInfo` values are not supported for the surface by the physical device. NotSupported, /// The provided `queue_family_index` was not less than the number of queue families in the /// physical device. QueueFamilyIndexOutOfRange { queue_family_index: u32, queue_family_count: u32, }, // The provided `surface` is not supported by any of the physical device's queue families. SurfaceNotSupported, } impl Error for PhysicalDeviceError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::VulkanError(err) => Some(err), _ => None, } } } impl Display for PhysicalDeviceError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::VulkanError(_) => write!(f, "a runtime error occurred"), Self::RequirementNotMet { required_for, requires_one_of, } => write!( f, "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), Self::NotSupported => write!( f, "the given `SurfaceInfo` values are not supported for the surface by the physical \ device", ), Self::QueueFamilyIndexOutOfRange { queue_family_index, queue_family_count, } => write!( f, "the provided `queue_family_index` ({}) was not less than the number of queue \ families in the physical device ({})", queue_family_index, queue_family_count, ), Self::SurfaceNotSupported => write!( f, "the provided `surface` is not supported by any of the physical device's queue families", ), } } } impl From for PhysicalDeviceError { fn from(err: VulkanError) -> Self { Self::VulkanError(err) } } impl From for PhysicalDeviceError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } }