Skip to main content

vk/load/
feature.rs

1//! Owned enabled feature records, independent of their input pointer chains.
2use super::CallError;
3use crate as vk;
4
5/// One explicitly captured native feature member. Names use the registry's C
6/// spelling, so `shaderInt64` remains recognizable in specification diagnostics.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct FeatureValue {
9	pub structure: vk::StructureType,
10	pub member: &'static str,
11	pub enabled: bool,
12}
13
14/// Snapshot of exact feature records used to create a device.
15///
16/// Individual extension and unified core records are distinct representations;
17/// this snapshot does not invent equivalence between equal member names. Native
18/// type aliases share the same sType. Legacy base features are represented by
19/// `PHYSICAL_DEVICE_FEATURES_2`, including default-false base features.
20#[derive(Debug)]
21pub struct Features {
22	values: Vec<FeatureValue>,
23	structures: Vec<vk::StructureType>,
24	unrecognized: Vec<vk::StructureType>,
25}
26
27impl Features {
28	/// Input records follow chain order; legacy/default base features follow the
29	/// chain. Members within each record follow official XML declaration order.
30	pub fn values(&self) -> &[FeatureValue] {
31		&self.values
32	}
33
34	/// Exact input records not recognized as feature records by this build.
35	/// This includes ordinary non-feature device-create records. Their presence
36	/// is not a feature claim, and their payload is never interpreted or retained.
37	pub fn unrecognized_structures(&self) -> &[vk::StructureType] {
38		&self.unrecognized
39	}
40
41	/// Read an explicitly recorded member by its exact native representation.
42	/// None means unrecorded/unknown, including alternate unified representations.
43	/// A false value is an explicitly captured disabled bit, not inferred absence.
44	pub fn enabled(&self, structure: vk::StructureType, member: &str) -> Option<bool> {
45		self
46			.values
47			.iter()
48			.find(|value| value.structure == structure && value.member == member)
49			.map(|value| value.enabled)
50	}
51
52	/// Admit a caller-declared set of exact feature-record requirements.
53	///
54	/// # Errors
55	/// Returns FeatureNotEnabled for the first disabled or unrecorded member.
56	/// This checks enabled bits only; API/extension support, limits, native object
57	/// state and argument-dependent valid usage remain separate obligations.
58	pub fn require(
59		&self,
60		requirements: &[(vk::StructureType, &'static str)],
61	) -> Result<(), CallError> {
62		for &(structure, member) in requirements {
63			if self.enabled(structure, member) != Some(true) {
64				return Err(CallError::FeatureNotEnabled { structure, member });
65			}
66		}
67		Ok(())
68	}
69
70	pub(super) fn record(
71		&mut self,
72		structure: vk::StructureType,
73		members: &[(&'static str, vk::Bool32)],
74	) -> Result<(), CallError> {
75		if self.structures.contains(&structure) {
76			return Err(CallError::InvalidMetadata(
77				"duplicate enabled-feature record",
78			));
79		}
80		self
81			.structures
82			.try_reserve(1)
83			.map_err(|_| CallError::HostAllocationFailed)?;
84		self
85			.values
86			.try_reserve(members.len())
87			.map_err(|_| CallError::HostAllocationFailed)?;
88		self.structures.push(structure);
89		self
90			.values
91			.extend(members.iter().map(|&(member, value)| FeatureValue {
92				structure,
93				member,
94				enabled: value != vk::FALSE,
95			}));
96		Ok(())
97	}
98
99	/// Caller guarantees every reachable pointer is valid, aligned, readable and
100	/// identifies the complete native record declared by its sType.
101	pub(super) unsafe fn capture(info: &vk::DeviceCreateInfo) -> Result<Self, CallError> {
102		const MAX_NODES: usize = 1024;
103		let mut result = Self {
104			values: Vec::new(),
105			structures: Vec::new(),
106			unrecognized: Vec::new(),
107		};
108		let mut visited = Vec::new();
109		let mut node = info.p_next.cast::<vk::BaseInStructure>();
110		let mut features2 = false;
111		while !node.is_null() {
112			if visited.len() == MAX_NODES || visited.contains(&node) {
113				return Err(CallError::InvalidMetadata(
114					"cyclic or oversized device feature chain",
115				));
116			}
117			visited
118				.try_reserve(1)
119				.map_err(|_| CallError::HostAllocationFailed)?;
120			visited.push(node);
121			// SAFETY: caller guarantees a readable header matching the complete record.
122			let header = unsafe { &*node };
123			if header.s_type == vk::StructureType::PHYSICAL_DEVICE_FEATURES_2 {
124				if !info.p_enabled_features.is_null() {
125					return Err(CallError::InvalidMetadata(
126						"Features2 and p_enabled_features are mutually exclusive",
127					));
128				}
129				features2 = true;
130			}
131			// SAFETY: complete record validity is the caller's explicit contract.
132			if !unsafe { super::feature_generated::capture_node(node, &mut result) }? {
133				result
134					.unrecognized
135					.try_reserve(1)
136					.map_err(|_| CallError::HostAllocationFailed)?;
137				result.unrecognized.push(header.s_type);
138			}
139			node = header.p_next;
140		}
141		if !features2 {
142			// SAFETY: the generated base record consists entirely of VkBool32 fields.
143			let defaults = unsafe { std::mem::zeroed::<vk::PhysicalDeviceFeatures>() };
144			// SAFETY: caller supplies a valid optional base-feature record.
145			let base = unsafe { info.p_enabled_features.as_ref() }.unwrap_or(&defaults);
146			super::feature_generated::capture_base(base, &mut result)?;
147		}
148		Ok(result)
149	}
150}
151
152#[cfg(test)]
153#[path = "../../../test/rs/load/test_feature.rs"]
154mod tests;