use crate::core::Instance;
use crate::{core as vk, core::make_api_version};
use libloading::Library;
use std::{
ffi::{CStr, OsStr},
sync::Arc,
};
#[derive(Debug, PartialEq)]
pub enum LoadError {
InvalidMetadata(&'static str),
LibraryOpen(String),
MissingSymbol(&'static str),
NullHandle(&'static str),
}
impl std::fmt::Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidMetadata(s) => write!(f, "invalid Vulkan import metadata: {s}"),
Self::LibraryOpen(s) => write!(f, "Vulkan loader: {s}"),
Self::MissingSymbol(s) => write!(f, "missing Vulkan command: {s}"),
Self::NullHandle(s) => write!(f, "null Vulkan {s} handle"),
}
}
}
impl std::error::Error for LoadError {}
#[derive(Debug, PartialEq)]
pub enum CallError {
EnumerationUnstable {
attempts: u32,
},
InitializationFailed(super::InitializationFailure),
Vulkan(vk::Result),
MissingCommand(&'static str),
InvalidMetadata(&'static str),
FeatureNotEnabled {
structure: vk::StructureType,
member: &'static str,
},
HostAllocationFailed,
InvalidOutput(&'static str),
CountOverflow(usize),
SliceLengthMismatch {
parameter: &'static str,
expected: usize,
actual: usize,
},
}
impl std::fmt::Display for CallError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::EnumerationUnstable { attempts } => write!(
f,
"Vulkan enumeration did not stabilize after {attempts} attempts"
),
Self::InitializationFailed(failure) => failure.fmt(f),
Self::Vulkan(r) => write!(f, "Vulkan result {}", r.as_raw()),
Self::MissingCommand(n) => write!(f, "missing Vulkan command: {n}"),
Self::InvalidMetadata(reason) => write!(f, "invalid Vulkan metadata: {reason}"),
Self::FeatureNotEnabled { structure, member } => write!(
f,
"Vulkan feature {structure:?}::{member} is not recorded as enabled"
),
Self::HostAllocationFailed => f.write_str("Vulkan host allocation failed"),
Self::InvalidOutput(reason) => write!(f, "invalid Vulkan output: {reason}"),
Self::CountOverflow(count) => write!(f, "Vulkan count exceeds u32: {count}"),
Self::SliceLengthMismatch {
parameter,
expected,
actual,
} => {
write!(f, "Vulkan {parameter} length {actual}, expected {expected}")
}
}
}
}
impl std::error::Error for CallError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InitializationFailed(failure) => Some(failure),
_ => None,
}
}
}
impl From<vk::Result> for CallError {
fn from(r: vk::Result) -> Self {
Self::Vulkan(r)
}
}
#[derive(Clone)]
pub struct Entry {
pub(crate) library: Option<Arc<Library>>,
pub(crate) gipa: unsafe extern "system" fn(
vk::InstanceHandle,
*const std::ffi::c_char,
) -> vk::PFN_vkVoidFunction,
fp: vk::FnEntry,
}
impl Entry {
pub unsafe fn new() -> Result<Self, LoadError> {
#[cfg(windows)]
let path = "vulkan-1.dll";
#[cfg(any(target_os = "macos", target_os = "ios"))]
let path = "libvulkan.dylib";
#[cfg(any(target_os = "android", target_os = "fuchsia"))]
let path = "libvulkan.so";
#[cfg(not(any(
windows,
target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "fuchsia"
)))]
let path = "libvulkan.so.1";
unsafe { Self::new_from_path(path) }
}
pub unsafe fn new_from_path(path: impl AsRef<OsStr>) -> Result<Self, LoadError> {
let library =
Arc::new(unsafe { Library::new(path) }.map_err(|e| LoadError::LibraryOpen(e.to_string()))?);
let gipa = unsafe {
library.get::<unsafe extern "system" fn(
vk::InstanceHandle,
*const std::ffi::c_char,
) -> vk::PFN_vkVoidFunction>(b"vkGetInstanceProcAddr\0")
}
.map_err(|_| LoadError::MissingSymbol("vkGetInstanceProcAddr"))?;
let mut entry = unsafe { Self::new_from_resolver(*gipa) }?;
entry.library = Some(library);
Ok(entry)
}
pub unsafe fn new_from_resolver(
gipa: unsafe extern "system" fn(
vk::InstanceHandle,
*const std::ffi::c_char,
) -> vk::PFN_vkVoidFunction,
) -> Result<Self, LoadError> {
let fp = unsafe { vk::FnEntry::new_from_resolver(gipa) };
for (present, name) in [
(fp.create_instance.is_some(), "vkCreateInstance"),
(
fp.enumerate_instance_extension_properties.is_some(),
"vkEnumerateInstanceExtensionProperties",
),
(
fp.enumerate_instance_layer_properties.is_some(),
"vkEnumerateInstanceLayerProperties",
),
] {
if !present {
return Err(LoadError::MissingSymbol(name));
}
}
Ok(Self {
library: None,
gipa,
fp,
})
}
pub fn fp(&self) -> &vk::FnEntry {
&self.fp
}
pub unsafe fn get_instance_proc_addr(
&self,
instance: vk::InstanceHandle,
name: &CStr,
) -> vk::PFN_vkVoidFunction {
unsafe { (self.gipa)(instance, name.as_ptr()) }
}
pub unsafe fn create_instance(
&self,
create_info: &vk::InstanceCreateInfo,
allocator: Option<&vk::AllocationCallbacks>,
) -> Result<Instance, CallError> {
unsafe { crate::core::create_instance(self, create_info, allocator) }
}
}
use crate::core::command::commands;
commands! { Entry, entry;
pub fn enumerate_instance_extension_properties(
layer_name: Option<&std::ffi::CStr>,
) -> Result<Vec<vk::ExtensionProperties>, CallError> {
let f = entry
.fp
.enumerate_instance_extension_properties
.ok_or(CallError::MissingCommand(
"vkEnumerateInstanceExtensionProperties",
))?;
let layer_ptr = layer_name.map_or(std::ptr::null(), |l| l.as_ptr());
unsafe { super::enumerate::enumerate(|count, data| f(layer_ptr, count, data)) }
}
pub fn enumerate_instance_layer_properties() -> Result<Vec<vk::LayerProperties>, CallError> {
let f = entry
.fp
.enumerate_instance_layer_properties
.ok_or(CallError::MissingCommand(
"vkEnumerateInstanceLayerProperties",
))?;
unsafe { super::enumerate::enumerate(|count, data| f(count, data)) }
}
pub fn enumerate_instance_version() -> Result<u32, vk::Result> {
let Some(f) = entry.fp.enumerate_instance_version else {
return Ok(make_api_version(0, 1, 0, 0));
};
let mut version = 0;
let result = unsafe { f(&mut version) };
if result == vk::Result::SUCCESS {
Ok(version)
} else {
Err(result)
}
}
}