// Copyright (c) 2016 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 std::error; use std::fmt; macro_rules! features { { $($member:ident => { doc: $doc:expr, ffi_name: $ffi_field:ident, ffi_members: [$($ffi_struct:ident $(.$ffi_struct_field:ident)?),+], requires_features: [$($requires_feature:ident),*], conflicts_features: [$($conflicts_feature:ident),*], required_by_extensions: [$($required_by_extension:ident),*], },)* } => { /// Represents all the features that are available on a physical device or enabled on /// a logical device. /// /// Note that the `robust_buffer_access` is guaranteed to be supported by all Vulkan /// implementations. /// /// # Example /// /// ``` /// use vulkano::device::Features; /// # let physical_device: vulkano::device::physical::PhysicalDevice = return; /// let minimal_features = Features { /// geometry_shader: true, /// .. Features::none() /// }; /// /// let optimal_features = vulkano::device::Features { /// geometry_shader: true, /// tessellation_shader: true, /// .. Features::none() /// }; /// /// if !physical_device.supported_features().is_superset_of(&minimal_features) { /// panic!("The physical device is not good enough for this application."); /// } /// /// assert!(optimal_features.is_superset_of(&minimal_features)); /// let features_to_request = optimal_features.intersection(physical_device.supported_features()); /// ``` /// #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] #[allow(missing_docs)] pub struct Features { $( #[doc = $doc] pub $member: bool, )* } impl Features { /// Checks enabled features against the device version, device extensions and each other. pub(super) fn check_requirements( &self, supported: &Features, api_version: crate::Version, extensions: &crate::device::DeviceExtensions, ) -> Result<(), crate::device::features::FeatureRestrictionError> { $( if self.$member { if !supported.$member { return Err(crate::device::features::FeatureRestrictionError { feature: stringify!($member), restriction: crate::device::features::FeatureRestriction::NotSupported, }); } $( if !self.$requires_feature { return Err(crate::device::features::FeatureRestrictionError { feature: stringify!($member), restriction: crate::device::features::FeatureRestriction::RequiresFeature(stringify!($requires_feature)), }); } )* $( if self.$conflicts_feature { return Err(crate::device::features::FeatureRestrictionError { feature: stringify!($member), restriction: crate::device::features::FeatureRestriction::ConflictsFeature(stringify!($conflicts_feature)), }); } )* } else { $( if extensions.$required_by_extension { return Err(crate::device::features::FeatureRestrictionError { feature: stringify!($member), restriction: crate::device::features::FeatureRestriction::RequiredByExtension(stringify!($required_by_extension)), }); } )* } )* Ok(()) } /// Builds a `Features` object with all values to false. pub const fn none() -> Features { Features { $($member: false,)* } } /// Builds a `Features` object with all values to true. /// /// > **Note**: This function is used for testing purposes, and is probably useless in /// > a real code. pub const fn all() -> Features { Features { $($member: true,)* } } /// Returns true if `self` is a superset of the parameter. /// /// That is, for each feature of the parameter that is true, the corresponding value /// in self is true as well. pub fn is_superset_of(&self, other: &Features) -> bool { $((self.$member == true || other.$member == false))&&+ } /// Builds a `Features` that is the intersection of `self` and another `Features` /// object. /// /// The result's field will be true if it is also true in both `self` and `other`. pub fn intersection(&self, other: &Features) -> Features { Features { $($member: self.$member && other.$member,)* } } /// Builds a `Features` that is the difference of another `Features` object from `self`. /// /// The result's field will be true if it is true in `self` but not `other`. pub fn difference(&self, other: &Features) -> Features { Features { $($member: self.$member && !other.$member,)* } } } impl FeaturesFfi { pub(crate) fn write(&mut self, features: &Features) { $( std::array::IntoIter::new([ $(self.$ffi_struct.as_mut().map(|s| &mut s$(.$ffi_struct_field)?.$ffi_field)),+ ]).flatten().next().map(|f| *f = features.$member as ash::vk::Bool32); )* } } impl From<&FeaturesFfi> for Features { fn from(features_ffi: &FeaturesFfi) -> Self { Features { $( $member: std::array::IntoIter::new([ $(features_ffi.$ffi_struct.map(|s| s$(.$ffi_struct_field)?.$ffi_field)),+ ]).flatten().next().unwrap_or(0) != 0, )* } } } }; } pub use crate::autogen::Features; pub(crate) use features; /// An error that can happen when enabling a feature on a device. #[derive(Clone, Copy, Debug)] pub struct FeatureRestrictionError { /// The feature in question. pub feature: &'static str, /// The restriction that was not met. pub restriction: FeatureRestriction, } impl error::Error for FeatureRestrictionError {} impl fmt::Display for FeatureRestrictionError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( fmt, "a restriction for the feature {} was not met: {}", self.feature, self.restriction, ) } } #[derive(Clone, Copy, Debug)] pub enum FeatureRestriction { /// Not supported by the physical device. NotSupported, /// Requires a feature to be enabled. RequiresFeature(&'static str), /// Requires a feature to be disabled. ConflictsFeature(&'static str), /// An extension requires this feature to be enabled. RequiredByExtension(&'static str), } impl fmt::Display for FeatureRestriction { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { FeatureRestriction::NotSupported => { write!(fmt, "not supported by the physical device") } FeatureRestriction::RequiresFeature(feat) => { write!(fmt, "requires feature {} to be enabled", feat) } FeatureRestriction::ConflictsFeature(feat) => { write!(fmt, "requires feature {} to be disabled", feat) } FeatureRestriction::RequiredByExtension(ext) => { write!(fmt, "required to be enabled by extension {}", ext) } } } } macro_rules! features_ffi { { $api_version:ident, $device_extensions:ident, $instance_extensions:ident, $($member:ident => { ty: $ty:ident, provided_by: [$($provided_by:expr),+], conflicts: [$($conflicts:ident),*], },)+ } => { #[derive(Default)] pub(crate) struct FeaturesFfi { features_vulkan10: Option, $( $member: Option, )+ } impl FeaturesFfi { pub(crate) fn make_chain( &mut self, $api_version: crate::Version, $device_extensions: &DeviceExtensions, $instance_extensions: &InstanceExtensions, ) { self.features_vulkan10 = Some(Default::default()); let head = self.features_vulkan10.as_mut().unwrap(); $( if std::array::IntoIter::new([$($provided_by),+]).any(|x| x) && std::array::IntoIter::new([$(self.$conflicts.is_none()),*]).all(|x| x) { self.$member = Some(Default::default()); let member = self.$member.as_mut().unwrap(); member.p_next = head.p_next; head.p_next = member as *mut _ as _; } )+ } pub(crate) fn head_as_ref(&self) -> &ash::vk::PhysicalDeviceFeatures2KHR { self.features_vulkan10.as_ref().unwrap() } pub(crate) fn head_as_mut(&mut self) -> &mut ash::vk::PhysicalDeviceFeatures2KHR { self.features_vulkan10.as_mut().unwrap() } } }; } pub(crate) use {crate::autogen::FeaturesFfi, features_ffi};