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 
21 pub use crate::fns::EntryFunctions;
22 use crate::{
23     instance::{InstanceExtensions, LayerProperties},
24     ExtensionProperties, OomError, SafeDeref, Version, VulkanError,
25 };
26 use libloading::{Error as LibloadingError, Library};
27 use 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)]
40 pub 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 
49 impl VulkanLibrary {
50     /// Loads the default Vulkan library for this system.
new() -> Result<Arc<Self>, LoadingError>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_path() -> &'static Path {
64                 Path::new("vulkan-1.dll")
65             }
66             #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
67             fn get_path() -> &'static Path {
68                 Path::new("libvulkan.so.1")
69             }
70             #[cfg(target_os = "macos")]
71             fn get_path() -> &'static Path {
72                 Path::new("libvulkan.1.dylib")
73             }
74             #[cfg(target_os = "android")]
75             fn get_path() -> &'static Path {
76                 Path::new("libvulkan.so")
77             }
78 
79             let loader = unsafe { DynamicLibraryLoader::new(get_path())? };
80 
81             Ok(Box::new(loader))
82         }
83 
84         def_loader_impl().and_then(VulkanLibrary::with_loader)
85     }
86 
87     /// Loads a custom Vulkan library.
with_loader(loader: impl Loader + 'static) -> Result<Arc<Self>, LoadingError>88     pub fn with_loader(loader: impl Loader + 'static) -> Result<Arc<Self>, LoadingError> {
89         let fns = EntryFunctions::load(|name| unsafe {
90             loader
91                 .get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr())
92                 .map_or(ptr::null(), |func| func as _)
93         });
94 
95         let api_version = unsafe { Self::get_api_version(&loader)? };
96         let extension_properties = unsafe { Self::get_extension_properties(&fns, None)? };
97         let supported_extensions = extension_properties
98             .iter()
99             .map(|property| property.extension_name.as_str())
100             .collect();
101 
102         Ok(Arc::new(VulkanLibrary {
103             loader: Box::new(loader),
104             fns,
105             api_version,
106             extension_properties,
107             supported_extensions,
108         }))
109     }
110 
get_api_version(loader: &impl Loader) -> Result<Version, VulkanError>111     unsafe fn get_api_version(loader: &impl Loader) -> Result<Version, VulkanError> {
112         // Per the Vulkan spec:
113         // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
114         // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
115         // to determine the version of Vulkan.
116 
117         let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0");
118         let func = 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 = transmute(func);
122             let mut api_version = 0;
123             func(&mut api_version).result().map_err(VulkanError::from)?;
124             Version::from(api_version)
125         } else {
126             Version {
127                 major: 1,
128                 minor: 0,
129                 patch: 0,
130             }
131         };
132 
133         Ok(version)
134     }
135 
get_extension_properties( fns: &EntryFunctions, layer: Option<&str>, ) -> Result<Vec<ExtensionProperties>, VulkanError>136     unsafe fn get_extension_properties(
137         fns: &EntryFunctions,
138         layer: Option<&str>,
139     ) -> Result<Vec<ExtensionProperties>, VulkanError> {
140         let layer_vk = layer.map(|layer| CString::new(layer).unwrap());
141 
142         loop {
143             let mut count = 0;
144             (fns.v1_0.enumerate_instance_extension_properties)(
145                 layer_vk
146                     .as_ref()
147                     .map_or(ptr::null(), |layer| layer.as_ptr()),
148                 &mut count,
149                 ptr::null_mut(),
150             )
151             .result()
152             .map_err(VulkanError::from)?;
153 
154             let mut output = Vec::with_capacity(count as usize);
155             let result = (fns.v1_0.enumerate_instance_extension_properties)(
156                 layer_vk
157                     .as_ref()
158                     .map_or(ptr::null(), |layer| layer.as_ptr()),
159                 &mut count,
160                 output.as_mut_ptr(),
161             );
162 
163             match result {
164                 ash::vk::Result::SUCCESS => {
165                     output.set_len(count as usize);
166                     return Ok(output.into_iter().map(Into::into).collect());
167                 }
168                 ash::vk::Result::INCOMPLETE => (),
169                 err => return Err(VulkanError::from(err)),
170             }
171         }
172     }
173 
174     /// Returns pointers to the raw global Vulkan functions of the library.
175     #[inline]
fns(&self) -> &EntryFunctions176     pub fn fns(&self) -> &EntryFunctions {
177         &self.fns
178     }
179 
180     /// Returns the highest Vulkan version that is supported for instances.
181     #[inline]
api_version(&self) -> Version182     pub fn api_version(&self) -> Version {
183         self.api_version
184     }
185 
186     /// Returns the extension properties reported by the core library.
187     #[inline]
extension_properties(&self) -> &[ExtensionProperties]188     pub fn extension_properties(&self) -> &[ExtensionProperties] {
189         &self.extension_properties
190     }
191 
192     /// Returns the extensions that are supported by the core library.
193     #[inline]
supported_extensions(&self) -> &InstanceExtensions194     pub fn supported_extensions(&self) -> &InstanceExtensions {
195         &self.supported_extensions
196     }
197 
198     /// Returns the list of layers that are available when creating an instance.
199     ///
200     /// On success, this function returns an iterator that produces
201     /// [`LayerProperties`](crate::instance::LayerProperties) objects. In order to enable a layer,
202     /// you need to pass its name (returned by `LayerProperties::name()`) when creating the
203     /// [`Instance`](crate::instance::Instance).
204     ///
205     /// > **Note**: The available layers may change between successive calls to this function, so
206     /// > each call may return different results. It is possible that one of the layers enumerated
207     /// > here is no longer available when you create the `Instance`. This will lead to an error
208     /// > when calling `Instance::new`.
209     ///
210     /// # Examples
211     ///
212     /// ```no_run
213     /// use vulkano::VulkanLibrary;
214     ///
215     /// let library = VulkanLibrary::new().unwrap();
216     ///
217     /// for layer in library.layer_properties().unwrap() {
218     ///     println!("Available layer: {}", layer.name());
219     /// }
220     /// ```
layer_properties( &self, ) -> Result<impl ExactSizeIterator<Item = LayerProperties>, OomError>221     pub fn layer_properties(
222         &self,
223     ) -> Result<impl ExactSizeIterator<Item = LayerProperties>, OomError> {
224         let fns = self.fns();
225 
226         let layer_properties = unsafe {
227             loop {
228                 let mut count = 0;
229                 (fns.v1_0.enumerate_instance_layer_properties)(&mut count, ptr::null_mut())
230                     .result()
231                     .map_err(VulkanError::from)?;
232 
233                 let mut properties = Vec::with_capacity(count as usize);
234                 let result = (fns.v1_0.enumerate_instance_layer_properties)(
235                     &mut count,
236                     properties.as_mut_ptr(),
237                 );
238 
239                 match result {
240                     ash::vk::Result::SUCCESS => {
241                         properties.set_len(count as usize);
242                         break properties;
243                     }
244                     ash::vk::Result::INCOMPLETE => (),
245                     err => return Err(VulkanError::from(err).into()),
246                 }
247             }
248         };
249 
250         Ok(layer_properties
251             .into_iter()
252             .map(|p| LayerProperties { props: p }))
253     }
254 
255     /// Returns the extension properties that are reported by the given layer.
256     #[inline]
layer_extension_properties( &self, layer: &str, ) -> Result<Vec<ExtensionProperties>, VulkanError>257     pub fn layer_extension_properties(
258         &self,
259         layer: &str,
260     ) -> Result<Vec<ExtensionProperties>, VulkanError> {
261         unsafe { Self::get_extension_properties(&self.fns, Some(layer)) }
262     }
263 
264     /// Returns the extensions that are supported by the given layer.
265     #[inline]
supported_layer_extensions( &self, layer: &str, ) -> Result<InstanceExtensions, VulkanError>266     pub fn supported_layer_extensions(
267         &self,
268         layer: &str,
269     ) -> Result<InstanceExtensions, VulkanError> {
270         Ok(self
271             .layer_extension_properties(layer)?
272             .iter()
273             .map(|property| property.extension_name.as_str())
274             .collect())
275     }
276 
277     /// Returns the union of the extensions that are supported by the core library and all
278     /// the given layers.
279     #[inline]
supported_extensions_with_layers<'a>( &self, layers: impl IntoIterator<Item = &'a str>, ) -> Result<InstanceExtensions, VulkanError>280     pub fn supported_extensions_with_layers<'a>(
281         &self,
282         layers: impl IntoIterator<Item = &'a str>,
283     ) -> Result<InstanceExtensions, VulkanError> {
284         layers
285             .into_iter()
286             .try_fold(self.supported_extensions, |extensions, layer| {
287                 self.supported_layer_extensions(layer)
288                     .map(|layer_extensions| extensions.union(&layer_extensions))
289             })
290     }
291 
292     /// Calls `get_instance_proc_addr` on the underlying loader.
293     #[inline]
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction294     pub unsafe fn get_instance_proc_addr(
295         &self,
296         instance: ash::vk::Instance,
297         name: *const c_char,
298     ) -> ash::vk::PFN_vkVoidFunction {
299         self.loader.get_instance_proc_addr(instance, name)
300     }
301 }
302 
303 /// Implemented on objects that grant access to a Vulkan implementation.
304 pub unsafe trait Loader: Send + Sync {
305     /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same.
306     ///
307     /// The returned function must stay valid for as long as `self` is alive.
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction308     unsafe fn get_instance_proc_addr(
309         &self,
310         instance: ash::vk::Instance,
311         name: *const c_char,
312     ) -> ash::vk::PFN_vkVoidFunction;
313 }
314 
315 unsafe impl<T> Loader for T
316 where
317     T: SafeDeref + Send + Sync,
318     T::Target: Loader,
319 {
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction320     unsafe fn get_instance_proc_addr(
321         &self,
322         instance: ash::vk::Instance,
323         name: *const c_char,
324     ) -> ash::vk::PFN_vkVoidFunction {
325         (**self).get_instance_proc_addr(instance, name)
326     }
327 }
328 
329 impl Debug for dyn Loader {
fmt(&self, _f: &mut Formatter<'_>) -> Result<(), FmtError>330     fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), FmtError> {
331         Ok(())
332     }
333 }
334 
335 /// Implementation of `Loader` that loads Vulkan from a dynamic library.
336 pub struct DynamicLibraryLoader {
337     _vk_lib: Library,
338     get_instance_proc_addr: ash::vk::PFN_vkGetInstanceProcAddr,
339 }
340 
341 impl DynamicLibraryLoader {
342     /// Tries to load the dynamic library at the given path, and tries to
343     /// load `vkGetInstanceProcAddr` in it.
344     ///
345     /// # Safety
346     ///
347     /// - The dynamic library must be a valid Vulkan implementation.
348     ///
new(path: impl AsRef<Path>) -> Result<DynamicLibraryLoader, LoadingError>349     pub unsafe fn new(path: impl AsRef<Path>) -> Result<DynamicLibraryLoader, LoadingError> {
350         let vk_lib = Library::new(path.as_ref()).map_err(LoadingError::LibraryLoadFailure)?;
351 
352         let get_instance_proc_addr = *vk_lib
353             .get(b"vkGetInstanceProcAddr")
354             .map_err(LoadingError::LibraryLoadFailure)?;
355 
356         Ok(DynamicLibraryLoader {
357             _vk_lib: vk_lib,
358             get_instance_proc_addr,
359         })
360     }
361 }
362 
363 unsafe impl Loader for DynamicLibraryLoader {
364     #[inline]
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> ash::vk::PFN_vkVoidFunction365     unsafe fn get_instance_proc_addr(
366         &self,
367         instance: ash::vk::Instance,
368         name: *const c_char,
369     ) -> ash::vk::PFN_vkVoidFunction {
370         (self.get_instance_proc_addr)(instance, name)
371     }
372 }
373 
374 /// Expression that returns a loader that assumes that Vulkan is linked to the executable you're
375 /// compiling.
376 ///
377 /// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr`
378 /// symbol.
379 ///
380 /// This is provided as a macro and not as a regular function, because the macro contains an
381 /// `extern {}` block.
382 // TODO: should this be unsafe?
383 #[macro_export]
384 macro_rules! statically_linked_vulkan_loader {
385     () => {{
386         extern "C" {
387             fn vkGetInstanceProcAddr(
388                 instance: ash::vk::Instance,
389                 pName: *const c_char,
390             ) -> ash::vk::PFN_vkVoidFunction;
391         }
392 
393         struct StaticallyLinkedVulkanLoader;
394         unsafe impl Loader for StaticallyLinkedVulkanLoader {
395             unsafe fn get_instance_proc_addr(
396                 &self,
397                 instance: ash::vk::Instance,
398                 name: *const c_char,
399             ) -> ash::vk::PFN_vkVoidFunction {
400                 vkGetInstanceProcAddr(instance, name)
401             }
402         }
403 
404         StaticallyLinkedVulkanLoader
405     }};
406 }
407 
408 /// Error that can happen when loading a Vulkan library.
409 #[derive(Debug)]
410 pub enum LoadingError {
411     /// Failed to load the Vulkan shared library.
412     LibraryLoadFailure(LibloadingError),
413 
414     /// Not enough memory.
415     OomError(OomError),
416 }
417 
418 impl Error for LoadingError {
source(&self) -> Option<&(dyn Error + 'static)>419     fn source(&self) -> Option<&(dyn Error + 'static)> {
420         match self {
421             //Self::LibraryLoadFailure(err) => Some(err),
422             Self::OomError(err) => Some(err),
423             _ => None,
424         }
425     }
426 }
427 
428 impl Display for LoadingError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>429     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
430         write!(
431             f,
432             "{}",
433             match self {
434                 Self::LibraryLoadFailure(_) => "failed to load the Vulkan shared library",
435                 Self::OomError(_) => "not enough memory available",
436             }
437         )
438     }
439 }
440 
441 impl From<VulkanError> for LoadingError {
from(err: VulkanError) -> Self442     fn from(err: VulkanError) -> Self {
443         match err {
444             err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
445             err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
446             _ => panic!("unexpected error: {:?}", err),
447         }
448     }
449 }
450 
451 #[cfg(test)]
452 mod tests {
453     use super::{DynamicLibraryLoader, LoadingError};
454 
455     #[test]
dl_open_error()456     fn dl_open_error() {
457         unsafe {
458             match DynamicLibraryLoader::new("_non_existing_library.void") {
459                 Err(LoadingError::LibraryLoadFailure(_)) => (),
460                 _ => panic!(),
461             }
462         }
463     }
464 }
465