ash/
entry.rs

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