1use super::{CallError, Features};
3use crate::core::{self as vk, CapabilityRequirement, CommandMetadata};
4use std::{
5 ffi::{CStr, c_char},
6 sync::Arc,
7};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum CommandSupport {
13 Enabled,
14 Disabled,
15 RequiresPhysicalDevice,
17}
18
19#[derive(Debug)]
23pub struct Capabilities {
24 api_version: u32,
25 instance_api_version: u32,
26 instance_extensions: Arc<Vec<Vec<u8>>>,
27 device_extensions: Option<Vec<Vec<u8>>>,
28 features: Option<Features>,
29}
30
31impl Capabilities {
32 pub fn api_version(&self) -> u32 {
35 self.api_version
36 }
37
38 pub fn instance_api_version(&self) -> u32 {
41 self.instance_api_version
42 }
43
44 pub(crate) fn limit_instance_version(&mut self, version: u32) -> Result<(), CallError> {
45 self.instance_api_version = self.api_version.min(normalize_version(version)?);
46 Ok(())
47 }
48
49 pub fn instance_extensions(&self) -> impl ExactSizeIterator<Item = &CStr> {
51 self.instance_extensions.iter().map(|bytes| {
52 unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
54 })
55 }
56
57 pub fn device_extensions(&self) -> Option<impl ExactSizeIterator<Item = &CStr>> {
59 self.device_extensions.as_ref().map(|extensions| {
60 extensions.iter().map(|bytes| {
61 unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }
63 })
64 })
65 }
66
67 pub fn features(&self) -> Option<&Features> {
69 self.features.as_ref()
70 }
71
72 pub unsafe fn new_from_instance_create_info(
82 info: &vk::InstanceCreateInfo,
83 implementation_version: u32,
84 ) -> Result<Self, CallError> {
85 let mut capabilities = unsafe { Self::instance(info) }?;
87 capabilities.limit_instance_version(implementation_version)?;
88 Ok(capabilities)
89 }
90
91 pub unsafe fn new_from_device_create_info(
103 &self,
104 info: &vk::DeviceCreateInfo,
105 physical_version: u32,
106 ) -> Result<Self, CallError> {
107 if self.device_extensions.is_some() {
108 return Err(CallError::InvalidMetadata(
109 "device capabilities cannot be an instance parent",
110 ));
111 }
112 unsafe { self.device(info, physical_version) }
114 }
115
116 pub(crate) fn matches_instance(&self, parent: &Self) -> bool {
117 !parent.is_device()
118 && self.api_version <= parent.api_version
119 && self.instance_api_version == parent.instance_api_version
120 && self
121 .instance_extensions()
122 .all(|name| parent.instance_extensions().any(|other| name == other))
123 && parent
124 .instance_extensions()
125 .all(|name| self.instance_extensions().any(|other| name == other))
126 }
127
128 pub(crate) fn is_device(&self) -> bool {
129 self.device_extensions.is_some()
130 }
131
132 pub fn command_support(&self, name: &CStr) -> Option<CommandSupport> {
137 vk::COMMANDS
138 .iter()
139 .find(|command| command.name.as_bytes() == name.to_bytes())
140 .map(|command| self.support(command))
141 }
142
143 pub(crate) fn support(&self, command: &CommandMetadata) -> CommandSupport {
144 use CommandSupport::*;
145 let api_version = if command.dispatch == "Instance" && !command.physical_device {
146 self.instance_api_version
147 } else {
148 self.api_version
149 };
150 let mut result = Disabled;
151 for clause in command.requirements {
152 let mut support = Enabled;
153 for atom in *clause {
154 let value = match atom {
155 CapabilityRequirement::Core(version) => {
156 if api_version >= *version {
157 if command.physical_device
158 && self.device_extensions.is_none()
159 && *version > vk::API_VERSION_1_0
160 {
161 RequiresPhysicalDevice
162 } else {
163 Enabled
164 }
165 } else {
166 Disabled
167 }
168 }
169 CapabilityRequirement::InstanceExtension(name) => {
170 if self.instance_extensions().any(|enabled| enabled == *name) {
171 Enabled
172 } else {
173 Disabled
174 }
175 }
176 CapabilityRequirement::DeviceExtension(name) => match self.device_extensions() {
177 Some(mut extensions) => {
178 if extensions.any(|enabled| enabled == *name) {
179 Enabled
180 } else {
181 Disabled
182 }
183 }
184 None => RequiresPhysicalDevice,
185 },
186 };
187 if value == Disabled {
188 support = Disabled;
189 break;
190 }
191 if value == RequiresPhysicalDevice {
192 support = RequiresPhysicalDevice;
193 }
194 }
195 if support == Enabled {
196 return Enabled;
197 }
198 if support == RequiresPhysicalDevice {
199 result = RequiresPhysicalDevice;
200 }
201 }
202 result
203 }
204
205 pub(crate) unsafe fn instance(info: &vk::InstanceCreateInfo) -> Result<Self, CallError> {
208 let requested = unsafe { info.p_application_info.as_ref() }.map_or(0, |app| app.api_version);
210 let api_version = normalize_version(if requested == 0 {
211 vk::API_VERSION_1_0
212 } else {
213 requested
214 })?;
215 let extensions = unsafe {
217 capture(
218 info.enabled_extension_count,
219 info.pp_enabled_extension_names,
220 )
221 }?;
222 Ok(Self {
223 api_version,
224 instance_api_version: api_version,
225 instance_extensions: Arc::new(extensions),
226 device_extensions: None,
227 features: None,
228 })
229 }
230
231 pub(crate) unsafe fn device(
234 &self,
235 info: &vk::DeviceCreateInfo,
236 physical_version: u32,
237 ) -> Result<Self, CallError> {
238 let api_version = self.api_version.min(normalize_version(physical_version)?);
239 let extensions = unsafe {
241 capture(
242 info.enabled_extension_count,
243 info.pp_enabled_extension_names,
244 )
245 }?;
246 Ok(Self {
247 api_version,
248 instance_api_version: self.instance_api_version,
249 instance_extensions: Arc::clone(&self.instance_extensions),
250 device_extensions: Some(extensions),
251 features: Some(unsafe { Features::capture(info) }?),
253 })
254 }
255}
256
257fn normalize_version(version: u32) -> Result<u32, CallError> {
258 if vk::api_version_variant(version) != 0 || version < vk::API_VERSION_1_0 {
259 return Err(CallError::InvalidOutput("unsupported Vulkan API version"));
260 }
261 Ok(version & !0xfff)
262}
263
264unsafe fn capture(count: u32, names: *const *const c_char) -> Result<Vec<Vec<u8>>, CallError> {
265 if count != 0 && names.is_null() {
266 return Err(CallError::InvalidOutput("null enabled-extension array"));
267 }
268 let mut result = Vec::new();
269 result
270 .try_reserve_exact(count as usize)
271 .map_err(|_| CallError::HostAllocationFailed)?;
272 for index in 0..count as usize {
273 let name = unsafe { *names.add(index) };
275 if name.is_null() {
276 return Err(CallError::InvalidOutput("null enabled-extension name"));
277 }
278 let bytes = unsafe { CStr::from_ptr(name) }.to_bytes_with_nul();
280 let mut owned = Vec::new();
281 owned
282 .try_reserve_exact(bytes.len())
283 .map_err(|_| CallError::HostAllocationFailed)?;
284 owned.extend_from_slice(bytes);
285 result.push(owned);
286 }
287 Ok(result)
288}
289
290#[cfg(test)]
291#[path = "../../../test/rs/load/test_capability.rs"]
292mod tests;