diff options
Diffstat (limited to 'src/macros.rs')
-rw-r--r-- | src/macros.rs | 947 |
1 files changed, 947 insertions, 0 deletions
diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..ee57133 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,947 @@ +// Copyright (c) 2022 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. + +macro_rules! vulkan_bitflags { + { + $(#[doc = $ty_doc:literal])* + $ty:ident + $( impl { $($impls:item)* } )? + = $ty_ffi:ident($repr:ty); + + $( + $(#[doc = $flag_doc:literal])* + $flag_name:ident = $flag_name_ffi:ident, + )+ + } => { + $(#[doc = $ty_doc])* + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct $ty($repr); + + impl $ty { + $( + $(#[doc = $flag_doc])* + pub const $flag_name: Self = Self(ash::vk::$ty_ffi::$flag_name_ffi.as_raw()); + )* + + #[doc = concat!("Returns a `", stringify!($ty), "` with none of the flags set.")] + #[inline] + pub const fn empty() -> Self { + Self(0) + } + + #[deprecated(since = "0.31.0", note = "Use `empty` instead.")] + #[doc = concat!("Returns a `", stringify!($ty), "` with none of the flags set.")] + #[inline] + pub const fn none() -> Self { + Self::empty() + } + + #[doc = concat!("Returns a `", stringify!($ty), "` with all of the flags set.")] + #[inline] + pub const fn all() -> Self { + Self(Self::all_raw()) + } + + const fn all_raw() -> $repr { + 0 + $( + | ash::vk::$ty_ffi::$flag_name_ffi.as_raw() + )* + } + + /// Returns whether no flags are set in `self`. + #[inline] + pub const fn is_empty(self) -> bool { + self.0 == 0 + } + + /// Returns whether any flags are set in both `self` and `other`. + #[inline] + pub const fn intersects(self, #[allow(unused_variables)] other: Self) -> bool { + self.0 & other.0 != 0 + } + + /// Returns whether all flags in `other` are set in `self`. + #[inline] + pub const fn contains(self, #[allow(unused_variables)] other: Self) -> bool { + self.0 & other.0 == other.0 + } + + /// Returns the union of `self` and `other`. + #[inline] + pub const fn union(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 | other.0) + } + + /// Returns the intersection of `self` and `other`. + #[inline] + pub const fn intersection(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 & other.0) + } + + /// Returns `self` without the flags set in `other`. + #[inline] + pub const fn difference(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 & !other.0) + } + + /// Returns the flags that are set in `self` or `other`, but not in both. + #[inline] + pub const fn symmetric_difference(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 ^ other.0) + } + + /// Returns the flags not in `self`. + #[inline] + pub const fn complement(self) -> Self { + Self(!self.0 & Self::all_raw()) + } + + $( $($impls)* )? + } + + impl Default for $ty { + #[inline] + fn default() -> Self { + Self::empty() + } + } + + impl std::fmt::Debug for $ty { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + #[allow(unused_mut)] + let mut written = false; + + $( + if self.intersects(Self::$flag_name) { + if written { + write!(f, " | ")?; + } + + write!(f, stringify!($flag_name))?; + written = true; + } + )* + + if !written { + write!(f, "empty()")?; + } + + Ok(()) + } + } + + impl From<$ty> for ash::vk::$ty_ffi { + #[inline] + fn from(val: $ty) -> Self { + ash::vk::$ty_ffi::from_raw(val.0) + } + } + + impl From<ash::vk::$ty_ffi> for $ty { + #[inline] + fn from(val: ash::vk::$ty_ffi) -> Self { + Self(val.as_raw() & Self::all_raw()) + } + } + + impl std::ops::BitAnd for $ty { + type Output = Self; + + #[inline] + fn bitand(self, rhs: Self) -> Self { + self.intersection(rhs) + } + } + + impl std::ops::BitAndAssign for $ty { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = self.intersection(rhs); + } + } + + impl std::ops::BitOr for $ty { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + self.union(rhs) + } + } + + impl std::ops::BitOrAssign for $ty { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = self.union(rhs); + } + } + + impl std::ops::BitXor for $ty { + type Output = Self; + + #[inline] + fn bitxor(self, rhs: Self) -> Self { + self.symmetric_difference(rhs) + } + } + + impl std::ops::BitXorAssign for $ty { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = self.symmetric_difference(rhs); + } + } + + impl std::ops::Sub for $ty { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + self.difference(rhs) + } + } + + impl std::ops::SubAssign for $ty { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = self.difference(rhs); + } + } + + impl std::ops::Not for $ty { + type Output = Self; + + #[inline] + fn not(self) -> Self { + self.complement() + } + } + }; + + { + #[non_exhaustive] + + $(#[doc = $ty_doc:literal])* + $ty:ident + $( impl { $($impls:item)* } )? + = $ty_ffi:ident($repr:ty); + + $( + $(#[doc = $flag_doc:literal])* + $flag_name:ident = $flag_name_ffi:ident + $({ + $(api_version: $api_version:ident,)? + $(features: [$($feature:ident),+ $(,)?],)? + $(device_extensions: [$($device_extension:ident),+ $(,)?],)? + $(instance_extensions: [$($instance_extension:ident),+ $(,)?],)? + })? + , + )* + } => { + $(#[doc = $ty_doc])* + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct $ty($repr); + + impl $ty { + $( + $(#[doc = $flag_doc])* + pub const $flag_name: Self = Self(ash::vk::$ty_ffi::$flag_name_ffi.as_raw()); + )* + + #[doc = concat!("Returns a `", stringify!($ty), "` with none of the flags set.")] + #[inline] + pub const fn empty() -> Self { + Self(0) + } + + #[deprecated(since = "0.31.0", note = "Use `empty` instead.")] + #[doc = concat!("Returns a `", stringify!($ty), "` with none of the flags set.")] + #[inline] + pub const fn none() -> Self { + Self::empty() + } + + const fn all_raw() -> $repr { + 0 + $( + | ash::vk::$ty_ffi::$flag_name_ffi.as_raw() + )* + } + + /// Returns the number of flags set in self. + #[inline] + pub const fn count(self) -> u32 { + self.0.count_ones() + } + + /// Returns whether no flags are set in `self`. + #[inline] + pub const fn is_empty(self) -> bool { + self.0 == 0 + } + + /// Returns whether any flags are set in both `self` and `other`. + #[inline] + pub const fn intersects(self, #[allow(unused_variables)] other: Self) -> bool { + self.0 & other.0 != 0 + } + + /// Returns whether all flags in `other` are set in `self`. + #[inline] + pub const fn contains(self, #[allow(unused_variables)] other: Self) -> bool { + self.0 & other.0 == other.0 + } + + /// Returns the union of `self` and `other`. + #[inline] + pub const fn union(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 | other.0) + } + + /// Returns the intersection of `self` and `other`. + #[inline] + pub const fn intersection(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 & other.0) + } + + /// Returns `self` without the flags set in `other`. + #[inline] + pub const fn difference(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 & !other.0) + } + + /// Returns the flags that are set in `self` or `other`, but not in both. + #[inline] + pub const fn symmetric_difference(self, #[allow(unused_variables)] other: Self) -> Self { + Self(self.0 ^ other.0) + } + + #[allow(dead_code)] + pub(crate) fn validate_device( + self, + #[allow(unused_variables)] device: &crate::device::Device, + ) -> Result<(), crate::RequirementNotMet> { + $( + $( + if self.intersects(Self::$flag_name) && ![ + $( + device.api_version() >= crate::Version::$api_version, + )? + $($( + device.enabled_features().$feature, + )+)? + $($( + device.enabled_extensions().$device_extension, + )+)? + $($( + device.instance().enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(features: &[$(stringify!($feature)),+],)? + $(device_extensions: &[$(stringify!($device_extension)),+],)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + )? + )* + + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn validate_physical_device( + self, + #[allow(unused_variables)] physical_device: &crate::device::physical::PhysicalDevice, + ) -> Result<(), crate::RequirementNotMet> { + $( + $( + if self.intersects(Self::$flag_name) && ![ + $( + physical_device.api_version() >= crate::Version::$api_version, + )? + $($( + physical_device.supported_features().$feature, + )+)? + $($( + physical_device.supported_extensions().$device_extension, + )+)? + $($( + physical_device.instance().enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(features: &[$(stringify!($feature)),+],)? + $(device_extensions: &[$(stringify!($device_extension)),+],)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + )? + )* + + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn validate_instance( + self, + #[allow(unused_variables)] instance: &crate::instance::Instance, + ) -> Result<(), crate::RequirementNotMet> { + $( + $( + if self.intersects(Self::$flag_name) && ![ + $( + instance.api_version() >= crate::Version::$api_version, + )? + $($( + instance.enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + )? + )* + + Ok(()) + } + + $( $($impls)* )? + } + + impl Default for $ty { + #[inline] + fn default() -> Self { + Self::empty() + } + } + + impl std::fmt::Debug for $ty { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + #[allow(unused_mut)] + let mut written = false; + + $( + if self.intersects(Self::$flag_name) { + if written { + write!(f, " | ")?; + } + + write!(f, stringify!($flag_name))?; + written = true; + } + )* + + if !written { + write!(f, "empty()")?; + } + + Ok(()) + } + } + + impl From<$ty> for ash::vk::$ty_ffi { + #[inline] + fn from(val: $ty) -> Self { + ash::vk::$ty_ffi::from_raw(val.0) + } + } + + impl From<ash::vk::$ty_ffi> for $ty { + #[inline] + fn from(val: ash::vk::$ty_ffi) -> Self { + Self(val.as_raw() & Self::all_raw()) + } + } + + impl std::ops::BitAnd for $ty { + type Output = Self; + + #[inline] + fn bitand(self, rhs: Self) -> Self { + self.intersection(rhs) + } + } + + impl std::ops::BitAndAssign for $ty { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = self.intersection(rhs); + } + } + + impl std::ops::BitOr for $ty { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + self.union(rhs) + } + } + + impl std::ops::BitOrAssign for $ty { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = self.union(rhs); + } + } + + impl std::ops::BitXor for $ty { + type Output = Self; + + #[inline] + fn bitxor(self, rhs: Self) -> Self { + self.symmetric_difference(rhs) + } + } + + impl std::ops::BitXorAssign for $ty { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = self.symmetric_difference(rhs); + } + } + + impl std::ops::Sub for $ty { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + self.difference(rhs) + } + } + + impl std::ops::SubAssign for $ty { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = self.difference(rhs); + } + } + }; +} + +macro_rules! vulkan_enum { + { + $(#[doc = $ty_doc:literal])* + $ty:ident + $( impl { $($impls:item)* } )? + = $ty_ffi:ident($repr:ty); + + $( + $(#[doc = $flag_doc:literal])* + $flag_name:ident = $flag_name_ffi:ident, + )+ + } => { + $(#[doc = $ty_doc])* + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[repr($repr)] + pub enum $ty { + $( + $(#[doc = $flag_doc])* + $flag_name = ash::vk::$ty_ffi::$flag_name_ffi.as_raw(), + )+ + } + + $( + impl $ty { + $($impls)* + } + )? + + impl From<$ty> for ash::vk::$ty_ffi { + #[inline] + fn from(val: $ty) -> Self { + ash::vk::$ty_ffi::from_raw(val as $repr) + } + } + + impl TryFrom<ash::vk::$ty_ffi> for $ty { + type Error = (); + + #[inline] + fn try_from(val: ash::vk::$ty_ffi) -> Result<Self, Self::Error> { + Ok(match val { + $( + ash::vk::$ty_ffi::$flag_name_ffi => Self::$flag_name, + )+ + _ => return Err(()), + }) + } + } + }; + + { + #[non_exhaustive] + + $(#[doc = $ty_doc:literal])* + $ty:ident + $( impl { $($impls:item)* } )? + = $ty_ffi:ident($repr:ty); + + $( + $(#[doc = $flag_doc:literal])* + $flag_name:ident = $flag_name_ffi:ident + $({ + $(api_version: $api_version:ident,)? + $(features: [$($feature:ident),+ $(,)?],)? + $(device_extensions: [$($device_extension:ident),+ $(,)?],)? + $(instance_extensions: [$($instance_extension:ident),+ $(,)?],)? + })? + , + )+ + } => { + $(#[doc = $ty_doc])* + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[non_exhaustive] + #[repr($repr)] + pub enum $ty { + $( + $(#[doc = $flag_doc])* + $flag_name = ash::vk::$ty_ffi::$flag_name_ffi.as_raw(), + )+ + } + + impl $ty { + #[allow(dead_code)] + pub(crate) fn validate_device( + self, + #[allow(unused_variables)] device: &crate::device::Device, + ) -> Result<(), crate::RequirementNotMet> { + match self { + $( + $( + Self::$flag_name => { + if ![ + $( + device.api_version() >= crate::Version::$api_version, + )? + $($( + device.enabled_features().$feature, + )+)? + $($( + device.enabled_extensions().$device_extension, + )+)? + $($( + device.instance().enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(features: &[$(stringify!($feature)),+],)? + $(device_extensions: &[$(stringify!($device_extension)),+],)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + }, + )? + )+ + _ => (), + } + + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn validate_physical_device( + self, + #[allow(unused_variables)] physical_device: &crate::device::physical::PhysicalDevice, + ) -> Result<(), crate::RequirementNotMet> { + match self { + $( + $( + Self::$flag_name => { + if ![ + $( + physical_device.api_version() >= crate::Version::$api_version, + )? + $($( + physical_device.supported_features().$feature, + )+)? + $($( + physical_device.supported_extensions().$device_extension, + )+)? + $($( + physical_device.instance().enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(features: &[$(stringify!($feature)),+],)? + $(device_extensions: &[$(stringify!($device_extension)),+],)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + }, + )? + )+ + _ => (), + } + + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn validate_instance( + self, + #[allow(unused_variables)] instance: &crate::instance::Instance, + ) -> Result<(), crate::RequirementNotMet> { + match self { + $( + $( + Self::$flag_name => { + if ![ + $( + instance.api_version() >= crate::Version::$api_version, + )? + $($( + instance.enabled_extensions().$instance_extension, + )+)? + ].into_iter().any(|x| x) { + return Err(crate::RequirementNotMet { + required_for: concat!("`", stringify!($ty), "::", stringify!($flag_name), "`"), + requires_one_of: crate::RequiresOneOf { + $(api_version: Some(crate::Version::$api_version),)? + $(instance_extensions: &[$(stringify!($instance_extension)),+],)? + ..Default::default() + }, + }); + } + }, + )? + )+ + _ => (), + } + + Ok(()) + } + + $( + $($impls)* + )? + } + + impl From<$ty> for ash::vk::$ty_ffi { + #[inline] + fn from(val: $ty) -> Self { + ash::vk::$ty_ffi::from_raw(val as $repr) + } + } + + impl TryFrom<ash::vk::$ty_ffi> for $ty { + type Error = (); + + #[inline] + fn try_from(val: ash::vk::$ty_ffi) -> Result<Self, Self::Error> { + Ok(match val { + $( + ash::vk::$ty_ffi::$flag_name_ffi => Self::$flag_name, + )+ + _ => return Err(()), + }) + } + } + }; +} + +macro_rules! vulkan_bitflags_enum { + { + #[non_exhaustive] + + $(#[doc = $ty_bitflags_doc:literal])* + $ty_bitflags:ident + $( impl { $($impls_bitflags:item)* } )? + , + + $(#[doc = $ty_enum_doc:literal])* + $ty_enum:ident + $( impl { $($impls_enum:item)* } )? + , + + = $ty_ffi:ident($repr:ty); + + $( + $(#[doc = $flag_doc:literal])* + $flag_name_bitflags:ident, $flag_name_enum:ident = $flag_name_ffi:ident + $({ + $(api_version: $api_version:ident,)? + $(features: [$($feature:ident),+ $(,)?],)? + $(device_extensions: [$($device_extension:ident),+ $(,)?],)? + $(instance_extensions: [$($instance_extension:ident),+ $(,)?],)? + })? + , + )* + } => { + crate::macros::vulkan_bitflags! { + #[non_exhaustive] + + $(#[doc = $ty_bitflags_doc])* + $ty_bitflags + impl { + /// Returns whether `self` contains the flag corresponding to `val`. + #[inline] + pub fn contains_enum(self, val: $ty_enum) -> bool { + self.intersects(val.into()) + } + + $( $($impls_bitflags)* )? + } + = $ty_ffi($repr); + + $( + $(#[doc = $flag_doc])* + $flag_name_bitflags = $flag_name_ffi + $({ + $(api_version: $api_version,)? + $(features: [$($feature),+],)? + $(device_extensions: [$($device_extension),+],)? + $(instance_extensions: [$($instance_extension),+],)? + })? + , + )* + } + + crate::macros::vulkan_enum! { + #[non_exhaustive] + + $(#[doc = $ty_enum_doc])* + $ty_enum + $( impl { $($impls_enum)* } )? + = $ty_ffi($repr); + + $( + $(#[doc = $flag_doc])* + $flag_name_enum = $flag_name_ffi + $({ + $(api_version: $api_version,)? + $(features: [$($feature),+],)? + $(device_extensions: [$($device_extension),+],)? + $(instance_extensions: [$($instance_extension),+],)? + })? + , + )* + } + + impl From<$ty_enum> for $ty_bitflags { + #[inline] + fn from(val: $ty_enum) -> Self { + Self(val as $repr) + } + } + + impl FromIterator<$ty_enum> for $ty_bitflags { + #[inline] + fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item = $ty_enum> { + iter.into_iter().map(|item| Self::from(item)).fold(Self::empty(), |r, i| r.union(i)) + } + } + + impl IntoIterator for $ty_bitflags { + type Item = $ty_enum; + type IntoIter = std::iter::Flatten< + std::array::IntoIter< + Option<Self::Item>, + { $ty_bitflags::all_raw().count_ones() as usize }, + > + >; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + [ + $( + self.intersects(Self::$flag_name_bitflags) + .then_some($ty_enum::$flag_name_enum), + )* + ].into_iter().flatten() + } + } + } +} + +macro_rules! impl_id_counter { + ($type:ident $(< $($param:ident $(: $bound:ident $(+ $bounds:ident)* )?),+ >)?) => { + $crate::macros::impl_id_counter!( + @inner $type $(< $($param),+ >)?, $( $($param $(: $bound $(+ $bounds)* )?),+)? + ); + }; + ($type:ident $(< $($param:ident $(: $bound:ident $(+ $bounds:ident)* )? + ?Sized),+ >)?) => { + $crate::macros::impl_id_counter!( + @inner $type $(< $($param),+ >)?, $( $($param $(: $bound $(+ $bounds)* )? + ?Sized),+)? + ); + }; + (@inner $type:ident $(< $($param:ident),+ >)?, $($bounds:tt)*) => { + impl< $($bounds)* > $type $(< $($param),+ >)? { + fn next_id() -> std::num::NonZeroU64 { + use std::{ + num::NonZeroU64, + sync::atomic::{AtomicU64, Ordering}, + }; + + static COUNTER: AtomicU64 = AtomicU64::new(1); + + NonZeroU64::new(COUNTER.fetch_add(1, Ordering::Relaxed)).unwrap_or_else(|| { + println!("an ID counter has overflown ...somehow"); + std::process::abort(); + }) + } + } + + impl< $($bounds)* > PartialEq for $type $(< $($param),+ >)? { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } + } + + impl< $($bounds)* > Eq for $type $(< $($param),+ >)? {} + + impl< $($bounds)* > std::hash::Hash for $type $(< $($param),+ >)? { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.id.hash(state); + } + } + }; +} + +// TODO: Replace with the `?` operator once its constness is stabilized. +macro_rules! try_opt { + ($e:expr) => { + if let Some(val) = $e { + val + } else { + return None; + } + }; +} + +pub(crate) use {impl_id_counter, try_opt, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}; |