alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates volcanic src loader library.rs
use super::{CmdsDevice, CmdsGlobal, CmdsInstance, LoadError};
use crate::{VkDevice, VkInstance};
use std::{ffi::OsStr, ops::Deref, path::PathBuf, sync::Arc};
#[derive(Clone)]
pub struct Loader {
library: Arc<libloading::Library>,
commands: CmdsGlobal,
}
impl Loader {
/// # Safety
/// The system Vulkan library and its initialization routines must be safe to load
pub unsafe fn new() -> Result<Self, LoadError> {
let path = if cfg!(target_os = "windows") {
"vulkan-1.dll"
} else if cfg!(target_os = "macos") {
"libvulkan.1.dylib"
} else {
"libvulkan.so.1"
};
unsafe { Self::from_path(path) }
}
/// # Safety
/// The library must implement the Vulkan loader ABI and be safe to load
pub unsafe fn from_path(path: impl AsRef<OsStr>) -> Result<Self, LoadError> {
let path = path.as_ref();
let library =
unsafe { libloading::Library::new(path) }.map_err(|source| LoadError::Library {
path: PathBuf::from(path),
source,
})?;
let commands = unsafe { CmdsGlobal::load(&library)? };
for (name, loaded) in [
(c"vkCreateInstance", commands.vkCreateInstance.is_some()),
(
c"vkEnumerateInstanceLayerProperties",
commands.vkEnumerateInstanceLayerProperties.is_some(),
),
(
c"vkEnumerateInstanceExtensionProperties",
commands.vkEnumerateInstanceExtensionProperties.is_some(),
),
] {
if !loaded {
return Err(LoadError::MissingBootstrap { name });
}
}
Ok(Self {
library: Arc::new(library),
commands,
})
}
/// # Safety
/// The instance must belong to this loader and remain live while loading its commands
pub unsafe fn load_instance(
&self,
instance: VkInstance,
) -> Result<InstanceCommands, LoadError> {
let commands = unsafe { CmdsInstance::load(&self.library, instance)? };
Ok(InstanceCommands {
library: self.library.clone(),
commands,
})
}
}
impl Deref for Loader {
type Target = CmdsGlobal;
fn deref(&self) -> &Self::Target {
&self.commands
}
}
#[derive(Clone)]
pub struct InstanceCommands {
library: Arc<libloading::Library>,
commands: CmdsInstance,
}
impl InstanceCommands {
/// # Safety
/// The device must belong to this instance and remain live while loading its commands
pub unsafe fn load_device(&self, device: VkDevice) -> Result<DeviceCommands, LoadError> {
let get_device_proc_addr =
self.commands
.vkGetDeviceProcAddr
.ok_or(LoadError::MissingBootstrap {
name: c"vkGetDeviceProcAddr",
})?;
let commands = unsafe { CmdsDevice::from_proc_addr(get_device_proc_addr, device) };
Ok(DeviceCommands {
_library: self.library.clone(),
commands,
})
}
}
impl Deref for InstanceCommands {
type Target = CmdsInstance;
fn deref(&self) -> &Self::Target {
&self.commands
}
}
#[derive(Clone)]
pub struct DeviceCommands {
_library: Arc<libloading::Library>,
commands: CmdsDevice,
}
impl Deref for DeviceCommands {
type Target = CmdsDevice;
fn deref(&self) -> &Self::Target {
&self.commands
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn library_errors_preserve_the_path_and_cause() {
let path = std::env::temp_dir()
.join(format!("volcanic-missing-{}", std::process::id()))
.join("loader");
let error = match unsafe { Loader::from_path(&path) } {
Ok(_) => panic!("unexpected loader at missing path"),
Err(error) => error,
};
assert!(matches!(&error, LoadError::Library { path: actual, .. } if actual == &path));
assert!(error.to_string().contains(&path.display().to_string()));
assert!(error.source().is_some());
}
#[test]
fn owned_commands_can_be_shared_across_threads() {
fn shared<T: Send + Sync + Clone>() {}
shared::<Loader>();
shared::<InstanceCommands>();
shared::<DeviceCommands>();
}
#[test]
#[ignore = "requires a Vulkan loader"]
fn instance_commands_keep_the_library_loaded() {
use crate::{
VK_API_VERSION_1_0, VkApplicationInfo, VkInstanceCreateFlags, VkInstanceCreateInfo,
VkResult, VkStructureType,
};
use std::ptr::{null, null_mut};
unsafe {
let loader = Loader::new().unwrap();
let library = Arc::downgrade(&loader.library);
let application = VkApplicationInfo {
sType: VkStructureType::ApplicationInfo,
pNext: null(),
pApplicationName: c"Volcanic loader test".as_ptr(),
applicationVersion: 0,
pEngineName: null(),
engineVersion: 0,
apiVersion: VK_API_VERSION_1_0,
};
let info = VkInstanceCreateInfo {
sType: VkStructureType::InstanceCreateInfo,
pNext: null(),
flags: VkInstanceCreateFlags::empty(),
pApplicationInfo: &application,
enabledLayerCount: 0,
ppEnabledLayerNames: null(),
enabledExtensionCount: 0,
ppEnabledExtensionNames: null(),
};
let mut instance = null_mut();
assert_eq!(
loader.vkCreateInstance(&info, null(), &mut instance),
VkResult::Success
);
let commands = loader.load_instance(instance).unwrap();
let clone = commands.clone();
drop(loader);
drop(commands);
assert!(library.upgrade().is_some());
clone.vkDestroyInstance(instance, null());
drop(clone);
assert!(library.upgrade().is_none());
}
}
}