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