use shared_library;
use std::error;
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::os::raw::c_char;
use std::os::raw::c_void;
use std::path::Path;
use SafeDeref;
use vk;
pub unsafe trait Loader {
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char)
-> extern "system" fn() -> ();
}
unsafe impl<T> Loader for T
where T: SafeDeref,
T::Target: Loader
{
#[inline]
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char)
-> extern "system" fn() -> () {
(**self).get_instance_proc_addr(instance, name)
}
}
pub struct DynamicLibraryLoader {
vk_lib: shared_library::dynamic_library::DynamicLibrary,
get_proc_addr: extern "system" fn(instance: vk::Instance, pName: *const c_char)
-> extern "system" fn() -> (),
}
impl DynamicLibraryLoader {
pub unsafe fn new<P>(path: P) -> Result<DynamicLibraryLoader, LoadingError>
where P: AsRef<Path>
{
let vk_lib = shared_library::dynamic_library::DynamicLibrary::open(Some(path.as_ref()))
.map_err(LoadingError::LibraryLoadFailure)?;
let get_proc_addr = {
let ptr: *mut c_void =
vk_lib
.symbol("vkGetInstanceProcAddr")
.map_err(|_| {
LoadingError::MissingEntryPoint("vkGetInstanceProcAddr".to_owned())
})?;
mem::transmute(ptr)
};
Ok(DynamicLibraryLoader {
vk_lib,
get_proc_addr,
})
}
}
unsafe impl Loader for DynamicLibraryLoader {
#[inline]
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char)
-> extern "system" fn() -> () {
(self.get_proc_addr)(instance, name)
}
}
pub struct FunctionPointers<L> {
loader: L,
entry_points: vk::EntryPoints,
}
impl<L> FunctionPointers<L> {
pub fn new(loader: L) -> FunctionPointers<L>
where L: Loader
{
let entry_points = vk::EntryPoints::load(|name| unsafe { mem::transmute(loader.get_instance_proc_addr(0, name.as_ptr())) });
FunctionPointers {
loader,
entry_points,
}
}
#[inline]
pub(crate) fn entry_points(&self) -> &vk::EntryPoints {
&self.entry_points
}
#[inline]
pub fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char)
-> extern "system" fn() -> ()
where L: Loader
{
self.loader.get_instance_proc_addr(instance, name)
}
}
#[macro_export]
macro_rules! statically_linked_vulkan_loader {
() => ({
extern "C" {
fn vkGetInstanceProcAddr(instance: vk::Instance, pName: *const c_char)
-> vk::PFN_vkVoidFunction;
}
struct StaticallyLinkedVulkanLoader;
unsafe impl Loader for StaticallyLinkedVulkanLoader {
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char)
-> extern "system" fn() -> () {
unsafe { vkGetInstanceProcAddr(instance, name) }
}
}
StaticallyLinkedVulkanLoader
})
}
pub fn auto_loader()
-> Result<&'static FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError>
{
#[cfg(target_os = "ios")]
#[allow(non_snake_case)]
fn def_loader_impl() -> Result<Box<Loader + Send + Sync>, LoadingError> {
let loader = statically_linked_vulkan_loader!();
Ok(Box::new(loader))
}
#[cfg(not(target_os = "ios"))]
fn def_loader_impl() -> Result<Box<dyn Loader + Send + Sync>, LoadingError> {
#[cfg(windows)]
fn get_path() -> &'static Path {
Path::new("vulkan-1.dll")
}
#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
fn get_path() -> &'static Path {
Path::new("libvulkan.so.1")
}
#[cfg(target_os = "macos")]
fn get_path() -> &'static Path {
Path::new("libvulkan.1.dylib")
}
#[cfg(target_os = "android")]
fn get_path() -> &'static Path {
Path::new("libvulkan.so")
}
let loader = unsafe { DynamicLibraryLoader::new(get_path())? };
Ok(Box::new(loader))
}
lazy_static! {
static ref DEFAULT_LOADER: Result<FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError> = {
def_loader_impl().map(FunctionPointers::new)
};
}
match DEFAULT_LOADER.deref() {
&Ok(ref ptr) => Ok(ptr),
&Err(ref err) => Err(err.clone()),
}
}
#[derive(Debug, Clone)]
pub enum LoadingError {
LibraryLoadFailure(String),
MissingEntryPoint(String),
}
impl error::Error for LoadingError {
#[inline]
fn description(&self) -> &str {
match *self {
LoadingError::LibraryLoadFailure(_) => {
"failed to load the Vulkan shared library"
},
LoadingError::MissingEntryPoint(_) => {
"one of the entry points required to be supported by the Vulkan implementation \
is missing"
},
}
}
}
impl fmt::Display for LoadingError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
#[cfg(test)]
mod tests {
use instance::loader::DynamicLibraryLoader;
use instance::loader::LoadingError;
#[test]
fn dl_open_error() {
unsafe {
match DynamicLibraryLoader::new("_non_existing_library.void") {
Err(LoadingError::LibraryLoadFailure(_)) => (),
_ => panic!(),
}
}
}
}