vulkano/
library.rs

1// Copyright (c) 2016 The vulkano developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! Vulkan library loading system.
11//!
12//! Before Vulkano can do anything, it first needs to find a library containing an implementation
13//! of Vulkan. A Vulkan implementation is defined as a single `vkGetInstanceProcAddr` function,
14//! which can be accessed through the `Loader` trait.
15//!
16//! This module provides various implementations of the `Loader` trait.
17//!
18//! Once you have a type that implements `Loader`, you can create a `VulkanLibrary`
19//! from it and use this `VulkanLibrary` struct to build an `Instance`.
20
21pub 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/// A loaded library containing a valid Vulkan implementation.
39#[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    /// Loads the default Vulkan library for this system.
51    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    /// Loads a custom Vulkan library.
101    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        // Per the Vulkan spec:
126        // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
127        // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
128        // to determine the version of Vulkan.
129
130        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    /// Returns pointers to the raw global Vulkan functions of the library.
188    #[inline]
189    pub fn fns(&self) -> &EntryFunctions {
190        &self.fns
191    }
192
193    /// Returns the highest Vulkan version that is supported for instances.
194    #[inline]
195    pub fn api_version(&self) -> Version {
196        self.api_version
197    }
198
199    /// Returns the extension properties reported by the core library.
200    #[inline]
201    pub fn extension_properties(&self) -> &[ExtensionProperties] {
202        &self.extension_properties
203    }
204
205    /// Returns the extensions that are supported by the core library.
206    #[inline]
207    pub fn supported_extensions(&self) -> &InstanceExtensions {
208        &self.supported_extensions
209    }
210
211    /// Returns the list of layers that are available when creating an instance.
212    ///
213    /// On success, this function returns an iterator that produces
214    /// [`LayerProperties`] objects. In order to enable a layer,
215    /// you need to pass its name (returned by `LayerProperties::name()`) when creating the
216    /// [`Instance`](crate::instance::Instance).
217    ///
218    /// > **Note**: The available layers may change between successive calls to this function, so
219    /// > each call may return different results. It is possible that one of the layers enumerated
220    /// > here is no longer available when you create the `Instance`. This will lead to an error
221    /// > when calling `Instance::new`.
222    ///
223    /// # Examples
224    ///
225    /// ```no_run
226    /// use vulkano::VulkanLibrary;
227    ///
228    /// let library = VulkanLibrary::new().unwrap();
229    ///
230    /// for layer in library.layer_properties().unwrap() {
231    ///     println!("Available layer: {}", layer.name());
232    /// }
233    /// ```
234    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    /// Returns the extension properties that are reported by the given layer.
269    #[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    /// Returns the extensions that are supported by the given layer.
278    #[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    /// Returns the union of the extensions that are supported by the core library and all
291    /// the given layers.
292    #[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    /// Calls `get_instance_proc_addr` on the underlying loader.
306    #[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
316/// Implemented on objects that grant access to a Vulkan implementation.
317pub unsafe trait Loader: Send + Sync {
318    /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same.
319    ///
320    /// The returned function must stay valid for as long as `self` is alive.
321    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
348/// Implementation of `Loader` that loads Vulkan from a dynamic library.
349pub struct DynamicLibraryLoader {
350    _vk_lib: Library,
351    get_instance_proc_addr: ash::vk::PFN_vkGetInstanceProcAddr,
352}
353
354impl DynamicLibraryLoader {
355    /// Tries to load the dynamic library at the given path, and tries to
356    /// load `vkGetInstanceProcAddr` in it.
357    ///
358    /// # Safety
359    ///
360    /// - The dynamic library must be a valid Vulkan implementation.
361    ///
362    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/// Expression that returns a loader that assumes that Vulkan is linked to the executable you're
388/// compiling.
389///
390/// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr`
391/// symbol.
392///
393/// This is provided as a macro and not as a regular function, because the macro contains an
394/// `extern {}` block.
395// TODO: should this be unsafe?
396#[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/// Error that can happen when loading a Vulkan library.
422#[derive(Debug)]
423pub enum LoadingError {
424    /// Failed to load the Vulkan shared library.
425    LibraryLoadFailure(LibloadingError),
426
427    /// The Vulkan driver returned an error and was unable to complete the operation.
428    VulkanError(VulkanError),
429}
430
431impl Error for LoadingError {
432    fn source(&self) -> Option<&(dyn Error + 'static)> {
433        match self {
434            //Self::LibraryLoadFailure(err) => Some(err),
435            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}