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