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::*;