1pub use crate::fns::EntryFunctions;
22use crate::{
23 instance::{InstanceExtensions, LayerProperties},
24 ExtensionProperties, SafeDeref, Version, VulkanError,
25};
26use libloading::{Error as LibloadingError, Library};
27use std::{
28 error::Error,
29 ffi::{CStr, CString},
30 fmt::{Debug, Display, Error as FmtError, Formatter},
31 mem::transmute,
32 os::raw::c_char,
33 path::Path,
34 ptr,
35 sync::Arc,
36};
37
38#[derive(Debug)]
40pub struct VulkanLibrary {
41 loader: Box<dyn Loader>,
42 fns: EntryFunctions,
43
44 api_version: Version,
45 extension_properties: Vec<ExtensionProperties>,
46 supported_extensions: InstanceExtensions,
47}
48
49impl VulkanLibrary {
50 pub fn new() -> Result<Arc<Self>, LoadingError> {
52 #[cfg(target_os = "ios")]
53 #[allow(non_snake_case)]
54 fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> {
55 let loader = crate::statically_linked_vulkan_loader!();
56
57 Ok(Box::new(loader))
58 }
59
60 #[cfg(not(target_os = "ios"))]
61 fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> {
62 #[cfg(windows)]
63 fn get_paths() -> [&'static Path; 1] {
64 [Path::new("vulkan-1.dll")]
65 }
66 #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
67 fn get_paths() -> [&'static Path; 1] {
68 [Path::new("libvulkan.so.1")]
69 }
70 #[cfg(target_os = "macos")]
71 fn get_paths() -> [&'static Path; 3] {
72 [
73 Path::new("libvulkan.dylib"),
74 Path::new("libvulkan.1.dylib"),
75 Path::new("libMoltenVK.dylib"),
76 ]
77 }
78 #[cfg(target_os = "android")]
79 fn get_paths() -> [&'static Path; 2] {
80 [Path::new("libvulkan.so.1"), Path::new("libvulkan.so")]
81 }
82
83 let paths = get_paths();
84
85 let mut err: Option<LoadingError> = None;
86
87 for path in paths {
88 match unsafe { DynamicLibraryLoader::new(path) } {
89 Ok(library) => return Ok(Box::new(library)),
90 Err(e) => err = Some(e),
91 }
92 }
93
94 Err(err.unwrap())
95 }
96
97 def_loader_impl().and_then(VulkanLibrary::with_loader)
98 }
99
100 pub fn with_loader(loader: impl Loader + 'static) -> Result<Arc<Self>, LoadingError> {
102 let fns = EntryFunctions::load(|name| unsafe {
103 loader
104 .get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr())
105 .map_or(ptr::null(), |func| func as _)
106 });
107
108 let api_version = unsafe { Self::get_api_version(&loader)? };
109 let extension_properties = unsafe { Self::get_extension_properties(&fns, None)? };
110 let supported_extensions = extension_properties
111 .iter()
112 .map(|property| property.extension_name.as_str())
113 .collect();
114
115 Ok(Arc::new(VulkanLibrary {
116 loader: Box::new(loader),
117 fns,
118 api_version,
119 extension_properties,
120 supported_extensions,
121 }))
122 }
123
124 unsafe fn get_api_version(loader: &impl Loader) -> Result<Version, VulkanError> {
125 let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0");
131 let func = loader.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr());
132
133 let version = if let Some(func) = func {
134 let func: ash::vk::PFN_vkEnumerateInstanceVersion = transmute(func);
135 let mut api_version = 0;
136 func(&mut api_version).result().map_err(VulkanError::from)?;
137 Version::from(api_version)
138 } else {
139 Version {
140 major: 1,
141 minor: 0,
142 patch: 0,
143 }
144 };
145
146 Ok(version)
147 }
148
149 unsafe fn get_extension_properties(
150 fns: &EntryFunctions,
151 layer: Option<&str>,
152 ) -> Result<Vec<ExtensionProperties>, VulkanError> {
153 let layer_vk = layer.map(|layer| CString::new(layer).unwrap());
154
155 loop {
156 let mut count = 0;
157 (fns.v1_0.enumerate_instance_extension_properties)(
158 layer_vk
159 .as_ref()
160 .map_or(ptr::null(), |layer| layer.as_ptr()),
161 &mut count,
162 ptr::null_mut(),
163 )
164 .result()
165 .map_err(VulkanError::from)?;
166
167 let mut output = Vec::with_capacity(count as usize);
168 let result = (fns.v1_0.enumerate_instance_extension_properties)(
169 layer_vk
170 .as_ref()
171 .map_or(ptr::null(), |layer| layer.as_ptr()),
172 &mut count,
173 output.as_mut_ptr(),
174 );
175
176 match result {
177 ash::vk::Result::SUCCESS => {
178 output.set_len(count as usize);
179 return Ok(output.into_iter().map(Into::into).collect());
180 }
181 ash::vk::Result::INCOMPLETE => (),
182 err => return Err(VulkanError::from(err)),
183 }
184 }
185 }
186
187 #[inline]
189 pub fn fns(&self) -> &EntryFunctions {
190 &self.fns
191 }
192
193 #[inline]
195 pub fn api_version(&self) -> Version {
196 self.api_version
197 }
198
199 #[inline]
201 pub fn extension_properties(&self) -> &[ExtensionProperties] {
202 &self.extension_properties
203 }
204
205 #[inline]
207 pub fn supported_extensions(&self) -> &InstanceExtensions {
208 &self.supported_extensions
209 }
210
211 pub fn layer_properties(
235 &self,
236 ) -> Result<impl ExactSizeIterator<Item = LayerProperties>, VulkanError> {
237 let fns = self.fns();
238
239 let layer_properties = unsafe {
240 loop {
241 let mut count = 0;
242 (fns.v1_0.enumerate_instance_layer_properties)(&mut count, ptr::null_mut())
243 .result()
244 .map_err(VulkanError::from)?;
245
246 let mut properties = Vec::with_capacity(count as usize);
247 let result = (fns.v1_0.enumerate_instance_layer_properties)(
248 &mut count,
249 properties.as_mut_ptr(),
250 );
251
252 match result {
253 ash::vk::Result::SUCCESS => {
254 properties.set_len(count as usize);
255 break properties;
256 }
257 ash::vk::Result::INCOMPLETE => (),
258 err => return Err(VulkanError::from(err)),
259 }
260 }
261 };
262
263 Ok(layer_properties
264 .into_iter()
265 .map(|p| LayerProperties { props: p }))
266 }
267
268 #[inline]
270 pub fn layer_extension_properties(
271 &self,
272 layer: &str,
273 ) -> Result<Vec<ExtensionProperties>, VulkanError> {
274 unsafe { Self::get_extension_properties(&self.fns, Some(layer)) }
275 }
276
277 #[inline]
279 pub fn supported_layer_extensions(
280 &self,
281 layer: &str,
282 ) -> Result<InstanceExtensions, VulkanError> {
283 Ok(self
284 .layer_extension_properties(layer)?
285 .iter()
286 .map(|property| property.extension_name.as_str())
287 .collect())
288 }
289
290 #[inline]
293 pub fn supported_extensions_with_layers<'a>(
294 &self,
295 layers: impl IntoIterator<Item = &'a str>,
296 ) -> Result<InstanceExtensions, VulkanError> {
297 layers
298 .into_iter()
299 .try_fold(self.supported_extensions, |extensions, layer| {
300 self.supported_layer_extensions(layer)
301 .map(|layer_extensions| extensions.union(&layer_extensions))
302 })
303 }
304
305 #[inline]
307 pub unsafe fn get_instance_proc_addr(
308 &self,
309 instance: ash::vk::Instance,
310 name: *const c_char,
311 ) -> ash::vk::PFN_vkVoidFunction {
312 self.loader.get_instance_proc_addr(instance, name)
313 }
314}
315
316pub unsafe trait Loader: Send + Sync {
318 unsafe fn get_instance_proc_addr(
322 &self,
323 instance: ash::vk::Instance,
324 name: *const c_char,
325 ) -> ash::vk::PFN_vkVoidFunction;
326}
327
328unsafe impl<T> Loader for T
329where
330 T: SafeDeref + Send + Sync,
331 T::Target: Loader,
332{
333 unsafe fn get_instance_proc_addr(
334 &self,
335 instance: ash::vk::Instance,
336 name: *const c_char,
337 ) -> ash::vk::PFN_vkVoidFunction {
338 (**self).get_instance_proc_addr(instance, name)
339 }
340}
341
342impl Debug for dyn Loader {
343 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
344 f.debug_struct("Loader").finish_non_exhaustive()
345 }
346}
347
348pub struct DynamicLibraryLoader {
350 _vk_lib: Library,
351 get_instance_proc_addr: ash::vk::PFN_vkGetInstanceProcAddr,
352}
353
354impl DynamicLibraryLoader {
355 pub unsafe fn new(path: impl AsRef<Path>) -> Result<DynamicLibraryLoader, LoadingError> {
363 let vk_lib = Library::new(path.as_ref()).map_err(LoadingError::LibraryLoadFailure)?;
364
365 let get_instance_proc_addr = *vk_lib
366 .get(b"vkGetInstanceProcAddr")
367 .map_err(LoadingError::LibraryLoadFailure)?;
368
369 Ok(DynamicLibraryLoader {
370 _vk_lib: vk_lib,
371 get_instance_proc_addr,
372 })
373 }
374}
375
376unsafe impl Loader for DynamicLibraryLoader {
377 #[inline]
378 unsafe fn get_instance_proc_addr(
379 &self,
380 instance: ash::vk::Instance,
381 name: *const c_char,
382 ) -> ash::vk::PFN_vkVoidFunction {
383 (self.get_instance_proc_addr)(instance, name)
384 }
385}
386
387#[macro_export]
397macro_rules! statically_linked_vulkan_loader {
398 () => {{
399 extern "C" {
400 fn vkGetInstanceProcAddr(
401 instance: ash::vk::Instance,
402 pName: *const c_char,
403 ) -> ash::vk::PFN_vkVoidFunction;
404 }
405
406 struct StaticallyLinkedVulkanLoader;
407 unsafe impl Loader for StaticallyLinkedVulkanLoader {
408 unsafe fn get_instance_proc_addr(
409 &self,
410 instance: ash::vk::Instance,
411 name: *const c_char,
412 ) -> ash::vk::PFN_vkVoidFunction {
413 vkGetInstanceProcAddr(instance, name)
414 }
415 }
416
417 StaticallyLinkedVulkanLoader
418 }};
419}
420
421#[derive(Debug)]
423pub enum LoadingError {
424 LibraryLoadFailure(LibloadingError),
426
427 VulkanError(VulkanError),
429}
430
431impl Error for LoadingError {
432 fn source(&self) -> Option<&(dyn Error + 'static)> {
433 match self {
434 Self::VulkanError(err) => Some(err),
436 _ => None,
437 }
438 }
439}
440
441impl Display for LoadingError {
442 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
443 match self {
444 Self::LibraryLoadFailure(_) => write!(f, "failed to load the Vulkan shared library"),
445 Self::VulkanError(err) => write!(f, "a runtime error occurred: {err}"),
446 }
447 }
448}
449
450impl From<VulkanError> for LoadingError {
451 fn from(err: VulkanError) -> Self {
452 Self::VulkanError(err)
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::{DynamicLibraryLoader, LoadingError};
459
460 #[test]
461 fn dl_open_error() {
462 unsafe {
463 match DynamicLibraryLoader::new("_non_existing_library.void") {
464 Err(LoadingError::LibraryLoadFailure(_)) => (),
465 _ => panic!(),
466 }
467 }
468 }
469}