Skip to main content

jay_ash/
entry.rs

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