use std::ffi::c_void;
use ash::version::InstanceV1_0;
use ash::vk;
#[cfg(feature = "tracing")]
use tracing::{debug, trace, warn};
#[cfg(feature = "tracing")]
use crate::debug_memory_types;
use crate::{
align_up, find_memory_type_index, has_granularity_conflict, is_on_same_page, AllocationInfo,
AllocationType, AllocatorError, AllocatorStatistic, MemoryBlock, MemoryLocation, Result,
};
#[deprecated(
since = "0.2.0",
note = "This allocator isn't really useful. A linear allocator is more useful when implemented by the application directly."
)]
pub struct LinearAllocator {
logical_device: ash::Device,
memory_block: MemoryBlock,
buffer_image_granularity: u64,
allocation_count: usize,
unused_range_count: usize,
used_bytes: u64,
unused_bytes: u64,
heap_end: u64,
previous_offset: u64,
previous_size: u64,
previous_is_linear: bool,
}
impl LinearAllocator {
#[cfg_attr(feature = "profiling", profiling::function)]
pub fn new(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
logical_device: &ash::Device,
descriptor: &LinearAllocatorDescriptor,
) -> Result<Self> {
let memory_properties =
unsafe { instance.get_physical_device_memory_properties(physical_device) };
let memory_types =
&memory_properties.memory_types[..memory_properties.memory_type_count as usize];
#[cfg(feature = "tracing")]
debug_memory_types(memory_properties, memory_types);
let memory_type_index =
find_memory_type_index(&memory_properties, descriptor.location, u32::MAX)?;
#[cfg(feature = "tracing")]
debug!(
"Creating linear allocator for memory type[{}]",
memory_type_index
);
let size = (2usize).pow(descriptor.block_size as u32) as u64;
let memory_type = memory_types[memory_type_index];
let is_mappable = memory_type
.property_flags
.contains(vk::MemoryPropertyFlags::HOST_VISIBLE);
let memory_block =
MemoryBlock::new(logical_device, size, memory_type_index, is_mappable, false)?;
let physical_device_properties =
unsafe { instance.get_physical_device_properties(physical_device) };
let buffer_image_granularity = physical_device_properties.limits.buffer_image_granularity;
Ok(Self {
logical_device: logical_device.clone(),
memory_block,
heap_end: 0,
previous_offset: 0,
previous_size: 0,
buffer_image_granularity,
allocation_count: 0,
unused_range_count: 0,
used_bytes: 0,
previous_is_linear: false,
unused_bytes: 0,
})
}
#[cfg_attr(feature = "profiling", profiling::function)]
pub fn allocate(
&mut self,
descriptor: &LinearAllocationDescriptor,
) -> Result<LinearAllocation> {
let is_linear = descriptor.allocation_type.is_linear();
let free = self.memory_block.size - self.heap_end;
if descriptor.size > free {
#[cfg(feature = "tracing")]
warn!(
"Can't allocate {} bytes on the linear allocator, because only {} bytes are free",
descriptor.size, free
);
return Err(AllocatorError::OutOfMemory);
}
let mut offset = align_up(self.heap_end, descriptor.alignment);
if has_granularity_conflict(self.previous_is_linear, is_linear)
&& is_on_same_page(
self.previous_offset,
self.previous_size,
offset,
self.buffer_image_granularity,
)
{
offset = align_up(offset, self.buffer_image_granularity);
}
let padding = offset - self.heap_end;
let aligned_size = padding + descriptor.size;
self.allocation_count += 1;
if offset != self.heap_end {
self.unused_range_count += 1;
}
self.used_bytes += descriptor.size;
self.unused_bytes += padding;
self.previous_is_linear = is_linear;
self.previous_size = aligned_size;
self.previous_offset = self.heap_end;
self.heap_end += aligned_size;
#[cfg(feature = "tracing")]
trace!(
"Allocating {} bytes on the linear allocator. Padded with {} bytes",
descriptor.size,
aligned_size
);
Ok(LinearAllocation {
device_memory: self.memory_block.device_memory,
offset,
size: descriptor.size,
mapped_ptr: std::ptr::NonNull::new(self.memory_block.mapped_ptr),
})
}
#[cfg_attr(feature = "profiling", profiling::function)]
pub fn free(&mut self) {
self.allocation_count = 0;
self.unused_range_count = 0;
self.used_bytes = 0;
self.unused_bytes = 0;
self.heap_end = 0;
self.previous_is_linear = false;
self.previous_offset = 0;
self.previous_size = 0;
}
}
impl Drop for LinearAllocator {
fn drop(&mut self) {
self.memory_block.destroy(&self.logical_device)
}
}
impl AllocatorStatistic for LinearAllocator {
fn allocation_count(&self) -> usize {
self.allocation_count
}
fn unused_range_count(&self) -> usize {
self.unused_range_count
}
fn used_bytes(&self) -> u64 {
self.used_bytes
}
fn unused_bytes(&self) -> u64 {
self.unused_bytes
}
fn block_count(&self) -> usize {
1
}
}
#[derive(Debug, Clone)]
pub struct LinearAllocatorDescriptor {
pub location: MemoryLocation,
pub block_size: u8,
}
impl Default for LinearAllocatorDescriptor {
fn default() -> Self {
Self {
location: MemoryLocation::CpuToGpu,
block_size: 26,
}
}
}
pub struct LinearAllocationDescriptor {
pub size: u64,
pub alignment: u64,
pub allocation_type: AllocationType,
}
pub struct LinearAllocation {
device_memory: vk::DeviceMemory,
offset: u64,
size: u64,
mapped_ptr: Option<std::ptr::NonNull<c_void>>,
}
impl AllocationInfo for LinearAllocation {
fn memory(&self) -> vk::DeviceMemory {
self.device_memory
}
fn offset(&self) -> u64 {
self.offset
}
fn size(&self) -> u64 {
self.size
}
fn mapped_ptr(&self) -> Option<std::ptr::NonNull<c_void>> {
self.mapped_ptr
}
}