use super::CallError;
use crate as vk;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FeatureValue {
pub structure: vk::StructureType,
pub member: &'static str,
pub enabled: bool,
}
#[derive(Debug)]
pub struct Features {
values: Vec<FeatureValue>,
structures: Vec<vk::StructureType>,
unrecognized: Vec<vk::StructureType>,
}
impl Features {
pub fn values(&self) -> &[FeatureValue] {
&self.values
}
pub fn unrecognized_structures(&self) -> &[vk::StructureType] {
&self.unrecognized
}
pub fn enabled(&self, structure: vk::StructureType, member: &str) -> Option<bool> {
self
.values
.iter()
.find(|value| value.structure == structure && value.member == member)
.map(|value| value.enabled)
}
pub fn require(
&self,
requirements: &[(vk::StructureType, &'static str)],
) -> Result<(), CallError> {
for &(structure, member) in requirements {
if self.enabled(structure, member) != Some(true) {
return Err(CallError::FeatureNotEnabled { structure, member });
}
}
Ok(())
}
pub(super) fn record(
&mut self,
structure: vk::StructureType,
members: &[(&'static str, vk::Bool32)],
) -> Result<(), CallError> {
if self.structures.contains(&structure) {
return Err(CallError::InvalidMetadata(
"duplicate enabled-feature record",
));
}
self
.structures
.try_reserve(1)
.map_err(|_| CallError::HostAllocationFailed)?;
self
.values
.try_reserve(members.len())
.map_err(|_| CallError::HostAllocationFailed)?;
self.structures.push(structure);
self
.values
.extend(members.iter().map(|&(member, value)| FeatureValue {
structure,
member,
enabled: value != vk::FALSE,
}));
Ok(())
}
pub(super) unsafe fn capture(info: &vk::DeviceCreateInfo) -> Result<Self, CallError> {
const MAX_NODES: usize = 1024;
let mut result = Self {
values: Vec::new(),
structures: Vec::new(),
unrecognized: Vec::new(),
};
let mut visited = Vec::new();
let mut node = info.p_next.cast::<vk::BaseInStructure>();
let mut features2 = false;
while !node.is_null() {
if visited.len() == MAX_NODES || visited.contains(&node) {
return Err(CallError::InvalidMetadata(
"cyclic or oversized device feature chain",
));
}
visited
.try_reserve(1)
.map_err(|_| CallError::HostAllocationFailed)?;
visited.push(node);
let header = unsafe { &*node };
if header.s_type == vk::StructureType::PHYSICAL_DEVICE_FEATURES_2 {
if !info.p_enabled_features.is_null() {
return Err(CallError::InvalidMetadata(
"Features2 and p_enabled_features are mutually exclusive",
));
}
features2 = true;
}
if !unsafe { super::feature_generated::capture_node(node, &mut result) }? {
result
.unrecognized
.try_reserve(1)
.map_err(|_| CallError::HostAllocationFailed)?;
result.unrecognized.push(header.s_type);
}
node = header.p_next;
}
if !features2 {
let defaults = unsafe { std::mem::zeroed::<vk::PhysicalDeviceFeatures>() };
let base = unsafe { info.p_enabled_features.as_ref() }.unwrap_or(&defaults);
super::feature_generated::capture_base(base, &mut result)?;
}
Ok(result)
}
}
#[cfg(test)]
#[path = "../../../test/rs/load/test_feature.rs"]
mod tests;