use super::{
AllocError, AllocationAlgorithm,
host::{HostMemory, HostVec, Identity},
metadata::{Metadata, Placement},
};
use crate as vk;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum PlacementStrategy {
#[default]
Default,
MinMemory,
MinTime,
MinOffset,
}
pub struct VirtualAllocation {
owner: Identity,
placement: Option<Placement>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VirtualAllocationInfo {
pub user_data: usize,
pub offset: u64,
pub size: u64,
}
impl VirtualAllocationInfo {
#[inline]
#[must_use]
pub fn set_user_data(mut self, value: usize) -> Self {
self.user_data = value;
self
}
#[inline]
#[must_use]
pub fn set_offset(mut self, value: u64) -> Self {
self.offset = value;
self
}
#[inline]
#[must_use]
pub fn set_size(mut self, value: u64) -> Self {
self.size = value;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VirtualBlockStats {
pub size: u64,
pub allocation_count: usize,
pub allocated_bytes: u64,
pub free_bytes: u64,
pub free_region_count: usize,
pub largest_free_region: u64,
}
impl VirtualBlockStats {
#[inline]
#[must_use]
pub fn set_size(mut self, value: u64) -> Self {
self.size = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_count(mut self, value: usize) -> Self {
self.allocation_count = value;
self
}
#[inline]
#[must_use]
pub fn set_allocated_bytes(mut self, value: u64) -> Self {
self.allocated_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_free_bytes(mut self, value: u64) -> Self {
self.free_bytes = value;
self
}
#[inline]
#[must_use]
pub fn set_free_region_count(mut self, value: usize) -> Self {
self.free_region_count = value;
self
}
#[inline]
#[must_use]
pub fn set_largest_free_region(mut self, value: u64) -> Self {
self.largest_free_region = value;
self
}
}
pub struct VirtualBlock<'callbacks> {
owner: Identity,
metadata: Metadata<'callbacks>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VirtualBlockDetails {
pub statistics: VirtualBlockStats,
pub allocation_size_min: Option<u64>,
pub allocation_size_max: Option<u64>,
pub free_range_size_min: Option<u64>,
pub free_range_size_max: Option<u64>,
}
impl VirtualBlockDetails {
#[inline]
#[must_use]
pub fn set_statistics(mut self, value: VirtualBlockStats) -> Self {
self.statistics = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_size_min(mut self, value: Option<u64>) -> Self {
self.allocation_size_min = value;
self
}
#[inline]
#[must_use]
pub fn set_allocation_size_max(mut self, value: Option<u64>) -> Self {
self.allocation_size_max = value;
self
}
#[inline]
#[must_use]
pub fn set_free_range_size_min(mut self, value: Option<u64>) -> Self {
self.free_range_size_min = value;
self
}
#[inline]
#[must_use]
pub fn set_free_range_size_max(mut self, value: Option<u64>) -> Self {
self.free_range_size_max = value;
self
}
}
pub fn set_user_data(
block: &mut VirtualBlock,
allocation: &VirtualAllocation,
data: usize,
) -> Result<(), AllocError> {
allocation_info(block, allocation)?;
block.metadata.set_user_data(
allocation.placement.ok_or(AllocError::UnknownAllocation)?,
data,
)
}
pub fn is_empty(block: &VirtualBlock) -> bool {
block.metadata.is_empty()
}
pub fn detailed_statistics(block: &VirtualBlock) -> VirtualBlockDetails {
let mut result = VirtualBlockDetails {
statistics: statistics(block),
allocation_size_min: None,
allocation_size_max: None,
free_range_size_min: None,
free_range_size_max: None,
};
block.metadata.for_each_allocation(|_, size, _| {
result.allocation_size_min = Some(result.allocation_size_min.map_or(size, |old| old.min(size)));
result.allocation_size_max = Some(result.allocation_size_max.map_or(size, |old| old.max(size)));
});
block.metadata.for_each_free(|_, size| {
result.free_range_size_min = Some(result.free_range_size_min.map_or(size, |old| old.min(size)));
result.free_range_size_max = Some(result.free_range_size_max.map_or(size, |old| old.max(size)));
});
result
}
pub fn write_statistics_json(
block: &VirtualBlock,
output: &mut impl std::io::Write,
detailed: bool,
) -> Result<(), AllocError> {
let stats = statistics(block);
let mut allocations = HostVec::new(block.metadata.host_memory());
if detailed {
allocations
.try_reserve_exact(stats.allocation_count)
.map_err(|_| AllocError::HostAllocationFailed)?;
block
.metadata
.for_each_allocation(|offset, size, user_data| {
allocations.push_reserved((offset, size, user_data))
});
allocations.sort_unstable_by_key(|value| value.0);
}
let result = (|| -> std::io::Result<()> {
write!(
output,
"{{\"schema\":\"vk.memory.virtual.v1\",\"size\":{},\"allocation_count\":{},\"allocation_bytes\":{},\"free_bytes\":{},\"free_range_count\":{},\"largest_free_range\":{}",
stats.size,
stats.allocation_count,
stats.allocated_bytes,
stats.free_bytes,
stats.free_region_count,
stats.largest_free_region
)?;
if detailed {
output.write_all(b",\"allocations\":[")?;
for (index, (offset, size, data)) in allocations.iter().enumerate() {
if index != 0 {
output.write_all(b",")?;
}
write!(
output,
"{{\"offset\":{offset},\"size\":{size},\"user_data\":{data}}}"
)?;
}
output.write_all(b"],\"free_ranges\":[")?;
let mut first = true;
let mut error = None;
block.metadata.for_each_free(|offset, size| {
if error.is_some() {
return;
}
let result = (|| {
if !first {
output.write_all(b",")?;
}
first = false;
write!(output, "[{offset},{size}]")
})();
if let Err(failure) = result {
error = Some(failure);
}
});
if let Some(error) = error {
return Err(error);
}
output.write_all(b"]")?;
}
output.write_all(b"}\n")
})();
result.map_err(|error| AllocError::Output {
kind: error.kind(),
code: error.raw_os_error(),
})
}
impl<'callbacks> VirtualBlock<'callbacks> {
pub fn is_empty(&self) -> bool {
is_empty(self)
}
pub fn set_user_data(
&mut self,
allocation: &VirtualAllocation,
data: usize,
) -> Result<(), AllocError> {
set_user_data(self, allocation, data)
}
pub fn detailed_statistics(&self) -> VirtualBlockDetails {
detailed_statistics(self)
}
pub fn write_statistics_json(
&self,
output: &mut impl std::io::Write,
detailed: bool,
) -> Result<(), AllocError> {
write_statistics_json(self, output, detailed)
}
pub fn new(size: u64) -> Result<Self, AllocError> {
Self::new_with_algorithm(size, AllocationAlgorithm::Tlsf)
}
pub fn new_with_algorithm(size: u64, algorithm: AllocationAlgorithm) -> Result<Self, AllocError> {
Ok(Self {
owner: Identity::new()?,
metadata: Metadata::new(size, algorithm, true)?,
})
}
pub unsafe fn new_with_callbacks(
size: u64,
algorithm: AllocationAlgorithm,
callbacks: &'callbacks vk::AllocationCallbacks,
) -> Result<Self, AllocError> {
let host = unsafe { HostMemory::new_from_callbacks(callbacks) }?;
Ok(Self {
owner: Identity::new()?,
metadata: Metadata::new_with_host(size, algorithm, true, host)?,
})
}
#[inline]
pub fn allocate(
&mut self,
size: u64,
alignment: u64,
strategy: PlacementStrategy,
) -> Result<VirtualAllocation, AllocError> {
allocate(self, size, alignment, strategy)
}
pub fn allocate_upper(
&mut self,
size: u64,
alignment: u64,
) -> Result<VirtualAllocation, AllocError> {
allocate_upper(self, size, alignment)
}
#[inline]
pub fn free(&mut self, allocation: &mut VirtualAllocation) -> Result<(), AllocError> {
free(self, allocation)
}
pub fn allocation_info(
&self,
allocation: &VirtualAllocation,
) -> Result<VirtualAllocationInfo, AllocError> {
allocation_info(self, allocation)
}
pub fn clear(&mut self) {
clear(self)
}
pub fn statistics(&self) -> VirtualBlockStats {
statistics(self)
}
}
#[inline]
pub fn allocate(
block: &mut VirtualBlock,
size: u64,
alignment: u64,
strategy: PlacementStrategy,
) -> Result<VirtualAllocation, AllocError> {
let placement = block.metadata.allocate(size, alignment, strategy, false)?;
Ok(VirtualAllocation {
owner: block.owner,
placement: Some(placement),
})
}
pub fn allocate_upper(
block: &mut VirtualBlock,
size: u64,
alignment: u64,
) -> Result<VirtualAllocation, AllocError> {
let placement = block
.metadata
.allocate(size, alignment, PlacementStrategy::Default, true)?;
Ok(VirtualAllocation {
owner: block.owner,
placement: Some(placement),
})
}
pub fn allocation_info(
block: &VirtualBlock,
allocation: &VirtualAllocation,
) -> Result<VirtualAllocationInfo, AllocError> {
if block.owner != allocation.owner {
return Err(AllocError::WrongAllocator);
}
block
.metadata
.info(allocation.placement.ok_or(AllocError::UnknownAllocation)?)
}
#[inline]
pub fn free(
block: &mut VirtualBlock,
allocation: &mut VirtualAllocation,
) -> Result<(), AllocError> {
if block.owner != allocation.owner {
return Err(AllocError::WrongAllocator);
}
block
.metadata
.free(allocation.placement.ok_or(AllocError::UnknownAllocation)?)?;
allocation.placement = None;
Ok(())
}
pub fn clear(block: &mut VirtualBlock) {
block.metadata.clear()
}
pub fn statistics(block: &VirtualBlock) -> VirtualBlockStats {
block.metadata.statistics()
}