1 use crate::instance::Instance;
2 use crate::prelude::*;
3 use crate::vk;
4 use crate::RawPtr;
5 use std::ffi::CStr;
6 #[cfg(feature = "loaded")]
7 use std::ffi::OsStr;
8 use std::mem;
9 use std::os::raw::c_char;
10 use std::os::raw::c_void;
11 use std::ptr;
12 #[cfg(feature = "loaded")]
13 use std::sync::Arc;
14 
15 #[cfg(feature = "loaded")]
16 use libloading::Library;
17 
18 /// Holds the Vulkan functions independent of a particular instance
19 #[derive(Clone)]
20 pub struct Entry {
21     static_fn: vk::StaticFn,
22     entry_fn_1_0: vk::EntryFnV1_0,
23     entry_fn_1_1: vk::EntryFnV1_1,
24     entry_fn_1_2: vk::EntryFnV1_2,
25     #[cfg(feature = "loaded")]
26     _lib_guard: Option<Arc<Library>>,
27 }
28 
29 /// Vulkan core 1.0
30 #[allow(non_camel_case_types)]
31 impl Entry {
32     /// Load default Vulkan library for the current platform
33     ///
34     /// Prefer this over [`linked`](Self::linked) when your application can gracefully handle
35     /// environments that lack Vulkan support, and when the build environment might not have Vulkan
36     /// development packages installed (e.g. the Vulkan SDK, or Ubuntu's `libvulkan-dev`).
37     ///
38     /// # Safety
39     /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
40     /// for [`Library::new()`] and [`Library::get()`] apply here.
41     ///
42     /// ```no_run
43     /// use ash::{vk, Entry};
44     /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
45     /// let entry = unsafe { Entry::load()? };
46     /// let app_info = vk::ApplicationInfo {
47     ///     api_version: vk::make_api_version(0, 1, 0, 0),
48     ///     ..Default::default()
49     /// };
50     /// let create_info = vk::InstanceCreateInfo {
51     ///     p_application_info: &app_info,
52     ///     ..Default::default()
53     /// };
54     /// let instance = unsafe { entry.create_instance(&create_info, None)? };
55     /// # Ok(()) }
56     /// ```
57     #[cfg(feature = "loaded")]
58     #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
load() -> Result<Self, LoadingError>59     pub unsafe fn load() -> Result<Self, LoadingError> {
60         #[cfg(windows)]
61         const LIB_PATH: &str = "vulkan-1.dll";
62 
63         #[cfg(all(
64             unix,
65             not(any(target_os = "macos", target_os = "ios", target_os = "android"))
66         ))]
67         const LIB_PATH: &str = "libvulkan.so.1";
68 
69         #[cfg(target_os = "android")]
70         const LIB_PATH: &str = "libvulkan.so";
71 
72         #[cfg(any(target_os = "macos", target_os = "ios"))]
73         const LIB_PATH: &str = "libvulkan.dylib";
74 
75         Self::load_from(LIB_PATH)
76     }
77 
78     /// Load entry points from a Vulkan loader linked at compile time
79     ///
80     /// Compared to [`load`](Self::load), this is infallible, but requires that the build
81     /// environment have Vulkan development packages installed (e.g. the Vulkan SDK, or Ubuntu's
82     /// `libvulkan-dev`), and prevents the resulting binary from starting in environments that do not
83     /// support Vulkan.
84     ///
85     /// Note that instance/device functions are still fetched via `vkGetInstanceProcAddr` and
86     /// `vkGetDeviceProcAddr` for maximum performance.
87     ///
88     /// ```no_run
89     /// use ash::{vk, Entry};
90     /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
91     /// let entry = Entry::linked();
92     /// let app_info = vk::ApplicationInfo {
93     ///     api_version: vk::make_api_version(0, 1, 0, 0),
94     ///     ..Default::default()
95     /// };
96     /// let create_info = vk::InstanceCreateInfo {
97     ///     p_application_info: &app_info,
98     ///     ..Default::default()
99     /// };
100     /// let instance = unsafe { entry.create_instance(&create_info, None)? };
101     /// # Ok(()) }
102     /// ```
103     #[cfg(feature = "linked")]
104     #[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
linked() -> Self105     pub fn linked() -> Self {
106         // Sound because we're linking to Vulkan, which provides a vkGetInstanceProcAddr that has
107         // defined behavior in this use.
108         unsafe {
109             Self::from_static_fn(vk::StaticFn {
110                 get_instance_proc_addr: vkGetInstanceProcAddr,
111             })
112         }
113     }
114 
115     /// Load Vulkan library at `path`
116     ///
117     /// # Safety
118     /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
119     /// for [`Library::new()`] and [`Library::get()`] apply here.
120     #[cfg(feature = "loaded")]
121     #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError>122     pub unsafe fn load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError> {
123         let lib = Library::new(path)
124             .map_err(LoadingError::LibraryLoadFailure)
125             .map(Arc::new)?;
126 
127         let static_fn = vk::StaticFn::load_checked(|name| {
128             lib.get(name.to_bytes_with_nul())
129                 .map(|symbol| *symbol)
130                 .unwrap_or(ptr::null_mut())
131         })?;
132 
133         Ok(Self {
134             _lib_guard: Some(lib),
135             ..Self::from_static_fn(static_fn)
136         })
137     }
138 
139     /// Load entry points based on an already-loaded [`vk::StaticFn`]
140     ///
141     /// # Safety
142     /// `static_fn` must contain valid function pointers that comply with the semantics specified by
143     /// Vulkan 1.0, which must remain valid for at least the lifetime of the returned [`Entry`].
from_static_fn(static_fn: vk::StaticFn) -> Self144     pub unsafe fn from_static_fn(static_fn: vk::StaticFn) -> Self {
145         let load_fn = |name: &std::ffi::CStr| unsafe {
146             mem::transmute(static_fn.get_instance_proc_addr(vk::Instance::null(), name.as_ptr()))
147         };
148         let entry_fn_1_0 = vk::EntryFnV1_0::load(load_fn);
149         let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn);
150         let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn);
151 
152         Self {
153             static_fn,
154             entry_fn_1_0,
155             entry_fn_1_1,
156             entry_fn_1_2,
157             #[cfg(feature = "loaded")]
158             _lib_guard: None,
159         }
160     }
161 
fp_v1_0(&self) -> &vk::EntryFnV1_0162     pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 {
163         &self.entry_fn_1_0
164     }
165 
static_fn(&self) -> &vk::StaticFn166     pub fn static_fn(&self) -> &vk::StaticFn {
167         &self.static_fn
168     }
169 
170     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceVersion.html>"]
171     /// ```no_run
172     /// # use ash::{Entry, vk};
173     /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
174     /// let entry = Entry::linked();
175     /// match entry.try_enumerate_instance_version()? {
176     ///     // Vulkan 1.1+
177     ///     Some(version) => {
178     ///         let major = vk::version_major(version);
179     ///         let minor = vk::version_minor(version);
180     ///         let patch = vk::version_patch(version);
181     ///     },
182     ///     // Vulkan 1.0
183     ///     None => {},
184     /// }
185     /// # Ok(()) }
186     /// ```
try_enumerate_instance_version(&self) -> VkResult<Option<u32>>187     pub fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> {
188         unsafe {
189             let mut api_version = 0;
190             let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = {
191                 let name = b"vkEnumerateInstanceVersion\0".as_ptr() as *const _;
192                 mem::transmute(
193                     self.static_fn
194                         .get_instance_proc_addr(vk::Instance::null(), name),
195                 )
196             };
197             if let Some(enumerate_instance_version) = enumerate_instance_version {
198                 (enumerate_instance_version)(&mut api_version)
199                     .result_with_success(Some(api_version))
200             } else {
201                 Ok(None)
202             }
203         }
204     }
205 
206     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCreateInstance.html>"]
207     ///
208     /// # Safety
209     /// In order for the created [`Instance`] to be valid for the duration of its
210     /// usage, the [`Entry`](Self) this was called on must be dropped later than the
211     /// resulting [`Instance`].
create_instance( &self, create_info: &vk::InstanceCreateInfo, allocation_callbacks: Option<&vk::AllocationCallbacks>, ) -> VkResult<Instance>212     pub unsafe fn create_instance(
213         &self,
214         create_info: &vk::InstanceCreateInfo,
215         allocation_callbacks: Option<&vk::AllocationCallbacks>,
216     ) -> VkResult<Instance> {
217         let mut instance = mem::zeroed();
218         self.entry_fn_1_0
219             .create_instance(
220                 create_info,
221                 allocation_callbacks.as_raw_ptr(),
222                 &mut instance,
223             )
224             .result()?;
225         Ok(Instance::load(&self.static_fn, instance))
226     }
227 
228     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceLayerProperties.html>"]
enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>>229     pub fn enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>> {
230         unsafe {
231             read_into_uninitialized_vector(|count, data| {
232                 self.entry_fn_1_0
233                     .enumerate_instance_layer_properties(count, data)
234             })
235         }
236     }
237 
238     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>"]
enumerate_instance_extension_properties( &self, ) -> VkResult<Vec<vk::ExtensionProperties>>239     pub fn enumerate_instance_extension_properties(
240         &self,
241     ) -> VkResult<Vec<vk::ExtensionProperties>> {
242         unsafe {
243             read_into_uninitialized_vector(|count, data| {
244                 self.entry_fn_1_0
245                     .enumerate_instance_extension_properties(ptr::null(), count, data)
246             })
247         }
248     }
249 
250     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkGetInstanceProcAddr.html>"]
get_instance_proc_addr( &self, instance: vk::Instance, p_name: *const c_char, ) -> vk::PFN_vkVoidFunction251     pub unsafe fn get_instance_proc_addr(
252         &self,
253         instance: vk::Instance,
254         p_name: *const c_char,
255     ) -> vk::PFN_vkVoidFunction {
256         self.static_fn.get_instance_proc_addr(instance, p_name)
257     }
258 }
259 
260 /// Vulkan core 1.1
261 #[allow(non_camel_case_types)]
262 impl Entry {
fp_v1_1(&self) -> &vk::EntryFnV1_1263     pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 {
264         &self.entry_fn_1_1
265     }
266 
267     #[deprecated = "This function is unavailable and therefore panics on Vulkan 1.0, please use `try_enumerate_instance_version` instead"]
268     #[doc = "<https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkEnumerateInstanceVersion.html>"]
269     ///
270     /// Please use [`Self::try_enumerate_instance_version`] instead.
enumerate_instance_version(&self) -> VkResult<u32>271     pub fn enumerate_instance_version(&self) -> VkResult<u32> {
272         unsafe {
273             let mut api_version = 0;
274             self.entry_fn_1_1
275                 .enumerate_instance_version(&mut api_version)
276                 .result_with_success(api_version)
277         }
278     }
279 }
280 
281 /// Vulkan core 1.2
282 #[allow(non_camel_case_types)]
283 impl Entry {
fp_v1_2(&self) -> &vk::EntryFnV1_2284     pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 {
285         &self.entry_fn_1_2
286     }
287 }
288 
289 #[cfg(feature = "linked")]
290 #[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
291 impl Default for Entry {
default() -> Self292     fn default() -> Self {
293         Self::linked()
294     }
295 }
296 
297 impl vk::StaticFn {
load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> where F: FnMut(&::std::ffi::CStr) -> *const c_void,298     pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint>
299     where
300         F: FnMut(&::std::ffi::CStr) -> *const c_void,
301     {
302         // TODO: Make this a &'static CStr once CStr::from_bytes_with_nul_unchecked is const
303         static ENTRY_POINT: &[u8] = b"vkGetInstanceProcAddr\0";
304 
305         Ok(Self {
306             get_instance_proc_addr: unsafe {
307                 let cname = CStr::from_bytes_with_nul_unchecked(ENTRY_POINT);
308                 let val = _f(cname);
309                 if val.is_null() {
310                     return Err(MissingEntryPoint);
311                 } else {
312                     ::std::mem::transmute(val)
313                 }
314             },
315         })
316     }
317 }
318 
319 #[derive(Clone, Debug)]
320 pub struct MissingEntryPoint;
321 impl std::fmt::Display for MissingEntryPoint {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>322     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
323         write!(f, "Cannot load `vkGetInstanceProcAddr` symbol from library")
324     }
325 }
326 impl std::error::Error for MissingEntryPoint {}
327 
328 #[cfg(feature = "linked")]
329 extern "system" {
vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char) -> vk::PFN_vkVoidFunction330     fn vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char)
331         -> vk::PFN_vkVoidFunction;
332 }
333 
334 #[cfg(feature = "loaded")]
335 mod loaded {
336     use std::error::Error;
337     use std::fmt;
338 
339     use super::*;
340 
341     #[derive(Debug)]
342     #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
343     pub enum LoadingError {
344         LibraryLoadFailure(libloading::Error),
345         MissingEntryPoint(MissingEntryPoint),
346     }
347 
348     impl fmt::Display for LoadingError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result349         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350             match self {
351                 LoadingError::LibraryLoadFailure(err) => fmt::Display::fmt(err, f),
352                 LoadingError::MissingEntryPoint(err) => fmt::Display::fmt(err, f),
353             }
354         }
355     }
356 
357     impl Error for LoadingError {
source(&self) -> Option<&(dyn Error + 'static)>358         fn source(&self) -> Option<&(dyn Error + 'static)> {
359             Some(match self {
360                 LoadingError::LibraryLoadFailure(err) => err,
361                 LoadingError::MissingEntryPoint(err) => err,
362             })
363         }
364     }
365 
366     impl From<MissingEntryPoint> for LoadingError {
from(err: MissingEntryPoint) -> Self367         fn from(err: MissingEntryPoint) -> Self {
368             Self::MissingEntryPoint(err)
369         }
370     }
371 }
372 #[cfg(feature = "loaded")]
373 pub use self::loaded::*;
374