use std::ffi::CString;
use std::os::raw;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use super::dispatch;
pub use super::dispatch::*; use super::table;
use types::*;
use util;
use util::VkNullHandle;
#[cfg(not(target_os = "windows"))]
mod dl {
use std::ptr;
use std::io::{Error,ErrorKind,Result};
use std::ffi::{CStr,OsStr};
use std::os::unix::ffi::OsStrExt;
use libc;
use types::PFN_vkVoidFunction;
pub struct Library(*mut libc::c_void);
pub unsafe fn open<S: AsRef<OsStr>>(name: &S) -> Result<Library> {
let name = name.as_ref();
let mut buf: Vec<u8> = Vec::new();
let name_cstr = if name.len() > 0 && name.as_bytes()[name.len() - 1] == 0 {
CStr::from_bytes_with_nul_unchecked(name.as_bytes())
} else {
buf.extend_from_slice(name.as_bytes());
buf.push(0);
CStr::from_bytes_with_nul_unchecked(buf.as_slice())
};
libc::dlerror(); let handle = libc::dlopen(name_cstr.as_ptr(), libc::RTLD_LOCAL | libc::RTLD_LAZY);
if handle == ptr::null_mut() {
let msg_cstr = CStr::from_ptr(libc::dlerror());
return Err(Error::new(ErrorKind::Other, msg_cstr.to_string_lossy().to_string()));
}
Ok(Library(handle))
}
impl Library {
pub unsafe fn get(&self, name: &str) -> Result<Option<PFN_vkVoidFunction>> {
let mut v: Vec<u8> = Vec::new();
let name_cstr = if name.len() > 0 && name.as_bytes()[name.len() - 1] == 0 {
CStr::from_bytes_with_nul_unchecked(name.as_bytes())
} else {
v.extend_from_slice(name.as_bytes());
v.push(0);
CStr::from_bytes_with_nul_unchecked(v.as_slice())
};
libc::dlerror(); let sym = libc::dlsym(self.0, name_cstr.as_ptr());
if sym == ptr::null_mut() {
let msg = libc::dlerror();
if msg != ptr::null_mut() {
let msg_cstr = CStr::from_ptr(msg);
return Err(Error::new(ErrorKind::Other, msg_cstr.to_string_lossy().to_string()));
}
}
Ok(::std::mem::transmute(sym))
}
}
}
#[cfg(target_os = "windows")]
mod dl {
use winapi;
use kernel32;
use std::ptr;
use std::io::{Error,Result};
use std::os::raw;
use std::ffi::{CStr,OsStr};
use std::os::unix::ffi::OsStrExt;
use types::PFN_vkVoidFunction;
pub struct Library(winapi::HMODULE);
pub unsafe fn open<S: AsRef<OsStr>>(name: &S) -> Result<Library> {
let name_wide: Vec<u16> = name.as_ref().encode_wide().chain(Some(0)).collect();
SetLastError(0); let handle = kernel32::LoadLibraryW(name_wide.as_ptr());
if handle == ptr::null_mut() {
let err = kernel32::GetLastError();
if err == 0 {
return Err(Error::new(ErrorKind::Other, "LoadLibraryW didn't return any library handle"));
} else {
return Err(IoError::from_raw_os_error(err as i32));
}
}
Ok(Library(handle))
}
impl Library {
pub unsafe fn get(&self, name: &str) -> Result<Option<PFN_vkVoidFunction>> {
let mut v: Vec<u8> = Vec::new();
let name_cstr = if name.len() > 0 && name.as_bytes()[name.len() - 1] == 0 {
CStr::from_bytes_with_nul_unchecked(name.as_bytes())
} else {
v.extend_from_slice(name.as_bytes());
v.push(0);
CStr::from_bytes_with_nul_unchecked(v.as_slice())
};
SetLastError(0); let sym = GetProcAddress(self.0, name_cstr.as_ptr());
if sym == ptr::null_mut() {
let err = kernel32::GetLastError();
if err != 0 {
return Err(IoError::from_raw_os_error(err as i32));
}
}
Ok(::std::mem::transmute(sym))
}
}
}
#[inline]
fn wrap_proc_addr<T>(get_proc_addr: extern "system" fn (T,*const raw::c_char) -> Option<PFN_vkVoidFunction>, h: T, n: &str) -> util::VkResultObj<PFN_vkVoidFunction> {
let cn = CString::new(n).unwrap();
if let Some(f) = get_proc_addr(h, cn.as_ptr()) {
debug!("loaded function '{}'", n);
Ok(f)
} else {
warn!("Unable to load function '{}'", n);
Err(VK_ERROR_EXTENSION_NOT_PRESENT)
}
}
#[allow(non_camel_case_types)]
pub type PFN_vkGetInstanceProcAddr = extern "system" fn (VkInstance, *const raw::c_char) -> Option<PFN_vkVoidFunction>;
#[allow(non_camel_case_types)]
pub type PFN_vkGetDeviceProcAddr = extern "system" fn (VkDevice, *const raw::c_char) -> Option<PFN_vkVoidFunction>;
#[cfg(not(target_os = "windows"))]
const VULKAN_LIB_NAME : &str = "libvulkan.so";
#[cfg(target_os = "windows")]
const VULKAN_LIB_NAME : &str = "vulkan.dll";
lazy_static!{
static ref LOADER_DATA: Option<(table::VkLoaderTable,PFN_vkGetInstanceProcAddr)> = {
unsafe {
match dl::open(&VULKAN_LIB_NAME) {
Err(e) => {
error!("unable to load vulkan library: {}", e);
None
},
Ok(lib) => {
debug!("loaded {}", VULKAN_LIB_NAME);
let get_inst_proc_addr : Result<Option<PFN_vkGetInstanceProcAddr>,::std::io::Error> = ::std::mem::transmute(lib.get("vkGetInstanceProcAddr"));
match get_inst_proc_addr {
Ok(Some(get_inst_proc_addr)) => {
debug!("loaded vkGetInstanceProcAddr from library");
let mut tab : table::VkLoaderTable = ::std::mem::zeroed();
debug!("loading VkLoaderTable");
match tab.load(|n| wrap_proc_addr(get_inst_proc_addr, util::vk_null_handle(), n)) {
Ok(_) => Some((tab,get_inst_proc_addr)),
Err(e) => {
error!("unable to load vulkan table (loader): {:?}", e);
None
}
}
},
Ok(None) => {
error!("unable to find vkGetInstanceProcAddr function");
None
},
Err(e) => {
error!("unable to find vkGetInstanceProcAddr function: {}", e);
None
}
}
}
}
}
};
}
impl table::VkLoaderTable {
pub unsafe fn get() -> Option<&'static table::VkLoaderTable> {
if let Some((ref tab,_)) = *LOADER_DATA {
Some(tab)
} else {
None
}
}
}
static mut INSTANCE_DATA : Option<(table::VkInstanceTable,VkInstance)> = None;
static mut INSTANCE_INITIALIZED : AtomicUsize = ATOMIC_USIZE_INIT;
impl table::VkInstanceTable {
pub unsafe fn get() -> Option<&'static table::VkInstanceTable> {
if INSTANCE_INITIALIZED.load(Ordering::Relaxed) == 1 {
if let Some((ref t,_)) = INSTANCE_DATA {
return Some(t);
}
}
None
}
unsafe fn initialize_from<E>(instance: VkInstance, has_extension: E) where E: Fn(&str) -> bool {
if let Some((_, mut get_inst_proc_addr)) = *LOADER_DATA {
if let Ok(tmp) = wrap_proc_addr(get_inst_proc_addr, instance, "vkGetInstanceProcAddr") {
get_inst_proc_addr = ::std::mem::transmute(tmp);
let mut tab : table::VkInstanceTable = ::std::mem::zeroed();
tab.vkGetInstanceProcAddr = get_inst_proc_addr;
debug!("loading VkInstanceTable for {:?}", instance);
if tab.load(|n| wrap_proc_addr(get_inst_proc_addr, instance, n), has_extension).is_ok() {
INSTANCE_DATA = Some((tab, instance));
}
}
}
}
}
static mut DEVICE_DATA : Option<(table::VkDeviceTable,VkDevice)> = None;
static mut DEVICE_INITIALIZED : AtomicUsize = ATOMIC_USIZE_INIT;
impl table::VkDeviceTable {
pub unsafe fn get() -> Option<&'static table::VkDeviceTable> {
if DEVICE_INITIALIZED.load(Ordering::Relaxed) == 1 {
if let Some((ref t,_)) = DEVICE_DATA {
return Some(t);
}
}
None
}
unsafe fn initialize_from<E>(device: VkDevice, has_extension: E) where E: Fn(&str) -> bool {
if INSTANCE_INITIALIZED.load(Ordering::Relaxed) != 1 {
return;
};
if let Some((ref _it, instance)) = INSTANCE_DATA {
let mut get_device_proc_addr : PFN_vkGetDeviceProcAddr;
if let Ok(tmp) = wrap_proc_addr(_it.vkGetInstanceProcAddr, instance, "vkGetDeviceProcAddr") {
get_device_proc_addr = ::std::mem::transmute(tmp);
if let Ok(tmp) = wrap_proc_addr(get_device_proc_addr, device, "vkGetDeviceProcAddr") {
get_device_proc_addr = ::std::mem::transmute(tmp);
let mut tab : table::VkDeviceTable = ::std::mem::zeroed();
tab.vkGetDeviceProcAddr = get_device_proc_addr;
debug!("loading VkDeviceTable for {:?} and {:?}", instance, device);
if tab.load(|n| wrap_proc_addr(get_device_proc_addr, device, n), has_extension).is_ok() {
DEVICE_DATA = Some((tab, device));
}
}
}
}
}
}
#[allow(non_snake_case)]
pub unsafe fn vkGetInstanceProcAddr (instance: VkInstance, p_name: *const raw::c_char) -> Option<PFN_vkVoidFunction> {
if let Some(v) = dispatch::vkGetInstanceProcAddr(instance, p_name) {
Some(v)
} else if let Some((_, get_inst_proc_addr)) = *LOADER_DATA {
get_inst_proc_addr(instance, p_name)
} else {
None
}
}
#[allow(non_snake_case)]
pub unsafe fn vkCreateInstance (p_create_info: *const VkInstanceCreateInfo, p_allocator: *const VkAllocationCallbacks, p_instance: *mut VkInstance) -> VkResult {
if p_instance != util::vk_null() {
*p_instance = VkInstance::null();
}
if INSTANCE_INITIALIZED.compare_and_swap(0, 2, Ordering::Relaxed) != 0 {
warn!("unable to call 'vkCreateInstance': multiple instances of 'VkInstance' are not supported!");
return VK_ERROR_INITIALIZATION_FAILED;
}
let mut instance = VkInstance::null();
let result = dispatch::vkCreateInstance(p_create_info, p_allocator, &mut instance);
if result == VK_SUCCESS {
if p_instance != util::vk_null() {
*p_instance = instance;
}
let extensions = if !p_create_info.is_null() {
util::extensions_list_to_set((*p_create_info).ppEnabledExtensionNames, (*p_create_info).enabledExtensionCount)
} else {
::std::collections::BTreeSet::new()
};
table::VkInstanceTable::initialize_from(instance, |e| extensions.contains(e));
INSTANCE_INITIALIZED.store(1, Ordering::Relaxed);
} else {
INSTANCE_INITIALIZED.store(0, Ordering::Relaxed);
}
result
}
#[allow(non_snake_case)]
pub unsafe fn vkCurrentInstance() -> Option<VkInstance> {
if INSTANCE_INITIALIZED.load(Ordering::Relaxed) == 1 {
if let Some((_,i)) = INSTANCE_DATA {
return Some(i);
}
}
None
}
#[allow(non_snake_case)]
pub unsafe fn vkDestroyInstance (instance: VkInstance, p_allocator: *const VkAllocationCallbacks) {
dispatch::vkDestroyInstance(instance, p_allocator);
if INSTANCE_INITIALIZED.compare_and_swap(1, 2, Ordering::Relaxed) == 1 {
INSTANCE_DATA = None;
DEVICE_INITIALIZED.store(0, Ordering::Relaxed);
} else {
warn!("unable to destroy VkInstanceTable, was not initialized");
}
}
#[allow(non_snake_case)]
pub unsafe fn vkCreateDevice (physical_device: VkPhysicalDevice, p_create_info: *const VkDeviceCreateInfo, p_allocator: *const VkAllocationCallbacks, p_device: *mut VkDevice) -> VkResult {
if p_device != util::vk_null() {
*p_device = VkDevice::null();
}
if DEVICE_INITIALIZED.compare_and_swap(0, 2, Ordering::Relaxed) != 0 {
warn!("unable to call 'vkCreateDevice': multiple instances of 'VkDevice' are not supported!");
return VK_ERROR_INITIALIZATION_FAILED;
}
let mut device = VkDevice::null();
let result = dispatch::vkCreateDevice(physical_device, p_create_info, p_allocator, &mut device);
if result == VK_SUCCESS {
if p_device != util::vk_null() {
*p_device = device;
}
let extensions = if !p_create_info.is_null() {
util::extensions_list_to_set((*p_create_info).ppEnabledExtensionNames, (*p_create_info).enabledExtensionCount)
} else {
::std::collections::BTreeSet::new()
};
table::VkDeviceTable::initialize_from(device, |e| extensions.contains(e));
DEVICE_INITIALIZED.store(1, Ordering::Relaxed);
} else {
DEVICE_INITIALIZED.store(0, Ordering::Relaxed);
}
result
}
#[allow(non_snake_case)]
pub unsafe fn vkCurrentDevice<'l>() -> Option<VkDevice> {
if INSTANCE_INITIALIZED.load(Ordering::Relaxed) == 1 {
if let Some((_,d)) = DEVICE_DATA {
return Some(d);
}
}
None
}
#[allow(non_snake_case)]
pub unsafe fn vkDestroyDevice (device: VkDevice, p_allocator: *const VkAllocationCallbacks) {
dispatch::vkDestroyDevice(device, p_allocator);
if DEVICE_INITIALIZED.compare_and_swap(1, 2, Ordering::Relaxed) == 1 {
DEVICE_DATA = None;
DEVICE_INITIALIZED.store(0, Ordering::Relaxed);
} else {
warn!("unable to destroy VkDeviceTable, was not initialized");
}
}