mod import;
pub(super) use import::ImportKind;
use super::{AllocError, ImportMemoryInfo};
use crate as vk;
use std::{ffi::c_void, marker::PhantomData, ptr};
#[derive(Clone, Copy, Debug)]
pub struct AllocationChain<'a> {
pub(super) data: ChainData,
borrow: PhantomData<&'a c_void>,
}
#[derive(Clone, Copy, Debug)]
pub(super) struct ChainData {
pub next: *const c_void,
pub flags: Option<vk::MemoryAllocateFlags>,
pub exports: vk::ExternalMemoryHandleTypeFlags,
pub has_export: bool,
pub imports: bool,
pub import_extension: Option<&'static std::ffi::CStr>,
pub import_kind: ImportKind,
pub import_info: Option<ImportMemoryInfo>,
}
impl ChainData {
pub(super) const EMPTY: Self = Self {
next: ptr::null(),
flags: None,
exports: vk::ExternalMemoryHandleTypeFlags::empty(),
has_export: false,
imports: false,
import_extension: None,
import_kind: ImportKind::Ordinary,
import_info: None,
};
}
impl Default for ChainData {
fn default() -> Self {
Self::EMPTY
}
}
impl<'a> AllocationChain<'a> {
pub unsafe fn new_from_record<T: vk::Extends<vk::MemoryAllocateInfo<'a>>>(
record: &'a T,
) -> Result<Self, AllocError> {
let data = unsafe { inspect(ptr::from_ref(record).cast(), None) }?;
Ok(Self {
data,
borrow: PhantomData,
})
}
pub unsafe fn new_from_record_with_import<T: vk::Extends<vk::MemoryAllocateInfo<'a>>>(
record: &'a T,
info: ImportMemoryInfo,
) -> Result<Self, AllocError> {
let data = unsafe { inspect(ptr::from_ref(record).cast(), Some(info)) }?;
Ok(Self {
data,
borrow: PhantomData,
})
}
pub fn as_ptr(&self) -> *const c_void {
self.data.next
}
}
unsafe fn inspect(
next: *const c_void,
import_info: Option<ImportMemoryInfo>,
) -> Result<ChainData, AllocError> {
let mut data = ChainData {
next,
import_info,
..Default::default()
};
let mut seen = [ptr::null::<vk::BaseInStructure>(); 64];
let mut tags = [vk::StructureType::from_raw(0); 64];
let mut count = 0;
let mut cursor = next.cast::<vk::BaseInStructure>();
while !cursor.is_null() {
if count == seen.len() || seen[..count].contains(&cursor) {
return Err(AllocError::InvalidRequest(
"cyclic or excessive allocation chain",
));
}
let node = unsafe { &*cursor };
if tags[..count].contains(&node.s_type) {
return Err(AllocError::InvalidRequest(
"duplicate allocation chain record",
));
}
seen[count] = cursor;
tags[count] = node.s_type;
count += 1;
match node.s_type {
vk::StructureType::MEMORY_DEDICATED_ALLOCATE_INFO
| vk::StructureType::DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV
| vk::StructureType::MEMORY_PRIORITY_ALLOCATE_INFO_EXT
| vk::StructureType::MEMORY_DEDICATED_ALLOCATE_INFO_TENSOR_ARM => {
return Err(AllocError::InvalidRequest(
"dedication and priority are allocator-owned",
));
}
vk::StructureType::MEMORY_ALLOCATE_FLAGS_INFO => {
let flags = unsafe { &*cursor.cast::<vk::MemoryAllocateFlagsInfo>() };
if flags.flags.as_raw() & vk::MemoryAllocateFlagBits::DEVICE_MASK_BIT.as_raw() != 0
&& flags.device_mask == 0
{
return Err(AllocError::InvalidRequest(
"device-mask allocation requires a nonzero mask",
));
}
if flags.flags.as_raw()
& vk::MemoryAllocateFlagBits::DEVICE_ADDRESS_CAPTURE_REPLAY_BIT.as_raw()
!= 0
{
return Err(AllocError::UnsupportedCapability(
"allocation capture/replay",
));
}
data.flags = Some(flags.flags);
}
vk::StructureType::EXPORT_MEMORY_ALLOCATE_INFO => {
data.exports = unsafe { (*cursor.cast::<vk::ExportMemoryAllocateInfo>()).handle_types };
data.has_export = true;
}
vk::StructureType::IMPORT_MEMORY_FD_INFO_KHR => {
if data.imports {
return Err(AllocError::InvalidRequest(
"multiple memory import operations",
));
}
let import = unsafe { &*cursor.cast::<vk::ImportMemoryFdInfoKHR>() };
if import.fd < 0
|| (import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::OPAQUE_FD_BIT
&& import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::DMA_BUF_BIT_EXT)
{
return Err(AllocError::InvalidRequest("invalid memory fd import"));
}
data.imports = true;
data.import_extension = Some(c"VK_KHR_external_memory_fd");
}
vk::StructureType::IMPORT_MEMORY_HOST_POINTER_INFO_EXT => {
if data.imports {
return Err(AllocError::InvalidRequest(
"multiple memory import operations",
));
}
let import = unsafe { &*cursor.cast::<vk::ImportMemoryHostPointerInfoEXT>() };
if import.p_host_pointer.is_null()
|| (import.handle_type != vk::ExternalMemoryHandleTypeFlagBits::HOST_ALLOCATION_BIT_EXT
&& import.handle_type
!= vk::ExternalMemoryHandleTypeFlagBits::HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT)
{
return Err(AllocError::InvalidRequest("invalid host pointer import"));
}
data.imports = true;
data.import_kind = ImportKind::Host;
data.import_extension = Some(c"VK_EXT_external_memory_host");
}
vk::StructureType::MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO => {
return Err(AllocError::UnsupportedCapability(
"allocation capture/replay",
));
}
#[cfg(feature = "platform-win32")]
vk::StructureType::EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR => {}
_ => {
let (extension, kind) = unsafe { import::inspect(cursor) }?;
if data.imports {
return Err(AllocError::InvalidRequest(
"multiple memory import operations",
));
}
data.imports = true;
data.import_extension = Some(extension);
data.import_kind = kind;
}
}
cursor = node.p_next;
}
import::validate(&data)?;
Ok(data)
}
#[cfg(test)]
#[path = "../../../test/rs/mem/test_chain.rs"]
mod tests;