use crate::{vtbl::GlobalCommands, *};
use alloc::sync::Arc;
use core::{
ffi::{CStr, c_char},
ops::Deref,
};
#[cfg(feature = "loaded")]
use libloading::{AsFilename, Library};
use thiserror::Error;
#[derive(Debug, Clone, Copy)]
pub struct ProcAddr(pub FN_vkVoidFunction);
impl ProcAddr {
pub unsafe fn cast<T: Copy>(self) -> T {
const { assert!(core::mem::size_of::<T>() == core::mem::size_of::<usize>()) }
let ptr = ((&self.0) as *const FN_vkVoidFunction).cast::<T>();
unsafe { *ptr }
}
}
#[derive(Debug, Clone, Copy)]
pub struct GetInstanceProcAddr(pub FN_vkGetInstanceProcAddr);
impl GetInstanceProcAddr {
pub fn get(&self, instance: VkInstance, name: *const c_char) -> Option<ProcAddr> {
unsafe { (self.0)(instance, name).map(ProcAddr) }
}
}
#[derive(Debug, Clone, Copy)]
pub struct GetDeviceProcAddr(pub FN_vkGetDeviceProcAddr);
impl GetDeviceProcAddr {
pub fn get(&self, device: VkDevice, name: *const c_char) -> Option<ProcAddr> {
unsafe { (self.0)(device, name).map(ProcAddr) }
}
}
#[derive(Debug, Error)]
pub enum LoadingError {
#[error(transparent)]
#[cfg(feature = "loaded")]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
LoadError(libloading::Error),
#[error(transparent)]
#[cfg(feature = "loaded")]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
SymbolLoadError(libloading::Error),
#[error("The command `{0}` is not available in the Vulkan library")]
MissingGlobalCommand(&'static str),
}
#[derive(Debug)]
pub struct Entry {
pub vkGetInstanceProcAddr: FN_vkGetInstanceProcAddr,
_lib: Option<Library>,
}
impl Entry {
#[cfg(any(feature = "loaded", feature = "linked"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "loaded", feature = "linked"))))]
pub unsafe fn new() -> Result<Arc<Self>, LoadingError> {
#[cfg(feature = "linked")]
{
return Ok(Self::linked());
}
#[cfg(feature = "loaded")]
{
return unsafe { Self::load() };
}
unreachable!()
}
pub unsafe fn from_vkGetInstanceProcAddr(
vkGetInstanceProcAddr: FN_vkGetInstanceProcAddr,
) -> Arc<Self> {
Arc::new(Self {
vkGetInstanceProcAddr,
_lib: None,
})
}
}
impl Entry {
#[cfg(feature = "linked")]
#[cfg_attr(docsrs, doc(cfg(feature = "linked")))]
pub fn linked() -> Arc<Self> {
Arc::new(Self {
vkGetInstanceProcAddr,
_lib: None,
})
}
}
#[cfg(feature = "linked")]
unsafe extern "system" {
unsafe fn vkGetInstanceProcAddr(
instance: VkInstance,
pName: *const c_char,
) -> PFN_vkVoidFunction;
}
#[cfg(feature = "loaded")]
#[cfg_attr(docsrs, doc(cfg(feature = "loaded")))]
impl Entry {
#[cfg(windows)]
pub const DEFAULT_LIB_PATH: &'static str = "vulkan-1.dll";
#[cfg(all(
unix,
not(any(
target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "fuchsia"
))
))]
pub const DEFAULT_LIB_PATH: &'static str = "libvulkan.so.1";
#[cfg(any(target_os = "android", target_os = "fuchsia"))]
pub const DEFAULT_LIB_PATH: &'static str = "libvulkan.so";
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub const DEFAULT_LIB_PATH: &'static str = "libvulkan.dylib";
pub unsafe fn load() -> Result<Arc<Self>, LoadingError> {
unsafe { Self::load_from(Self::DEFAULT_LIB_PATH) }
}
pub unsafe fn load_from(path: impl AsFilename) -> Result<Arc<Self>, LoadingError> {
let lib = unsafe { Library::new(path) }.map_err(LoadingError::LoadError)?;
let vkGetInstanceProcAddr = unsafe {
lib.get(b"vkGetInstanceProcAddr\0")
.map(|a| *a)
.map_err(LoadingError::SymbolLoadError)?
};
Ok(Arc::new(Self {
vkGetInstanceProcAddr,
_lib: Some(lib),
}))
}
}
impl Entry {
pub unsafe fn GetInstanceProcAddr(
&self,
instance: VkInstance,
name: *const c_char,
) -> Option<ProcAddr> {
unsafe { (self.vkGetInstanceProcAddr)(instance, name).map(ProcAddr) }
}
}
#[derive(Debug)]
pub struct Vulkan {
pub entry: Arc<Entry>,
pub global_commands: GlobalCommands,
}
impl Vulkan {
#[cfg(any(feature = "loaded", feature = "linked"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "loaded", feature = "linked"))))]
pub unsafe fn new() -> Result<Arc<Self>, LoadingError> {
unsafe { Self::from_entry(Entry::new()?) }
}
pub unsafe fn from_entry(entry: Arc<Entry>) -> Result<Arc<Self>, LoadingError> {
Ok(Arc::new(Self {
global_commands: unsafe { GlobalCommands::new(&entry)? },
entry,
}))
}
}
impl Vulkan {
pub unsafe fn GetInstanceProcAddr(
&self,
instance: VkInstance,
name: *const c_char,
) -> Option<ProcAddr> {
unsafe { self.entry.GetInstanceProcAddr(instance, name) }
}
}
impl Deref for Vulkan {
type Target = GlobalCommands;
fn deref(&self) -> &Self::Target {
&self.global_commands
}
}