ash/
entry.rs

1use crate::instance::Instance;
2#[cfg(doc)]
3use crate::khr;
4use crate::prelude::*;
5use crate::vk;
6use crate::RawPtr;
7use alloc::vec::Vec;
8use core::ffi;
9use core::fmt;
10use core::mem;
11use core::ptr;
12
13#[cfg(feature = "loaded")]
14use libloading::Library;
15
16/// Holds the Vulkan functions independent of a particular instance
17#[derive(Clone)]
18pub struct Entry {
19    static_fn: crate::StaticFn,
20    entry_fn_1_0: crate::EntryFnV1_0,
21    entry_fn_1_1: crate::EntryFnV1_1,
22    #[cfg(feature = "loaded")]
23    _lib_guard: Option<alloc::sync::Arc<Library>>,
24}
25
26/// Vulkan core 1.0
27impl Entry {
28    /// Load default Vulkan library for the current platform
29    ///
30    /// Prefer this over [`linked()`][Self::linked()] when your application can gracefully handle
31    /// environments that lack Vulkan support, and when the build environment might not have Vulkan
32    /// development packages installed (e.g. the Vulkan SDK, or Ubuntu's `libvulkan-dev`).
33    ///
34    /// # Safety
35    ///
36    /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines
37    /// for [`Library::new()`] and [`Library::get()`] apply here.
38    ///
39    /// No Vulkan functions loaded directly or indirectly from this [`Entry`]
40    /// may be called after it is [dropped][drop()].
41    ///
42    /// # Example
43    ///
44    /// ```no_run
45    /// use ash::{vk, Entry};
46    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
47    /// let entry = unsafe { Entry::load()? };
48    /// let app_info = vk::ApplicationInfo {
49    ///     api_version: vk::make_api_version(0, 1, 0, 0),
50    ///     ..Default::default()
51    /// };
52    /// let create_info = vk::InstanceCreateInfo {
53    ///     p_application_info: &app_info,
54    ///     ..Default::default()
55    /// };
56    /// let instance = unsafe { entry.create_instance(&create_info, None)? };
57    /// # Ok(()) }
58    /// ```
59    #[cfg(feature = "loaded")]
60    #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
61    pub unsafe fn load() -> Result<Self, LoadingError> {
62        #[cfg(windows)]
63        const LIB_PATH: &str = "vulkan-1.dll";
64
65        #[cfg(all(
66            unix,
67            not(any(
68                target_os = "macos",
69                target_os = "ios",
70                target_os = "android",
71                target_os = "fuchsia"
72            ))
73        ))]
74        const LIB_PATH: &str = "libvulkan.so.1";
75
76        #[cfg(any(target_os = "android", target_os = "fuchsia"))]
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(crate::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<std::ffi::OsStr>) -> Result<Self, LoadingError> {
139        let lib = Library::new(path)
140            .map_err(LoadingError::LibraryLoadFailure)
141            .map(alloc::sync::Arc::new)?;
142
143        let static_fn = crate::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 [`crate::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: crate::StaticFn) -> Self {
162        let load_fn = move |name: &ffi::CStr| {
163            mem::transmute((static_fn.get_instance_proc_addr)(
164                vk::Instance::null(),
165                name.as_ptr(),
166            ))
167        };
168
169        Self::from_parts_1_1(
170            static_fn,
171            crate::EntryFnV1_0::load(load_fn),
172            crate::EntryFnV1_1::load(load_fn),
173        )
174    }
175
176    #[inline]
177    pub fn from_parts_1_1(
178        static_fn: crate::StaticFn,
179        entry_fn_1_0: crate::EntryFnV1_0,
180        entry_fn_1_1: crate::EntryFnV1_1,
181    ) -> Self {
182        Self {
183            static_fn,
184            entry_fn_1_0,
185            entry_fn_1_1,
186            #[cfg(feature = "loaded")]
187            _lib_guard: None,
188        }
189    }
190
191    #[inline]
192    pub fn fp_v1_0(&self) -> &crate::EntryFnV1_0 {
193        &self.entry_fn_1_0
194    }
195
196    #[inline]
197    pub fn static_fn(&self) -> &crate::StaticFn {
198        &self.static_fn
199    }
200
201    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceVersion.html>
202    ///
203    /// # Example
204    ///
205    /// ```no_run
206    /// # use ash::{Entry, vk};
207    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
208    /// let entry = Entry::linked();
209    /// match unsafe { entry.try_enumerate_instance_version() }? {
210    ///     // Vulkan 1.1+
211    ///     Some(version) => {
212    ///         let major = vk::version_major(version);
213    ///         let minor = vk::version_minor(version);
214    ///         let patch = vk::version_patch(version);
215    ///     },
216    ///     // Vulkan 1.0
217    ///     None => {},
218    /// }
219    /// # Ok(()) }
220    /// ```
221    #[inline]
222    pub unsafe fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> {
223        let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = {
224            let name = ffi::CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0");
225            mem::transmute((self.static_fn.get_instance_proc_addr)(
226                vk::Instance::null(),
227                name.as_ptr(),
228            ))
229        };
230        if let Some(enumerate_instance_version) = enumerate_instance_version {
231            let mut api_version = mem::MaybeUninit::uninit();
232            (enumerate_instance_version)(api_version.as_mut_ptr())
233                .assume_init_on_success(api_version)
234                .map(Some)
235        } else {
236            Ok(None)
237        }
238    }
239
240    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCreateInstance.html>
241    ///
242    /// # Safety
243    ///
244    /// The resulting [`Instance`] and any function-pointer objects (e.g. [`Device`][crate::Device]
245    /// and extensions like [`khr::swapchain::Device`]) loaded from it may not be used after
246    /// this [`Entry`] object is dropped, unless it was crated using [`Entry::linked()`] or
247    /// [`Entry::from_parts_1_1()`].
248    ///
249    /// [`Instance`] does _not_ implement [drop][drop()] semantics and can only be destroyed via
250    /// [`destroy_instance()`][Instance::destroy_instance()].
251    #[inline]
252    pub unsafe fn create_instance(
253        &self,
254        create_info: &vk::InstanceCreateInfo<'_>,
255        allocation_callbacks: Option<&vk::AllocationCallbacks<'_>>,
256    ) -> VkResult<Instance> {
257        let mut instance = mem::MaybeUninit::uninit();
258        let instance = (self.entry_fn_1_0.create_instance)(
259            create_info,
260            allocation_callbacks.as_raw_ptr(),
261            instance.as_mut_ptr(),
262        )
263        .assume_init_on_success(instance)?;
264        Ok(Instance::load(&self.static_fn, instance))
265    }
266
267    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html>
268    #[inline]
269    pub unsafe fn enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>> {
270        read_into_uninitialized_vector(|count, data| {
271            (self.entry_fn_1_0.enumerate_instance_layer_properties)(count, data)
272        })
273    }
274
275    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>
276    #[inline]
277    pub unsafe fn enumerate_instance_extension_properties(
278        &self,
279        layer_name: Option<&ffi::CStr>,
280    ) -> VkResult<Vec<vk::ExtensionProperties>> {
281        read_into_uninitialized_vector(|count, data| {
282            (self.entry_fn_1_0.enumerate_instance_extension_properties)(
283                layer_name.map_or(ptr::null(), |str| str.as_ptr()),
284                count,
285                data,
286            )
287        })
288    }
289
290    /// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html>
291    #[inline]
292    pub unsafe fn get_instance_proc_addr(
293        &self,
294        instance: vk::Instance,
295        p_name: *const ffi::c_char,
296    ) -> vk::PFN_vkVoidFunction {
297        (self.static_fn.get_instance_proc_addr)(instance, p_name)
298    }
299}
300
301/// Vulkan core 1.1
302impl Entry {
303    #[inline]
304    pub fn fp_v1_1(&self) -> &crate::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://registry.khronos.org/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 unsafe fn enumerate_instance_version(&self) -> VkResult<u32> {
314        let mut api_version = mem::MaybeUninit::uninit();
315        (self.entry_fn_1_1.enumerate_instance_version)(api_version.as_mut_ptr())
316            .assume_init_on_success(api_version)
317    }
318}
319
320#[cfg(feature = "linked")]
321#[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
322impl Default for Entry {
323    #[inline]
324    fn default() -> Self {
325        Self::linked()
326    }
327}
328
329impl crate::StaticFn {
330    pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint>
331    where
332        F: FnMut(&ffi::CStr) -> *const ffi::c_void,
333    {
334        Ok(Self {
335            get_instance_proc_addr: unsafe {
336                let cname = ffi::CStr::from_bytes_with_nul_unchecked(b"vkGetInstanceProcAddr\0");
337                let val = _f(cname);
338                if val.is_null() {
339                    return Err(MissingEntryPoint);
340                } else {
341                    mem::transmute(val)
342                }
343            },
344        })
345    }
346}
347
348#[derive(Clone, Debug)]
349pub struct MissingEntryPoint;
350impl fmt::Display for MissingEntryPoint {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        write!(f, "Cannot load `vkGetInstanceProcAddr` symbol from library")
353    }
354}
355#[cfg(feature = "std")] // TODO: implement when error_in_core is stabilized
356impl std::error::Error for MissingEntryPoint {}
357
358#[cfg(feature = "linked")]
359extern "system" {
360    fn vkGetInstanceProcAddr(
361        instance: vk::Instance,
362        name: *const ffi::c_char,
363    ) -> vk::PFN_vkVoidFunction;
364}
365
366#[cfg(feature = "loaded")]
367mod loaded {
368
369    use super::*;
370
371    #[derive(Debug)]
372    #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
373    pub enum LoadingError {
374        LibraryLoadFailure(libloading::Error),
375        MissingEntryPoint(MissingEntryPoint),
376    }
377
378    impl fmt::Display for LoadingError {
379        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380            match self {
381                Self::LibraryLoadFailure(err) => fmt::Display::fmt(err, f),
382                Self::MissingEntryPoint(err) => fmt::Display::fmt(err, f),
383            }
384        }
385    }
386
387    #[cfg(feature = "std")]
388    impl std::error::Error for LoadingError {
389        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
390            Some(match self {
391                Self::LibraryLoadFailure(err) => err,
392                Self::MissingEntryPoint(err) => err,
393            })
394        }
395    }
396
397    impl From<MissingEntryPoint> for LoadingError {
398        fn from(err: MissingEntryPoint) -> Self {
399            Self::MissingEntryPoint(err)
400        }
401    }
402}
403#[cfg(feature = "loaded")]
404pub use self::loaded::*;