use crate::core as vk;
use libloading::Library;
use std::{ffi::CStr, sync::Arc};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CreatedHandle {
Instance(vk::InstanceHandle),
Device(vk::DeviceHandle),
}
#[derive(Debug)]
#[must_use = "a created native object still needs explicit recovery"]
pub struct InitializationFailure {
pub(crate) handle: CreatedHandle,
pub(crate) missing_command: &'static str,
pub(crate) library: Option<Arc<Library>>,
pub(crate) instance: vk::InstanceHandle,
pub(crate) gipa: unsafe extern "system" fn(
vk::InstanceHandle,
*const std::ffi::c_char,
) -> vk::PFN_vkVoidFunction,
pub(crate) gdpa: vk::PFN_vkGetDeviceProcAddr,
}
impl PartialEq for InitializationFailure {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
&& self.instance == other.instance
&& self.missing_command == other.missing_command
}
}
impl std::fmt::Display for InitializationFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"missing {}; {:?} requires recovery",
self.missing_command, self.handle
)
}
}
impl std::error::Error for InitializationFailure {}
impl InitializationFailure {
fn exported(&self, name: &CStr) -> vk::PFN_vkVoidFunction {
unsafe {
self.library.as_ref().and_then(|library| {
library
.get::<unsafe extern "system" fn()>(name.to_bytes_with_nul())
.ok()
.map(|symbol| *symbol)
})
}
}
pub fn handle(&self) -> CreatedHandle {
self.handle
}
pub fn missing_command(&self) -> &'static str {
self.missing_command
}
pub unsafe fn destroy(self, allocator: Option<&vk::AllocationCallbacks>) -> Result<(), Self> {
let allocator = allocator.map_or(std::ptr::null(), |value| value);
let _retained = &self.library;
match self.handle {
CreatedHandle::Instance(handle) => {
let function = unsafe { (self.gipa)(handle, c"vkDestroyInstance".as_ptr()) }
.or_else(|| self.exported(c"vkDestroyInstance"));
let function = unsafe {
std::mem::transmute::<vk::PFN_vkVoidFunction, vk::PFN_vkDestroyInstance>(function)
};
let Some(function) = function else {
return Err(self);
};
unsafe { function(handle, allocator) };
}
CreatedHandle::Device(handle) => {
let function = unsafe {
self
.gdpa
.and_then(|load| load(handle, c"vkDestroyDevice".as_ptr()))
.or_else(|| (self.gipa)(self.instance, c"vkDestroyDevice".as_ptr()))
.or_else(|| self.exported(c"vkDestroyDevice"))
};
let function = unsafe {
std::mem::transmute::<vk::PFN_vkVoidFunction, vk::PFN_vkDestroyDevice>(function)
};
let Some(function) = function else {
return Err(self);
};
unsafe { function(handle, allocator) };
}
}
Ok(())
}
}
#[cfg(test)]
#[path = "../../../test/rs/load/test_failure.rs"]
mod tests;