1use crate::Instance;
3use crate::{self as vk, make_api_version};
4use libloading::Library;
5use std::{
6 ffi::{CStr, OsStr},
7 sync::Arc,
8};
9
10#[derive(Debug, PartialEq)]
12pub enum LoadError {
13 InvalidMetadata(&'static str),
14 LibraryOpen(String),
16 MissingSymbol(&'static str),
18 NullHandle(&'static str),
20}
21
22impl std::fmt::Display for LoadError {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 match self {
25 Self::InvalidMetadata(s) => write!(f, "invalid Vulkan import metadata: {s}"),
26 Self::LibraryOpen(s) => write!(f, "Vulkan loader: {s}"),
27 Self::MissingSymbol(s) => write!(f, "missing Vulkan command: {s}"),
28 Self::NullHandle(s) => write!(f, "null Vulkan {s} handle"),
29 }
30 }
31}
32
33impl std::error::Error for LoadError {}
34
35#[derive(Debug, PartialEq)]
37pub enum CallError {
38 Construction(vk::ConstructionError),
40 EnumerationUnstable {
42 attempts: u32,
43 },
44 InitializationFailed(super::InitializationFailure),
47 Vulkan(vk::Result),
48 MissingCommand(&'static str),
49 InvalidMetadata(&'static str),
51 FeatureNotEnabled {
53 structure: vk::StructureType,
54 member: &'static str,
55 },
56 HostAllocationFailed,
57 InvalidOutput(&'static str),
58 CountOverflow(usize),
59 SliceLengthMismatch {
61 parameter: &'static str,
62 expected: usize,
63 actual: usize,
64 },
65}
66
67impl std::fmt::Display for CallError {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 Self::Construction(error) => error.fmt(f),
71 Self::EnumerationUnstable { attempts } => write!(
72 f,
73 "Vulkan enumeration did not stabilize after {attempts} attempts"
74 ),
75 Self::InitializationFailed(failure) => failure.fmt(f),
76 Self::Vulkan(r) => write!(f, "Vulkan result {}", r.as_raw()),
77 Self::MissingCommand(n) => write!(f, "missing Vulkan command: {n}"),
78 Self::InvalidMetadata(reason) => write!(f, "invalid Vulkan metadata: {reason}"),
79 Self::FeatureNotEnabled { structure, member } => write!(
80 f,
81 "Vulkan feature {structure:?}::{member} is not recorded as enabled"
82 ),
83 Self::HostAllocationFailed => f.write_str("Vulkan host allocation failed"),
84 Self::InvalidOutput(reason) => write!(f, "invalid Vulkan output: {reason}"),
85 Self::CountOverflow(count) => write!(f, "Vulkan count exceeds u32: {count}"),
86 Self::SliceLengthMismatch {
87 parameter,
88 expected,
89 actual,
90 } => {
91 write!(f, "Vulkan {parameter} length {actual}, expected {expected}")
92 }
93 }
94 }
95}
96
97impl std::error::Error for CallError {
98 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
99 match self {
100 Self::Construction(error) => Some(error),
101 Self::InitializationFailed(failure) => Some(failure),
102 _ => None,
103 }
104 }
105}
106
107impl From<vk::ConstructionError> for CallError {
108 fn from(error: vk::ConstructionError) -> Self {
109 Self::Construction(error)
110 }
111}
112
113impl From<vk::Result> for CallError {
114 fn from(r: vk::Result) -> Self {
119 Self::Vulkan(r)
120 }
121}
122
123#[derive(Clone)]
125pub struct Entry {
126 pub(crate) library: Option<Arc<Library>>,
127 pub(crate) gipa: unsafe extern "system" fn(
128 vk::InstanceHandle,
129 *const std::ffi::c_char,
130 ) -> vk::PFN_vkVoidFunction,
131 fp: vk::FnEntry,
132}
133
134impl Entry {
135 pub unsafe fn new() -> Result<Self, LoadError> {
141 #[cfg(windows)]
142 let path = "vulkan-1.dll";
143 #[cfg(any(target_os = "macos", target_os = "ios"))]
144 let path = "libvulkan.dylib";
145 #[cfg(any(target_os = "android", target_os = "fuchsia"))]
146 let path = "libvulkan.so";
147 #[cfg(not(any(
148 windows,
149 target_os = "macos",
150 target_os = "ios",
151 target_os = "android",
152 target_os = "fuchsia"
153 )))]
154 let path = "libvulkan.so.1";
155 unsafe { Self::new_from_path(path) }
157 }
158
159 pub unsafe fn new_from_path(path: impl AsRef<OsStr>) -> Result<Self, LoadError> {
165 let library =
167 Arc::new(unsafe { Library::new(path) }.map_err(|e| LoadError::LibraryOpen(e.to_string()))?);
168 let gipa = unsafe {
170 library.get::<unsafe extern "system" fn(
171 vk::InstanceHandle,
172 *const std::ffi::c_char,
173 ) -> vk::PFN_vkVoidFunction>(b"vkGetInstanceProcAddr\0")
174 }
175 .map_err(|_| LoadError::MissingSymbol("vkGetInstanceProcAddr"))?;
176 let mut entry = unsafe { Self::new_from_resolver(*gipa) }?;
178 entry.library = Some(library);
179 Ok(entry)
180 }
181
182 pub unsafe fn new_from_resolver(
188 gipa: unsafe extern "system" fn(
189 vk::InstanceHandle,
190 *const std::ffi::c_char,
191 ) -> vk::PFN_vkVoidFunction,
192 ) -> Result<Self, LoadError> {
193 let fp = unsafe { vk::FnEntry::new_from_resolver(gipa) };
195 for (present, name) in [
196 (fp.create_instance.is_some(), "vkCreateInstance"),
197 (
198 fp.enumerate_instance_extension_properties.is_some(),
199 "vkEnumerateInstanceExtensionProperties",
200 ),
201 (
202 fp.enumerate_instance_layer_properties.is_some(),
203 "vkEnumerateInstanceLayerProperties",
204 ),
205 ] {
206 if !present {
207 return Err(LoadError::MissingSymbol(name));
208 }
209 }
210 Ok(Self {
211 library: None,
212 gipa,
213 fp,
214 })
215 }
216
217 pub fn fp(&self) -> &vk::FnEntry {
219 &self.fp
220 }
221
222 #[doc(alias = "vkGetInstanceProcAddr")]
228 #[doc = "\n\n[Khronos reference: `vkGetInstanceProcAddr`](https://docs.vulkan.org/refpages/latest/refpages/source/vkGetInstanceProcAddr.html)"]
229 pub unsafe fn get_instance_proc_addr(
230 &self,
231 instance: vk::InstanceHandle,
232 name: &CStr,
233 ) -> vk::PFN_vkVoidFunction {
234 unsafe { (self.gipa)(instance, name.as_ptr()) }
236 }
237
238 #[doc(alias = "vkCreateInstance")]
244 #[doc = "\n\n[Khronos reference: `vkCreateInstance`](https://docs.vulkan.org/refpages/latest/refpages/source/vkCreateInstance.html)"]
245 pub unsafe fn create_instance(
246 &self,
247 create_info: &vk::InstanceCreateInfo,
248 allocator: Option<&vk::AllocationCallbacks>,
249 ) -> Result<Instance, CallError> {
250 unsafe { crate::create_instance(self, create_info, allocator) }
252 }
253}
254
255use crate::command::commands;
256commands! { Entry, entry;
257 #[doc(alias = "vkEnumerateInstanceExtensionProperties")]
266 #[doc = "\n\n[Khronos reference: `vkEnumerateInstanceExtensionProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkEnumerateInstanceExtensionProperties.html)"]
267 pub fn enumerate_instance_extension_properties(
268 layer_name: Option<&std::ffi::CStr>,
269 ) -> Result<Vec<vk::ExtensionProperties>, CallError> {
270 let f = entry
271 .fp
272 .enumerate_instance_extension_properties
273 .ok_or(CallError::MissingCommand(
274 "vkEnumerateInstanceExtensionProperties",
275 ))?;
276 let layer_ptr = layer_name.map_or(std::ptr::null(), |l| l.as_ptr());
277 unsafe { super::enumerate::enumerate(|count, data| f(layer_ptr, count, data)) }
279 }
280
281 #[doc(alias = "vkEnumerateInstanceLayerProperties")]
287 #[doc = "\n\n[Khronos reference: `vkEnumerateInstanceLayerProperties`](https://docs.vulkan.org/refpages/latest/refpages/source/vkEnumerateInstanceLayerProperties.html)"]
288 pub fn enumerate_instance_layer_properties() -> Result<Vec<vk::LayerProperties>, CallError> {
289 let f = entry
290 .fp
291 .enumerate_instance_layer_properties
292 .ok_or(CallError::MissingCommand(
293 "vkEnumerateInstanceLayerProperties",
294 ))?;
295 unsafe { super::enumerate::enumerate(|count, data| f(count, data)) }
297 }
298
299 #[doc(alias = "vkEnumerateInstanceVersion")]
301 #[doc = "\n\n[Khronos reference: `vkEnumerateInstanceVersion`](https://docs.vulkan.org/refpages/latest/refpages/source/vkEnumerateInstanceVersion.html)"]
302 pub fn enumerate_instance_version() -> Result<u32, vk::Result> {
303 let Some(f) = entry.fp.enumerate_instance_version else {
304 return Ok(make_api_version(0, 1, 0, 0));
305 };
306 let mut version = 0;
307 let result = unsafe { f(&mut version) };
309 if result == vk::Result::SUCCESS {
310 Ok(version)
311 } else {
312 Err(result)
313 }
314 }
315}