blob: 1d83545bcc9cd30818f0e0057b5849c7e63f14bd [file] [log] [blame]
// 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};