#![allow(clippy::all, unreachable_patterns)]
#![doc(html_logo_url = "https://gitlab.com/Friz64/erupt/-/raw/main/logo.svg")]
mod generated;
pub mod utils;
use fmt::Debug;
pub use generated::*;
use std::{
error::Error,
ffi::CStr,
fmt::{self, Display},
mem, ptr,
sync::Arc,
};
#[cfg(feature = "loading")]
pub use utils::loading::EntryLoader;
#[macro_export]
macro_rules! cstr {
($s:expr) => {
concat!($s, "\0").as_ptr().cast::<::std::os::raw::c_char>()
};
}
#[macro_export]
macro_rules! try_vk {
($expr:expr) => {
match $crate::utils::VulkanResult::result($expr) {
Ok(value) => value,
Err(raw) => return $crate::utils::VulkanResult::new_err(raw),
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! non_dispatchable_handle {
($name:ident, $ty:ident, $doc:literal, $doc_alias:literal) => {
#[doc = $doc]
#[doc(alias = $doc_alias)]
#[repr(transparent)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Default)]
pub struct $name(pub u64);
impl $crate::ObjectHandle for $name {
const TYPE: $crate::vk1_0::ObjectType = $crate::vk1_0::ObjectType::$ty;
fn to_raw(self) -> u64 {
self.0
}
fn from_raw(raw: u64) -> Self {
$name(raw)
}
}
impl std::fmt::Pointer for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "0x{:x}", self.0)
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "0x{:x}", self.0)
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! dispatchable_handle {
($name:ident, $ty:ident, $doc:literal, $doc_alias:literal) => {
#[doc = $doc]
#[doc(alias = $doc_alias)]
#[repr(transparent)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
pub struct $name(pub *mut ());
impl $crate::ObjectHandle for $name {
const TYPE: $crate::vk1_0::ObjectType = $crate::vk1_0::ObjectType::$ty;
fn to_raw(self) -> u64 {
self.0 as u64
}
fn from_raw(raw: u64) -> Self {
$name(raw as _)
}
}
unsafe impl Send for $name {}
unsafe impl Sync for $name {}
impl Default for $name {
fn default() -> $name {
$name(std::ptr::null_mut())
}
}
impl std::fmt::Pointer for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Pointer::fmt(&self.0, f)
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! bits_copy {
($dst:expr, $src:expr, $start:expr, $end:expr) => {{
let mut dst = $dst;
let src = $src;
let start: usize = $start;
let end: usize = $end;
let mask = !0 << start & !(!0 << end);
dst &= !mask;
dst |= (src << start) & mask;
dst
}};
}
const NOT_LOADED_MESSAGE: &str = "tried to call a function that isn't loaded: \
is the respective `enabled_extension_names` array correct? \
does the configured Vulkan version support this function?";
pub type SmallVec<T> = smallvec::SmallVec<[T; 8]>;
#[derive(Debug)]
pub enum LoaderError {
VulkanError(vk1_0::Result),
SymbolNotAvailable,
}
impl Display for LoaderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LoaderError::VulkanError(_) => {
write!(f, "a Vulkan function returned a negative `Result` value")
}
LoaderError::SymbolNotAvailable => {
write!(f, "a symbol was not available while it should have been")
}
}
}
}
impl Error for LoaderError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
LoaderError::VulkanError(vk_result) => Some(vk_result),
LoaderError::SymbolNotAvailable => None,
}
}
}
impl<T> CustomEntryLoader<T> {
pub unsafe fn with_library(
mut library: T,
mut symbol: impl FnMut(&mut T, *const std::os::raw::c_char) -> Option<vk1_0::PFN_vkVoidFunction>,
) -> Result<Self, LoaderError> {
Ok(
EntryEnabled::new(&mut library, &mut symbol).and_then(|entry_enabled| {
CustomEntryLoader::custom(library, &mut symbol, entry_enabled)
})?,
)
}
pub fn enabled(&self) -> &EntryEnabled {
&self.enabled
}
pub fn instance_version(&self) -> u32 {
self.enabled.instance_version
}
}
impl<T> Drop for CustomEntryLoader<T> {
fn drop(&mut self) {
if Arc::weak_count(&self.arc) != 0 {
panic!("attempting to drop a entry loader with active references to it");
}
}
}
impl<T> Debug for CustomEntryLoader<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Entry")
}
}
pub struct InstanceLoaderBuilder<'a> {
create_instance_fn: Option<
&'a mut dyn FnMut(
&vk1_0::InstanceCreateInfo,
Option<&vk1_0::AllocationCallbacks>,
) -> utils::VulkanResult<vk1_0::Instance>,
>,
symbol_fn: Option<
&'a mut dyn FnMut(
vk1_0::Instance,
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction>,
>,
allocation_callbacks: Option<&'a vk1_0::AllocationCallbacks>,
}
impl<'a> InstanceLoaderBuilder<'a> {
pub fn new() -> Self {
InstanceLoaderBuilder {
create_instance_fn: None,
symbol_fn: None,
allocation_callbacks: None,
}
}
pub fn create_instance_fn(
mut self,
create_instance: &'a mut dyn FnMut(
&vk1_0::InstanceCreateInfo,
Option<&vk1_0::AllocationCallbacks>,
) -> utils::VulkanResult<vk1_0::Instance>,
) -> Self {
self.create_instance_fn = Some(create_instance);
self
}
pub fn symbol_fn(
mut self,
symbol: &'a mut impl FnMut(
vk1_0::Instance,
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction>,
) -> Self {
self.symbol_fn = Some(symbol);
self
}
pub fn allocation_callbacks(mut self, allocator: &'a vk1_0::AllocationCallbacks) -> Self {
self.allocation_callbacks = Some(allocator);
self
}
pub unsafe fn build<T>(
self,
entry_loader: &'a CustomEntryLoader<T>,
create_info: &vk1_0::InstanceCreateInfo,
) -> Result<InstanceLoader, LoaderError> {
let instance = match self.create_instance_fn {
Some(create_instance) => create_instance(create_info, self.allocation_callbacks),
None => entry_loader.create_instance(create_info, self.allocation_callbacks),
};
let instance = instance.result().map_err(LoaderError::VulkanError)?;
let mut version = vk1_0::make_api_version(0, 1, 0, 0);
if !create_info.p_application_info.is_null() {
let user_version = (*create_info.p_application_info).api_version;
if user_version != 0 {
version = user_version;
}
}
let enabled_extensions = std::slice::from_raw_parts(
create_info.pp_enabled_extension_names,
create_info.enabled_extension_count as _,
);
let enabled_extensions: Vec<_> = enabled_extensions
.iter()
.map(|&ptr| CStr::from_ptr(ptr))
.collect();
let mut default_symbol = move |name| (entry_loader.get_instance_proc_addr)(instance, name);
let mut symbol: &mut dyn FnMut(
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction> = &mut default_symbol;
let mut user_symbol;
if let Some(internal_symbol) = self.symbol_fn {
user_symbol = move |name| internal_symbol(instance, name);
symbol = &mut user_symbol;
}
let all_physical_device_extension_properties =
all_physical_device_extension_properties(&mut symbol, instance)?;
let available_device_extensions: Vec<_> = all_physical_device_extension_properties
.iter()
.map(|properties| CStr::from_ptr(properties.extension_name.as_ptr()))
.collect();
let instance_enabled =
InstanceEnabled::new(version, &enabled_extensions, &available_device_extensions)?;
InstanceLoader::custom(entry_loader, instance, instance_enabled, symbol)
}
}
impl InstanceLoader {
#[inline]
pub unsafe fn new<T>(
entry_loader: &CustomEntryLoader<T>,
create_info: &vk1_0::InstanceCreateInfo,
) -> Result<InstanceLoader, LoaderError> {
InstanceLoaderBuilder::new().build(entry_loader, create_info)
}
pub fn enabled(&self) -> &InstanceEnabled {
&self.enabled
}
}
impl Drop for InstanceLoader {
fn drop(&mut self) {
if Arc::weak_count(&self.arc) != 0 {
panic!("attempting to drop a instance loader with active references to it");
}
}
}
unsafe fn all_physical_device_extension_properties(
symbol: &mut impl FnMut(*const std::os::raw::c_char) -> Option<vk1_0::PFN_vkVoidFunction>,
instance: vk1_0::Instance,
) -> Result<Vec<vk1_0::ExtensionProperties>, LoaderError> {
let enumerate_physical_devices: vk1_0::PFN_vkEnumeratePhysicalDevices = mem::transmute(
symbol(vk1_0::FN_ENUMERATE_PHYSICAL_DEVICES).ok_or(LoaderError::SymbolNotAvailable)?,
);
let enumerate_device_extension_properties: vk1_0::PFN_vkEnumerateDeviceExtensionProperties =
mem::transmute(
symbol(vk1_0::FN_ENUMERATE_DEVICE_EXTENSION_PROPERTIES)
.ok_or(LoaderError::SymbolNotAvailable)?,
);
let mut physical_device_count = 0;
let result = enumerate_physical_devices(instance, &mut physical_device_count, ptr::null_mut());
if result.0 < 0 {
return Err(LoaderError::VulkanError(result));
}
let mut physical_devices = vec![Default::default(); physical_device_count as usize];
let result = enumerate_physical_devices(
instance,
&mut physical_device_count,
physical_devices.as_mut_ptr(),
);
if result.0 < 0 {
return Err(LoaderError::VulkanError(result));
}
let mut all_physical_device_extension_properties = Vec::new();
for physical_device in physical_devices {
let mut property_count = 0;
let result = enumerate_device_extension_properties(
physical_device,
ptr::null(),
&mut property_count,
ptr::null_mut(),
);
if result.0 < 0 {
return Err(LoaderError::VulkanError(result));
}
let mut properties = vec![Default::default(); property_count as usize];
let result = enumerate_device_extension_properties(
physical_device,
ptr::null(),
&mut property_count,
properties.as_mut_ptr(),
);
if result.0 < 0 {
return Err(LoaderError::VulkanError(result));
}
all_physical_device_extension_properties.extend(properties.into_iter());
}
Ok(all_physical_device_extension_properties)
}
impl Debug for InstanceLoader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&self.handle, f)
}
}
pub struct DeviceLoaderBuilder<'a> {
create_device_fn: Option<
&'a mut dyn FnMut(
vk1_0::PhysicalDevice,
&vk1_0::DeviceCreateInfo,
Option<&vk1_0::AllocationCallbacks>,
) -> utils::VulkanResult<vk1_0::Device>,
>,
symbol_fn: Option<
&'a mut dyn FnMut(
vk1_0::Device,
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction>,
>,
allocation_callbacks: Option<&'a vk1_0::AllocationCallbacks>,
}
impl<'a> DeviceLoaderBuilder<'a> {
pub fn new() -> Self {
DeviceLoaderBuilder {
create_device_fn: None,
symbol_fn: None,
allocation_callbacks: None,
}
}
pub fn create_device_fn(
mut self,
create_device: &'a mut dyn FnMut(
vk1_0::PhysicalDevice,
&vk1_0::DeviceCreateInfo,
Option<&vk1_0::AllocationCallbacks>,
) -> utils::VulkanResult<vk1_0::Device>,
) -> Self {
self.create_device_fn = Some(create_device);
self
}
pub fn symbol_fn(
mut self,
symbol: &'a mut impl FnMut(
vk1_0::Device,
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction>,
) -> Self {
self.symbol_fn = Some(symbol);
self
}
pub fn allocation_callbacks(mut self, allocator: &'a vk1_0::AllocationCallbacks) -> Self {
self.allocation_callbacks = Some(allocator);
self
}
pub unsafe fn build_with_existing_device(
self,
instance_loader: &'a InstanceLoader,
device: vk1_0::Device,
create_info: &vk1_0::DeviceCreateInfo,
) -> Result<DeviceLoader, LoaderError> {
let device_enabled = {
let enabled_extensions = std::slice::from_raw_parts(
create_info.pp_enabled_extension_names,
create_info.enabled_extension_count as _,
);
let enabled_extensions: Vec<_> = enabled_extensions
.iter()
.map(|&ptr| CStr::from_ptr(ptr))
.collect();
DeviceEnabled::new(&enabled_extensions)
};
let mut default_symbol = move |name| (instance_loader.get_device_proc_addr)(device, name);
let mut symbol: &mut dyn FnMut(
*const std::os::raw::c_char,
) -> Option<vk1_0::PFN_vkVoidFunction> = &mut default_symbol;
let mut user_symbol;
if let Some(internal_symbol) = self.symbol_fn {
user_symbol = move |name| internal_symbol(device, name);
symbol = &mut user_symbol;
}
DeviceLoader::custom(instance_loader, device, device_enabled, symbol)
}
pub unsafe fn build(
mut self,
instance_loader: &'a InstanceLoader,
physical_device: vk1_0::PhysicalDevice,
create_info: &vk1_0::DeviceCreateInfo,
) -> Result<DeviceLoader, LoaderError> {
let device = match &mut self.create_device_fn {
Some(create_device) => {
create_device(physical_device, create_info, self.allocation_callbacks)
}
None => instance_loader.create_device(
physical_device,
create_info,
self.allocation_callbacks,
),
};
let device = device.result().map_err(LoaderError::VulkanError)?;
self.build_with_existing_device(instance_loader, device, create_info)
}
}
impl DeviceLoader {
#[inline]
pub unsafe fn new(
instance_loader: &InstanceLoader,
physical_device: vk1_0::PhysicalDevice,
create_info: &vk1_0::DeviceCreateInfo,
) -> Result<DeviceLoader, LoaderError> {
DeviceLoaderBuilder::new().build(instance_loader, physical_device, create_info)
}
pub fn enabled(&self) -> &DeviceEnabled {
&self.enabled
}
}
impl Debug for DeviceLoader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&self.handle, f)
}
}
pub trait ObjectHandle: Default + PartialEq {
const TYPE: vk1_0::ObjectType;
fn null() -> Self {
Default::default()
}
fn is_null(self) -> bool {
self == Self::null()
}
fn to_raw(self) -> u64;
fn from_raw(raw: u64) -> Self;
}
pub trait ExtendableFrom<'a, T> {
#[must_use]
fn extend_from(mut self, addition: &'a mut T) -> Self
where
Self: Sized,
{
unsafe {
insert_ptr_chain(&mut self as *mut Self as _, addition as *mut T as _);
}
self
}
}
#[inline]
unsafe fn insert_ptr_chain(
mut host: *mut vk1_0::BaseOutStructure,
mut addition: *mut vk1_0::BaseOutStructure,
) {
let addition_head = addition;
let addition_end = loop {
let p_next = (*addition).p_next;
if p_next.is_null() {
break addition;
} else {
addition = p_next;
}
};
let prev_host_next = (*host).p_next;
(*host).p_next = addition_head;
(*addition_end).p_next = prev_host_next;
}
#[cfg(test)]
mod tests {
use super::*;
use std::ptr;
#[test]
fn ptr_chain_simple() {
let mut s1 = vk::BaseOutStructure {
s_type: vk::StructureType(1),
p_next: ptr::null_mut(),
};
let s1 = ptr::addr_of_mut!(s1);
let mut s2 = vk::BaseOutStructure {
s_type: vk::StructureType(2),
p_next: ptr::null_mut(),
};
let s2 = ptr::addr_of_mut!(s2);
let mut s3 = vk::BaseOutStructure {
s_type: vk::StructureType(3),
p_next: ptr::null_mut(),
};
let s3 = ptr::addr_of_mut!(s3);
unsafe {
(*s1).p_next = s2;
(*s2).p_next = s3;
}
let mut s4 = vk::BaseOutStructure {
s_type: vk::StructureType(4),
p_next: ptr::null_mut(),
};
let s4 = ptr::addr_of_mut!(s4);
let mut s5 = vk::BaseOutStructure {
s_type: vk::StructureType(5),
p_next: ptr::null_mut(),
};
let s5 = ptr::addr_of_mut!(s5);
let mut s6 = vk::BaseOutStructure {
s_type: vk::StructureType(6),
p_next: ptr::null_mut(),
};
let s6 = ptr::addr_of_mut!(s6);
unsafe {
super::insert_ptr_chain(s1, s4);
super::insert_ptr_chain(s1, s5);
super::insert_ptr_chain(s1, s6);
}
let mut iter =
unsafe { utils::iterate_ptr_chain(s1) }.map(|item| unsafe { (*item).s_type.0 });
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), None);
}
#[test]
fn ptr_chain_addition_chain() {
let mut s1 = vk::BaseOutStructure {
s_type: vk::StructureType(1),
p_next: ptr::null_mut(),
};
let s1 = ptr::addr_of_mut!(s1);
let mut s2 = vk::BaseOutStructure {
s_type: vk::StructureType(2),
p_next: ptr::null_mut(),
};
let s2 = ptr::addr_of_mut!(s2);
let mut s3 = vk::BaseOutStructure {
s_type: vk::StructureType(3),
p_next: ptr::null_mut(),
};
let s3 = ptr::addr_of_mut!(s3);
unsafe {
(*s1).p_next = s2;
(*s2).p_next = s3;
}
let mut s4 = vk::BaseOutStructure {
s_type: vk::StructureType(4),
p_next: ptr::null_mut(),
};
let s4 = ptr::addr_of_mut!(s4);
let mut s5 = vk::BaseOutStructure {
s_type: vk::StructureType(5),
p_next: ptr::null_mut(),
};
let s5 = ptr::addr_of_mut!(s5);
let mut s6 = vk::BaseOutStructure {
s_type: vk::StructureType(6),
p_next: ptr::null_mut(),
};
let s6 = ptr::addr_of_mut!(s6);
unsafe {
(*s4).p_next = s5;
(*s5).p_next = s6;
}
unsafe {
super::insert_ptr_chain(s1, s4);
}
let mut iter =
unsafe { utils::iterate_ptr_chain(s1) }.map(|item| unsafe { (*item).s_type.0 });
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), None);
}
#[test]
fn ptr_chain_real_world() {
let mut vk1_1features = vk::PhysicalDeviceVulkan11FeaturesBuilder::new();
let mut vk1_2features = vk::PhysicalDeviceVulkan12FeaturesBuilder::new();
let mut features = vk::PhysicalDeviceFeatures2Builder::new()
.extend_from(&mut vk1_1features)
.extend_from(&mut vk1_2features);
let base_ptr = ptr::addr_of_mut!(features) as *mut vk::BaseOutStructure;
let mut iter =
unsafe { utils::iterate_ptr_chain(base_ptr) }.map(|item| unsafe { (*item).s_type });
assert_eq!(
iter.next(),
Some(vk::StructureType::PHYSICAL_DEVICE_FEATURES_2)
);
assert_eq!(
iter.next(),
Some(vk::StructureType::PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)
);
assert_eq!(
iter.next(),
Some(vk::StructureType::PHYSICAL_DEVICE_VULKAN_1_1_FEATURES)
);
assert_eq!(iter.next(), None);
}
}