diff options
Diffstat (limited to 'third_party/rust/ash/src/entry.rs')
-rw-r--r-- | third_party/rust/ash/src/entry.rs | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/third_party/rust/ash/src/entry.rs b/third_party/rust/ash/src/entry.rs new file mode 100644 index 0000000000..67e6c9a739 --- /dev/null +++ b/third_party/rust/ash/src/entry.rs @@ -0,0 +1,402 @@ +use crate::instance::Instance; +use crate::prelude::*; +use crate::vk; +use crate::RawPtr; +use std::ffi::CStr; +#[cfg(feature = "loaded")] +use std::ffi::OsStr; +use std::mem; +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; +#[cfg(feature = "loaded")] +use std::sync::Arc; + +#[cfg(feature = "loaded")] +use libloading::Library; + +/// Holds the Vulkan functions independent of a particular instance +#[derive(Clone)] +pub struct Entry { + static_fn: vk::StaticFn, + entry_fn_1_0: vk::EntryFnV1_0, + entry_fn_1_1: vk::EntryFnV1_1, + entry_fn_1_2: vk::EntryFnV1_2, + entry_fn_1_3: vk::EntryFnV1_3, + #[cfg(feature = "loaded")] + _lib_guard: Option<Arc<Library>>, +} + +/// Vulkan core 1.0 +#[allow(non_camel_case_types)] +impl Entry { + /// Load default Vulkan library for the current platform + /// + /// Prefer this over [`linked`](Self::linked) when your application can gracefully handle + /// environments that lack Vulkan support, and when the build environment might not have Vulkan + /// development packages installed (e.g. the Vulkan SDK, or Ubuntu's `libvulkan-dev`). + /// + /// # Safety + /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines + /// for [`Library::new()`] and [`Library::get()`] apply here. + /// + /// ```no_run + /// use ash::{vk, Entry}; + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let entry = unsafe { Entry::load()? }; + /// let app_info = vk::ApplicationInfo { + /// api_version: vk::make_api_version(0, 1, 0, 0), + /// ..Default::default() + /// }; + /// let create_info = vk::InstanceCreateInfo { + /// p_application_info: &app_info, + /// ..Default::default() + /// }; + /// let instance = unsafe { entry.create_instance(&create_info, None)? }; + /// # Ok(()) } + /// ``` + #[cfg(feature = "loaded")] + #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] + pub unsafe fn load() -> Result<Self, LoadingError> { + #[cfg(windows)] + const LIB_PATH: &str = "vulkan-1.dll"; + + #[cfg(all( + unix, + not(any(target_os = "macos", target_os = "ios", target_os = "android")) + ))] + const LIB_PATH: &str = "libvulkan.so.1"; + + #[cfg(target_os = "android")] + const LIB_PATH: &str = "libvulkan.so"; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + const LIB_PATH: &str = "libvulkan.dylib"; + + Self::load_from(LIB_PATH) + } + + /// Load entry points from a Vulkan loader linked at compile time + /// + /// Compared to [`load`](Self::load), this is infallible, but requires that the build + /// environment have Vulkan development packages installed (e.g. the Vulkan SDK, or Ubuntu's + /// `libvulkan-dev`), and prevents the resulting binary from starting in environments that do not + /// support Vulkan. + /// + /// Note that instance/device functions are still fetched via `vkGetInstanceProcAddr` and + /// `vkGetDeviceProcAddr` for maximum performance. + /// + /// ```no_run + /// use ash::{vk, Entry}; + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let entry = Entry::linked(); + /// let app_info = vk::ApplicationInfo { + /// api_version: vk::make_api_version(0, 1, 0, 0), + /// ..Default::default() + /// }; + /// let create_info = vk::InstanceCreateInfo { + /// p_application_info: &app_info, + /// ..Default::default() + /// }; + /// let instance = unsafe { entry.create_instance(&create_info, None)? }; + /// # Ok(()) } + /// ``` + #[cfg(feature = "linked")] + #[cfg_attr(docsrs, doc(cfg(feature = "linked")))] + pub fn linked() -> Self { + // Sound because we're linking to Vulkan, which provides a vkGetInstanceProcAddr that has + // defined behavior in this use. + unsafe { + Self::from_static_fn(vk::StaticFn { + get_instance_proc_addr: vkGetInstanceProcAddr, + }) + } + } + + /// Load Vulkan library at `path` + /// + /// # Safety + /// `dlopen`ing native libraries is inherently unsafe. The safety guidelines + /// for [`Library::new()`] and [`Library::get()`] apply here. + #[cfg(feature = "loaded")] + #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] + pub unsafe fn load_from(path: impl AsRef<OsStr>) -> Result<Self, LoadingError> { + let lib = Library::new(path) + .map_err(LoadingError::LibraryLoadFailure) + .map(Arc::new)?; + + let static_fn = vk::StaticFn::load_checked(|name| { + lib.get(name.to_bytes_with_nul()) + .map(|symbol| *symbol) + .unwrap_or(ptr::null_mut()) + })?; + + Ok(Self { + _lib_guard: Some(lib), + ..Self::from_static_fn(static_fn) + }) + } + + /// Load entry points based on an already-loaded [`vk::StaticFn`] + /// + /// # Safety + /// `static_fn` must contain valid function pointers that comply with the semantics specified by + /// Vulkan 1.0, which must remain valid for at least the lifetime of the returned [`Entry`]. + pub unsafe fn from_static_fn(static_fn: vk::StaticFn) -> Self { + let load_fn = |name: &std::ffi::CStr| { + mem::transmute((static_fn.get_instance_proc_addr)( + vk::Instance::null(), + name.as_ptr(), + )) + }; + let entry_fn_1_0 = vk::EntryFnV1_0::load(load_fn); + let entry_fn_1_1 = vk::EntryFnV1_1::load(load_fn); + let entry_fn_1_2 = vk::EntryFnV1_2::load(load_fn); + let entry_fn_1_3 = vk::EntryFnV1_3::load(load_fn); + + Self { + static_fn, + entry_fn_1_0, + entry_fn_1_1, + entry_fn_1_2, + entry_fn_1_3, + #[cfg(feature = "loaded")] + _lib_guard: None, + } + } + + #[inline] + pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 { + &self.entry_fn_1_0 + } + + #[inline] + pub fn static_fn(&self) -> &vk::StaticFn { + &self.static_fn + } + + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceVersion.html> + /// ```no_run + /// # use ash::{Entry, vk}; + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// let entry = Entry::linked(); + /// match entry.try_enumerate_instance_version()? { + /// // Vulkan 1.1+ + /// Some(version) => { + /// let major = vk::version_major(version); + /// let minor = vk::version_minor(version); + /// let patch = vk::version_patch(version); + /// }, + /// // Vulkan 1.0 + /// None => {}, + /// } + /// # Ok(()) } + /// ``` + #[inline] + pub fn try_enumerate_instance_version(&self) -> VkResult<Option<u32>> { + unsafe { + let mut api_version = 0; + let enumerate_instance_version: Option<vk::PFN_vkEnumerateInstanceVersion> = { + let name = ::std::ffi::CStr::from_bytes_with_nul_unchecked( + b"vkEnumerateInstanceVersion\0", + ); + mem::transmute((self.static_fn.get_instance_proc_addr)( + vk::Instance::null(), + name.as_ptr(), + )) + }; + if let Some(enumerate_instance_version) = enumerate_instance_version { + (enumerate_instance_version)(&mut api_version) + .result_with_success(Some(api_version)) + } else { + Ok(None) + } + } + } + + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateInstance.html> + /// + /// # Safety + /// In order for the created [`Instance`] to be valid for the duration of its + /// usage, the [`Entry`](Self) this was called on must be dropped later than the + /// resulting [`Instance`]. + #[inline] + pub unsafe fn create_instance( + &self, + create_info: &vk::InstanceCreateInfo, + allocation_callbacks: Option<&vk::AllocationCallbacks>, + ) -> VkResult<Instance> { + let mut instance = mem::zeroed(); + (self.entry_fn_1_0.create_instance)( + create_info, + allocation_callbacks.as_raw_ptr(), + &mut instance, + ) + .result()?; + Ok(Instance::load(&self.static_fn, instance)) + } + + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html> + #[inline] + pub fn enumerate_instance_layer_properties(&self) -> VkResult<Vec<vk::LayerProperties>> { + unsafe { + read_into_uninitialized_vector(|count, data| { + (self.entry_fn_1_0.enumerate_instance_layer_properties)(count, data) + }) + } + } + + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html> + #[inline] + pub fn enumerate_instance_extension_properties( + &self, + layer_name: Option<&CStr>, + ) -> VkResult<Vec<vk::ExtensionProperties>> { + unsafe { + read_into_uninitialized_vector(|count, data| { + (self.entry_fn_1_0.enumerate_instance_extension_properties)( + layer_name.map_or(ptr::null(), |str| str.as_ptr()), + count, + data, + ) + }) + } + } + + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html> + #[inline] + pub unsafe fn get_instance_proc_addr( + &self, + instance: vk::Instance, + p_name: *const c_char, + ) -> vk::PFN_vkVoidFunction { + (self.static_fn.get_instance_proc_addr)(instance, p_name) + } +} + +/// Vulkan core 1.1 +#[allow(non_camel_case_types)] +impl Entry { + #[inline] + pub fn fp_v1_1(&self) -> &vk::EntryFnV1_1 { + &self.entry_fn_1_1 + } + + #[deprecated = "This function is unavailable and therefore panics on Vulkan 1.0, please use `try_enumerate_instance_version()` instead"] + /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceVersion.html> + /// + /// Please use [`try_enumerate_instance_version()`][Self::try_enumerate_instance_version()] instead. + #[inline] + pub fn enumerate_instance_version(&self) -> VkResult<u32> { + unsafe { + let mut api_version = 0; + (self.entry_fn_1_1.enumerate_instance_version)(&mut api_version) + .result_with_success(api_version) + } + } +} + +/// Vulkan core 1.2 +#[allow(non_camel_case_types)] +impl Entry { + #[inline] + pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 { + &self.entry_fn_1_2 + } +} + +/// Vulkan core 1.3 +#[allow(non_camel_case_types)] +impl Entry { + #[inline] + pub fn fp_v1_3(&self) -> &vk::EntryFnV1_3 { + &self.entry_fn_1_3 + } +} + +#[cfg(feature = "linked")] +#[cfg_attr(docsrs, doc(cfg(feature = "linked")))] +impl Default for Entry { + #[inline] + fn default() -> Self { + Self::linked() + } +} + +impl vk::StaticFn { + pub fn load_checked<F>(mut _f: F) -> Result<Self, MissingEntryPoint> + where + F: FnMut(&::std::ffi::CStr) -> *const c_void, + { + // TODO: Make this a &'static CStr once CStr::from_bytes_with_nul_unchecked is const + static ENTRY_POINT: &[u8] = b"vkGetInstanceProcAddr\0"; + + Ok(Self { + get_instance_proc_addr: unsafe { + let cname = CStr::from_bytes_with_nul_unchecked(ENTRY_POINT); + let val = _f(cname); + if val.is_null() { + return Err(MissingEntryPoint); + } else { + ::std::mem::transmute(val) + } + }, + }) + } +} + +#[derive(Clone, Debug)] +pub struct MissingEntryPoint; +impl std::fmt::Display for MissingEntryPoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!(f, "Cannot load `vkGetInstanceProcAddr` symbol from library") + } +} +impl std::error::Error for MissingEntryPoint {} + +#[cfg(feature = "linked")] +extern "system" { + fn vkGetInstanceProcAddr(instance: vk::Instance, name: *const c_char) + -> vk::PFN_vkVoidFunction; +} + +#[cfg(feature = "loaded")] +mod loaded { + use std::error::Error; + use std::fmt; + + use super::*; + + #[derive(Debug)] + #[cfg_attr(docsrs, doc(cfg(feature = "loaded")))] + pub enum LoadingError { + LibraryLoadFailure(libloading::Error), + MissingEntryPoint(MissingEntryPoint), + } + + impl fmt::Display for LoadingError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::LibraryLoadFailure(err) => fmt::Display::fmt(err, f), + Self::MissingEntryPoint(err) => fmt::Display::fmt(err, f), + } + } + } + + impl Error for LoadingError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(match self { + Self::LibraryLoadFailure(err) => err, + Self::MissingEntryPoint(err) => err, + }) + } + } + + impl From<MissingEntryPoint> for LoadingError { + fn from(err: MissingEntryPoint) -> Self { + Self::MissingEntryPoint(err) + } + } +} +#[cfg(feature = "loaded")] +pub use self::loaded::*; |