mod map;
pub(super) use map::HostMap;
use super::AllocError;
use crate as vk;
use std::{
alloc::Layout,
ops::{Deref, DerefMut},
ptr::NonNull,
sync::atomic::{AtomicUsize, Ordering},
};
#[derive(Clone, Copy, Default)]
pub(super) struct HostMemory<'callbacks> {
callbacks: Option<&'callbacks vk::AllocationCallbacks>,
}
impl<'callbacks> HostMemory<'callbacks> {
pub unsafe fn new_from_callbacks(
callbacks: &'callbacks vk::AllocationCallbacks,
) -> Result<Self, AllocError> {
if callbacks.pfn_allocation.is_none() || callbacks.pfn_free.is_none() {
return Err(AllocError::InvalidRequest(
"host callbacks require allocation and free functions",
));
}
Ok(Self {
callbacks: Some(callbacks),
})
}
pub(super) unsafe fn allocate(self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
debug_assert_ne!(layout.size(), 0);
let pointer = if let Some(callbacks) = self.callbacks {
unsafe {
(callbacks
.pfn_allocation
.expect("validated host allocation callback"))(
callbacks.p_user_data,
layout.size(),
layout.align(),
vk::SystemAllocationScope::OBJECT,
)
.cast()
}
} else {
unsafe { std::alloc::alloc(layout) }
};
NonNull::new(pointer).ok_or(AllocError::HostAllocationFailed)
}
pub(super) unsafe fn deallocate(self, pointer: NonNull<u8>, layout: Layout) {
if let Some(callbacks) = self.callbacks {
unsafe {
(callbacks.pfn_free.expect("validated host free callback"))(
callbacks.p_user_data,
pointer.as_ptr().cast(),
)
}
} else {
unsafe { std::alloc::dealloc(pointer.as_ptr(), layout) }
}
}
}
unsafe impl Send for HostMemory<'_> {}
unsafe impl Sync for HostMemory<'_> {}
pub(super) struct HostVec<'callbacks, T> {
pointer: NonNull<T>,
length: usize,
capacity: usize,
host: HostMemory<'callbacks>,
}
impl<'callbacks, T> HostVec<'callbacks, T> {
pub fn new(host: HostMemory<'callbacks>) -> Self {
Self {
pointer: NonNull::dangling(),
length: 0,
capacity: if size_of::<T>() == 0 { usize::MAX } else { 0 },
host,
}
}
#[cfg(test)]
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn host_memory(&self) -> HostMemory<'callbacks> {
self.host
}
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), AllocError> {
self.reserve(additional, false)
}
pub fn try_reserve(&mut self, additional: usize) -> Result<(), AllocError> {
self.reserve(additional, true)
}
#[inline]
fn reserve(&mut self, additional: usize, amortized: bool) -> Result<(), AllocError> {
let required = self
.length
.checked_add(additional)
.ok_or(AllocError::HostAllocationFailed)?;
if required <= self.capacity {
return Ok(());
}
self.grow(required, amortized)
}
#[cold]
#[inline(never)]
fn grow(&mut self, required: usize, amortized: bool) -> Result<(), AllocError> {
let capacity = if amortized {
required.max(self.capacity.saturating_mul(2)).max(4)
} else {
required
};
let layout = Layout::array::<T>(capacity).map_err(|_| AllocError::HostAllocationFailed)?;
let pointer = unsafe { self.host.allocate(layout) }?.cast::<T>();
unsafe { std::ptr::copy_nonoverlapping(self.pointer.as_ptr(), pointer.as_ptr(), self.length) };
let old = self.storage();
self.pointer = pointer;
self.capacity = capacity;
drop(old);
Ok(())
}
pub fn push_reserved(&mut self, value: T) {
assert!(
self.length < self.capacity,
"host metadata write requires reserved capacity"
);
unsafe { self.pointer.as_ptr().add(self.length).write(value) };
self.length += 1;
}
pub fn resize_reserved(&mut self, length: usize, value: T)
where
T: Clone,
{
assert!(
length <= self.capacity,
"host metadata resize requires reserved capacity"
);
while self.length > length {
self.pop();
}
while self.length < length {
self.push_reserved(value.clone());
}
}
pub fn pop(&mut self) -> Option<T> {
if self.length == 0 {
return None;
}
self.length -= 1;
Some(unsafe { self.pointer.as_ptr().add(self.length).read() })
}
pub fn clear(&mut self) {
let length = std::mem::replace(&mut self.length, 0);
unsafe {
std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
self.pointer.as_ptr(),
length,
))
};
}
pub fn retain(&mut self, mut keep: impl FnMut(&T) -> bool) {
let original = std::mem::replace(&mut self.length, 0);
let mut guard = Retain {
vector: self,
read: 0,
written: 0,
original,
};
while guard.read < original {
let current = unsafe { guard.vector.pointer.as_ptr().add(guard.read) };
if keep(unsafe { &*current }) {
if guard.written != guard.read {
unsafe {
std::ptr::copy_nonoverlapping(
current,
guard.vector.pointer.as_ptr().add(guard.written),
1,
)
};
}
guard.written += 1;
guard.read += 1;
} else {
guard.read += 1;
unsafe { std::ptr::drop_in_place(current) };
}
}
}
#[cfg(test)]
pub fn extract_if_reserved(&mut self, target: &mut Self, mut selected: impl FnMut(&T) -> bool) {
self.extract_to_reserved(
|value| selected(value).then_some(0),
|_, value| target.push_reserved(value),
);
}
pub fn extract_to_reserved(
&mut self,
mut selected: impl FnMut(&T) -> Option<usize>,
mut accept: impl FnMut(usize, T),
) {
let original = std::mem::replace(&mut self.length, 0);
let mut guard = Retain {
vector: self,
read: 0,
written: 0,
original,
};
while guard.read < original {
let current = unsafe { guard.vector.pointer.as_ptr().add(guard.read) };
if let Some(target) = selected(unsafe { &*current }) {
let value = unsafe { current.read() };
guard.read += 1;
accept(target, value);
} else {
if guard.written != guard.read {
unsafe {
std::ptr::copy_nonoverlapping(
current,
guard.vector.pointer.as_ptr().add(guard.written),
1,
)
};
}
guard.written += 1;
guard.read += 1;
}
}
}
pub fn remove(&mut self, index: usize) -> T {
assert!(
index < self.length,
"host metadata removal exceeds initialized length"
);
self.length -= 1;
unsafe {
let pointer = self.pointer.as_ptr().add(index);
let value = pointer.read();
std::ptr::copy(pointer.add(1), pointer, self.length - index);
value
}
}
pub fn append_reserved(&mut self, other: &mut Self) {
let length = self
.length
.checked_add(other.length)
.expect("reserved metadata length");
assert!(
length <= self.capacity,
"metadata append requires reserved capacity"
);
unsafe {
std::ptr::copy_nonoverlapping(
other.pointer.as_ptr(),
self.pointer.as_ptr().add(self.length),
other.length,
)
};
self.length = length;
other.length = 0;
}
pub fn extend_from_slice_reserved(&mut self, values: &[T])
where
T: Copy,
{
let length = self
.length
.checked_add(values.len())
.expect("reserved metadata length");
assert!(
length <= self.capacity,
"host metadata extension requires reserved capacity"
);
unsafe {
std::ptr::copy_nonoverlapping(
values.as_ptr(),
self.pointer.as_ptr().add(self.length),
values.len(),
)
};
self.length = length;
}
pub fn remove_prefix(&mut self, count: usize)
where
T: Copy,
{
assert!(
count <= self.length,
"host metadata prefix exceeds initialized length"
);
unsafe {
std::ptr::copy(
self.pointer.as_ptr().add(count),
self.pointer.as_ptr(),
self.length - count,
)
};
self.length -= count;
}
fn storage(&self) -> Option<Storage<'callbacks>> {
if self.capacity == 0 || size_of::<T>() == 0 {
return None;
}
Some(Storage {
pointer: self.pointer.cast(),
layout: Layout::array::<T>(self.capacity).expect("validated host capacity layout"),
host: self.host,
})
}
}
struct Retain<'vector, 'callbacks, T> {
vector: &'vector mut HostVec<'callbacks, T>,
read: usize,
written: usize,
original: usize,
}
impl<T> Drop for Retain<'_, '_, T> {
fn drop(&mut self) {
let remaining = self.original - self.read;
unsafe {
std::ptr::copy(
self.vector.pointer.as_ptr().add(self.read),
self.vector.pointer.as_ptr().add(self.written),
remaining,
)
};
self.vector.length = self.written + remaining;
}
}
pub(super) struct HostString<'callbacks>(HostVec<'callbacks, u8>);
impl<'callbacks> HostString<'callbacks> {
pub fn new_from_str(host: HostMemory<'callbacks>, text: &str) -> Result<Self, AllocError> {
let mut bytes = HostVec::new(host);
bytes.try_reserve_exact(text.len())?;
bytes.extend_from_slice_reserved(text.as_bytes());
Ok(Self(bytes))
}
}
impl Deref for HostString<'_> {
type Target = str;
fn deref(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.0) }
}
}
struct Storage<'callbacks> {
pointer: NonNull<u8>,
layout: Layout,
host: HostMemory<'callbacks>,
}
impl Drop for Storage<'_> {
fn drop(&mut self) {
unsafe { self.host.deallocate(self.pointer, self.layout) };
}
}
impl<T> Drop for HostVec<'_, T> {
fn drop(&mut self) {
let _storage = self.storage();
self.clear();
}
}
impl<T> Deref for HostVec<'_, T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.pointer.as_ptr(), self.length) }
}
}
impl<T> DerefMut for HostVec<'_, T> {
fn deref_mut(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.pointer.as_ptr(), self.length) }
}
}
impl<'borrow, T> IntoIterator for &'borrow HostVec<'_, T> {
type Item = &'borrow T;
type IntoIter = std::slice::Iter<'borrow, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'borrow, T> IntoIterator for &'borrow mut HostVec<'_, T> {
type Item = &'borrow mut T;
type IntoIter = std::slice::IterMut<'borrow, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
unsafe impl<T: Send> Send for HostVec<'_, T> {}
unsafe impl<T: Sync> Sync for HostVec<'_, T> {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct Identity(usize);
static NEXT_IDENTITY: AtomicUsize = AtomicUsize::new(1);
impl Identity {
pub fn new() -> Result<Self, AllocError> {
Self::new_from_counter(&NEXT_IDENTITY)
}
fn new_from_counter(counter: &AtomicUsize) -> Result<Self, AllocError> {
counter
.try_update(Ordering::Relaxed, Ordering::Relaxed, |value| {
value.checked_add(1)
})
.map(Self)
.map_err(|_| AllocError::SerialExhausted)
}
}
#[cfg(test)]
#[path = "../../../test/rs/mem/host/test_callbacks.rs"]
pub(super) mod test_callbacks;
#[cfg(test)]
#[path = "../../../test/rs/mem/test_host.rs"]
mod tests;