• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 implementation loading system.
11 //!
12 //! Before vulkano can do anything, it first needs to find an implementation of Vulkan. A Vulkan
13 //! implementation is defined as a single `vkGetInstanceProcAddr` function, which can be accessed
14 //! through the `Loader` trait.
15 //!
16 //! This module provides various implementations of the `Loader` trait.
17 //!
18 //! Once you have a struct that implements `Loader`, you can create a `FunctionPointers` struct
19 //! from it and use this `FunctionPointers` struct to build an `Instance`.
20 //!
21 //! By default vulkano will use the `auto_loader()` function, which tries to automatically load
22 //! a Vulkan implementation from the system.
23 
24 use crate::check_errors;
25 use crate::fns::EntryFunctions;
26 use crate::OomError;
27 use crate::SafeDeref;
28 use crate::Version;
29 use lazy_static::lazy_static;
30 use shared_library;
31 use std::error;
32 use std::ffi::CStr;
33 use std::fmt;
34 use std::mem;
35 use std::ops::Deref;
36 use std::os::raw::c_char;
37 use std::os::raw::c_void;
38 use std::path::Path;
39 
40 /// Implemented on objects that grant access to a Vulkan implementation.
41 pub unsafe trait Loader {
42     /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same.
43     ///
44     /// 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, ) -> *const c_void45     fn get_instance_proc_addr(
46         &self,
47         instance: ash::vk::Instance,
48         name: *const c_char,
49     ) -> *const c_void;
50 }
51 
52 unsafe impl<T> Loader for T
53 where
54     T: SafeDeref,
55     T::Target: Loader,
56 {
57     #[inline]
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> *const c_void58     fn get_instance_proc_addr(
59         &self,
60         instance: ash::vk::Instance,
61         name: *const c_char,
62     ) -> *const c_void {
63         (**self).get_instance_proc_addr(instance, name)
64     }
65 }
66 
67 /// Implementation of `Loader` that loads Vulkan from a dynamic library.
68 pub struct DynamicLibraryLoader {
69     vk_lib: shared_library::dynamic_library::DynamicLibrary,
70     get_proc_addr:
71         extern "system" fn(instance: ash::vk::Instance, pName: *const c_char) -> *const c_void,
72 }
73 
74 impl DynamicLibraryLoader {
75     /// Tries to load the dynamic library at the given path, and tries to
76     /// load `vkGetInstanceProcAddr` in it.
77     ///
78     /// # Safety
79     ///
80     /// - The dynamic library must be a valid Vulkan implementation.
81     ///
new<P>(path: P) -> Result<DynamicLibraryLoader, LoadingError> where P: AsRef<Path>,82     pub unsafe fn new<P>(path: P) -> Result<DynamicLibraryLoader, LoadingError>
83     where
84         P: AsRef<Path>,
85     {
86         let vk_lib = shared_library::dynamic_library::DynamicLibrary::open(Some(path.as_ref()))
87             .map_err(LoadingError::LibraryLoadFailure)?;
88 
89         let get_proc_addr = {
90             let ptr: *mut c_void = vk_lib
91                 .symbol("vkGetInstanceProcAddr")
92                 .map_err(|_| LoadingError::MissingEntryPoint("vkGetInstanceProcAddr".to_owned()))?;
93             mem::transmute(ptr)
94         };
95 
96         Ok(DynamicLibraryLoader {
97             vk_lib,
98             get_proc_addr,
99         })
100     }
101 }
102 
103 unsafe impl Loader for DynamicLibraryLoader {
104     #[inline]
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> *const c_void105     fn get_instance_proc_addr(
106         &self,
107         instance: ash::vk::Instance,
108         name: *const c_char,
109     ) -> *const c_void {
110         (self.get_proc_addr)(instance, name)
111     }
112 }
113 
114 /// Wraps around a loader and contains function pointers.
115 pub struct FunctionPointers<L> {
116     loader: L,
117     fns: EntryFunctions,
118 }
119 
120 impl<L> FunctionPointers<L> {
121     /// Loads some global function pointer from the loader.
new(loader: L) -> FunctionPointers<L> where L: Loader,122     pub fn new(loader: L) -> FunctionPointers<L>
123     where
124         L: Loader,
125     {
126         let fns = EntryFunctions::load(|name| unsafe {
127             mem::transmute(loader.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr()))
128         });
129 
130         FunctionPointers { loader, fns }
131     }
132 
133     /// Returns the collection of Vulkan entry points from the Vulkan loader.
134     #[inline]
fns(&self) -> &EntryFunctions135     pub fn fns(&self) -> &EntryFunctions {
136         &self.fns
137     }
138 
139     /// Returns the highest Vulkan version that is supported for instances.
api_version(&self) -> Result<Version, OomError> where L: Loader,140     pub fn api_version(&self) -> Result<Version, OomError>
141     where
142         L: Loader,
143     {
144         // Per the Vulkan spec:
145         // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
146         // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
147         // to determine the version of Vulkan.
148         unsafe {
149             let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0");
150             let func = self.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr());
151 
152             if func.is_null() {
153                 Ok(Version {
154                     major: 1,
155                     minor: 0,
156                     patch: 0,
157                 })
158             } else {
159                 type Pfn = extern "system" fn(pApiVersion: *mut u32) -> ash::vk::Result;
160                 let func: Pfn = mem::transmute(func);
161                 let mut api_version = 0;
162                 check_errors(func(&mut api_version))?;
163                 Ok(Version::from(api_version))
164             }
165         }
166     }
167 
168     /// Calls `get_instance_proc_addr` on the underlying loader.
169     #[inline]
get_instance_proc_addr( &self, instance: ash::vk::Instance, name: *const c_char, ) -> *const c_void where L: Loader,170     pub fn get_instance_proc_addr(
171         &self,
172         instance: ash::vk::Instance,
173         name: *const c_char,
174     ) -> *const c_void
175     where
176         L: Loader,
177     {
178         self.loader.get_instance_proc_addr(instance, name)
179     }
180 }
181 
182 /// Expression that returns a loader that assumes that Vulkan is linked to the executable you're
183 /// compiling.
184 ///
185 /// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr`
186 /// symbol.
187 ///
188 /// This is provided as a macro and not as a regular function, because the macro contains an
189 /// `extern {}` block.
190 // TODO: should this be unsafe?
191 #[macro_export]
192 macro_rules! statically_linked_vulkan_loader {
193     () => {{
194         extern "C" {
195             fn vkGetInstanceProcAddr(
196                 instance: ash::vk::Instance,
197                 pName: *const c_char,
198             ) -> ash::vk::PFN_vkVoidFunction;
199         }
200 
201         struct StaticallyLinkedVulkanLoader;
202         unsafe impl Loader for StaticallyLinkedVulkanLoader {
203             fn get_instance_proc_addr(
204                 &self,
205                 instance: ash::vk::Instance,
206                 name: *const c_char,
207             ) -> extern "system" fn() -> () {
208                 unsafe { vkGetInstanceProcAddr(instance, name) }
209             }
210         }
211 
212         StaticallyLinkedVulkanLoader
213     }};
214 }
215 
216 /// Returns the default `FunctionPointers` for this system.
217 ///
218 /// This function tries to auto-guess where to find the Vulkan implementation, and loads it in a
219 /// `lazy_static!`. The content of the lazy_static is then returned, or an error if we failed to
220 /// load Vulkan.
auto_loader( ) -> Result<&'static FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError>221 pub fn auto_loader(
222 ) -> Result<&'static FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError> {
223     #[cfg(target_os = "ios")]
224     #[allow(non_snake_case)]
225     fn def_loader_impl() -> Result<Box<Loader + Send + Sync>, LoadingError> {
226         let loader = statically_linked_vulkan_loader!();
227         Ok(Box::new(loader))
228     }
229 
230     #[cfg(not(target_os = "ios"))]
231     fn def_loader_impl() -> Result<Box<dyn Loader + Send + Sync>, LoadingError> {
232         #[cfg(windows)]
233         fn get_path() -> &'static Path {
234             Path::new("vulkan-1.dll")
235         }
236         #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
237         fn get_path() -> &'static Path {
238             Path::new("libvulkan.so.1")
239         }
240         #[cfg(target_os = "macos")]
241         fn get_path() -> &'static Path {
242             Path::new("libvulkan.1.dylib")
243         }
244         #[cfg(target_os = "android")]
245         fn get_path() -> &'static Path {
246             Path::new("libvulkan.so")
247         }
248 
249         let loader = unsafe { DynamicLibraryLoader::new(get_path())? };
250 
251         Ok(Box::new(loader))
252     }
253 
254     lazy_static! {
255         static ref DEFAULT_LOADER: Result<FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError> =
256             def_loader_impl().map(FunctionPointers::new);
257     }
258 
259     match DEFAULT_LOADER.deref() {
260         &Ok(ref ptr) => Ok(ptr),
261         &Err(ref err) => Err(err.clone()),
262     }
263 }
264 
265 /// Error that can happen when loading the Vulkan loader.
266 #[derive(Debug, Clone)]
267 pub enum LoadingError {
268     /// Failed to load the Vulkan shared library.
269     LibraryLoadFailure(String), // TODO: meh for error type, but this needs changes in shared_library
270 
271     /// One of the entry points required to be supported by the Vulkan implementation is missing.
272     MissingEntryPoint(String),
273 }
274 
275 impl error::Error for LoadingError {
276     /*#[inline]
277     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
278         match *self {
279             LoadingError::LibraryLoadFailure(ref err) => Some(err),
280             _ => None
281         }
282     }*/
283 }
284 
285 impl fmt::Display for LoadingError {
286     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>287     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
288         write!(
289             fmt,
290             "{}",
291             match *self {
292                 LoadingError::LibraryLoadFailure(_) => "failed to load the Vulkan shared library",
293                 LoadingError::MissingEntryPoint(_) => {
294                     "one of the entry points required to be supported by the Vulkan implementation \
295                  is missing"
296                 }
297             }
298         )
299     }
300 }
301 
302 #[cfg(test)]
303 mod tests {
304     use crate::instance::loader::DynamicLibraryLoader;
305     use crate::instance::loader::LoadingError;
306 
307     #[test]
dl_open_error()308     fn dl_open_error() {
309         unsafe {
310             match DynamicLibraryLoader::new("_non_existing_library.void") {
311                 Err(LoadingError::LibraryLoadFailure(_)) => (),
312                 _ => panic!(),
313             }
314         }
315     }
316 }
317