diff options
Diffstat (limited to 'src/device/features.rs')
-rw-r--r-- | src/device/features.rs | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/src/device/features.rs b/src/device/features.rs new file mode 100644 index 0000000..1d83545 --- /dev/null +++ b/src/device/features.rs @@ -0,0 +1,290 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// 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<ash::vk::PhysicalDeviceFeatures2KHR>, + + $( + $member: Option<ash::vk::$ty>, + )+ + } + + 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}; |