ash 0.35.2+1.2.203

Vulkan bindings for Rust
Documentation
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,
    #[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| unsafe {
            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);

        Self {
            static_fn,
            entry_fn_1_0,
            entry_fn_1_1,
            entry_fn_1_2,
            #[cfg(feature = "loaded")]
            _lib_guard: None,
        }
    }

    pub fn fp_v1_0(&self) -> &vk::EntryFnV1_0 {
        &self.entry_fn_1_0
    }

    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(()) }
    /// ```
    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 = b"vkEnumerateInstanceVersion\0".as_ptr() as *const _;
                mem::transmute(
                    self.static_fn
                        .get_instance_proc_addr(vk::Instance::null(), name),
                )
            };
            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`].
    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>
    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>
    pub fn enumerate_instance_extension_properties(
        &self,
    ) -> VkResult<Vec<vk::ExtensionProperties>> {
        unsafe {
            read_into_uninitialized_vector(|count, data| {
                self.entry_fn_1_0
                    .enumerate_instance_extension_properties(ptr::null(), count, data)
            })
        }
    }

    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetInstanceProcAddr.html>
    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 {
    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.
    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 {
    pub fn fp_v1_2(&self) -> &vk::EntryFnV1_2 {
        &self.entry_fn_1_2
    }
}

#[cfg(feature = "linked")]
#[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
impl Default for Entry {
    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 {
                LoadingError::LibraryLoadFailure(err) => fmt::Display::fmt(err, f),
                LoadingError::MissingEntryPoint(err) => fmt::Display::fmt(err, f),
            }
        }
    }

    impl Error for LoadingError {
        fn source(&self) -> Option<&(dyn Error + 'static)> {
            Some(match self {
                LoadingError::LibraryLoadFailure(err) => err,
                LoadingError::MissingEntryPoint(err) => err,
            })
        }
    }

    impl From<MissingEntryPoint> for LoadingError {
        fn from(err: MissingEntryPoint) -> Self {
            Self::MissingEntryPoint(err)
        }
    }
}
#[cfg(feature = "loaded")]
pub use self::loaded::*;