vulkano/
library.rs

1//! Vulkan library loading system.
2//!
3//! Before Vulkano can do anything, it first needs to find a library containing an implementation
4//! of Vulkan. A Vulkan implementation is defined as a single `vkGetInstanceProcAddr` function,
5//! which can be accessed through the `Loader` trait.
6//!
7//! This module provides various implementations of the `Loader` trait.
8//!
9//! Once you have a type that implements `Loader`, you can create a `VulkanLibrary`
10//! from it and use this `VulkanLibrary` struct to build an `Instance`.
11
12pub 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/// A loaded library containing a valid Vulkan implementation.
30#[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    /// Loads the default Vulkan library for this system.
42    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                // Stock macOS no longer has `/usr/local/lib` in `LD_LIBRARY_PATH` like it used to,
65                // but libraries (including MoltenVK installed through the Vulkan SDK) are still
66                // installed here. Try the absolute path as a last resort.
67                "/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    /// Loads a custom Vulkan library.
88    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        // Per the Vulkan spec:
112        // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
113        // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
114        // to determine the version of Vulkan.
115
116        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    /// Returns pointers to the raw global Vulkan functions of the library.
181    #[inline]
182    pub fn fns(&self) -> &EntryFunctions {
183        &self.fns
184    }
185
186    /// Returns the highest Vulkan version that is supported for instances.
187    #[inline]
188    pub fn api_version(&self) -> Version {
189        self.api_version
190    }
191
192    /// Returns the extension properties reported by the core library.
193    #[inline]
194    pub fn extension_properties(&self) -> &[ExtensionProperties] {
195        &self.extension_properties
196    }
197
198    /// Returns the extensions that are supported by the core library.
199    #[inline]
200    pub fn supported_extensions(&self) -> &InstanceExtensions {
201        &self.supported_extensions
202    }
203
204    /// Returns the list of layers that are available when creating an instance.
205    ///
206    /// On success, this function returns an iterator that produces
207    /// [`LayerProperties`] objects. In order to enable a layer,
208    /// you need to pass its name (returned by `LayerProperties::name()`) when creating the
209    /// [`Instance`](crate::instance::Instance).
210    ///
211    /// > **Note**: The available layers may change between successive calls to this function, so
212    /// > each call may return different results. It is possible that one of the layers enumerated
213    /// > here is no longer available when you create the `Instance`. This will lead to an error
214    /// > when calling `Instance::new`.
215    ///
216    /// # Examples
217    ///
218    /// ```no_run
219    /// use vulkano::VulkanLibrary;
220    ///
221    /// let library = VulkanLibrary::new().unwrap();
222    ///
223    /// for layer in library.layer_properties().unwrap() {
224    ///     println!("Available layer: {}", layer.name());
225    /// }
226    /// ```
227    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    /// Returns the extension properties that are reported by the given layer.
259    #[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    /// Returns the extensions that are supported by the given layer.
268    #[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    /// Returns the union of the extensions that are supported by the core library and all
281    /// the given layers.
282    #[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    /// Calls `get_instance_proc_addr` on the underlying loader.
296    #[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
306/// Implemented on objects that grant access to a Vulkan implementation.
307pub unsafe trait Loader: Send + Sync {
308    /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same.
309    ///
310    /// The returned function must stay valid for as long as `self` is alive.
311    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
338/// Implementation of `Loader` that loads Vulkan from a dynamic library.
339pub struct DynamicLibraryLoader {
340    _vk_lib: Library,
341    get_instance_proc_addr: ash::vk::PFN_vkGetInstanceProcAddr,
342}
343
344impl DynamicLibraryLoader {
345    /// Tries to load the dynamic library at the given path, and tries to
346    /// load `vkGetInstanceProcAddr` in it.
347    ///
348    /// # Safety
349    ///
350    /// - The dynamic library must be a valid Vulkan implementation.
351    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/// Expression that returns a loader that assumes that Vulkan is linked to the executable you're
377/// compiling.
378///
379/// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr`
380/// symbol.
381///
382/// This is provided as a macro and not as a regular function, because the macro contains an
383/// `extern {}` block.
384// TODO: should this be unsafe?
385#[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/// Error that can happen when loading a Vulkan library.
411#[derive(Debug)]
412pub enum LoadingError {
413    /// Failed to load the Vulkan shared library.
414    LibraryLoadFailure(LibloadingError),
415
416    /// The Vulkan driver returned an error and was unable to complete the operation.
417    VulkanError(VulkanError),
418}
419
420impl Error for LoadingError {
421    fn source(&self) -> Option<&(dyn Error + 'static)> {
422        match self {
423            //Self::LibraryLoadFailure(err) => Some(err),
424            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}