pub unsafe trait Suballocator {
    // Required methods
    fn new(region: Region) -> Self
       where Self: Sized;
    fn allocate(
        &self,
        layout: DeviceLayout,
        allocation_type: AllocationType,
        buffer_image_granularity: DeviceAlignment
    ) -> Result<Suballocation, SuballocatorError>;
    unsafe fn deallocate(&self, suballocation: Suballocation);
    fn free_size(&self) -> DeviceSize;
    fn cleanup(&mut self);
}
Expand description

Suballocators are used to divide a region into smaller suballocations.

Regions

As the name implies, a region is a contiguous portion of memory. It may be the whole dedicated block of DeviceMemory, or only a part of it. Or it may be a buffer, or only a part of a buffer. Regions are just allocations like any other, but we use this term to refer specifically to an allocation that is to be suballocated. Every suballocator is created with a region to work with.

Free-lists

A free-list, also kind of predictably, refers to a list of (sub)allocations within a region that are currently free. Every (sub)allocator that can free allocations dynamically (in any order) needs to keep a free-list of some sort. This list is then consulted when new allocations are made, and can be used to coalesce neighboring allocations that are free into bigger ones.

Memory hierarchies

Different applications have wildly different allocation needs, and there’s no way to cover them all with a single type of allocator. Furthermore, different allocators have different trade-offs and are best suited to specific tasks. To account for all possible use-cases, Vulkano offers the ability to create memory hierarchies. We refer to the DeviceMemory as the root of any such hierarchy, even though technically the driver has levels that are further up, because those DeviceMemory blocks need to be allocated from physical memory pages themselves, but since those levels are not accessible to us we don’t need to consider them. You can create any number of levels/branches from there, bounded only by the amount of available memory within a DeviceMemory block. You can suballocate the root into regions, which are then suballocated into further regions and so on, creating hierarchies of arbitrary height.

Examples

TODO

Safety

First consider using the provided implementations as there should be no reason to implement this trait, but if you must:

  • allocate must return a memory block that is in bounds of the region.
  • allocate must return a memory block that doesn’t alias any other currently allocated memory blocks:
    • Two currently allocated memory blocks must not share any memory locations, meaning that the intersection of the byte ranges of the two memory blocks must be empty.
    • Two neighboring currently allocated memory blocks must not share any page whose size is given by the buffer-image granularity, unless either both were allocated with AllocationType::Linear or both were allocated with AllocationType::NonLinear.
    • The size does not have to be padded to the alignment. That is, as long the offset is aligned and the memory blocks don’t share any memory locations, a memory block is not considered to alias another even if the padded size shares memory locations with another memory block.
  • A memory block must stay allocated until either deallocate is called on it or the allocator is dropped. If the allocator is cloned, it must produce the same allocator, and memory blocks must stay allocated until either deallocate is called on the memory block using any of the clones or all of the clones have been dropped.

Required Methods§

source

fn new(region: Region) -> Selfwhere Self: Sized,

Creates a new suballocator for the given region.

source

fn allocate( &self, layout: DeviceLayout, allocation_type: AllocationType, buffer_image_granularity: DeviceAlignment ) -> Result<Suballocation, SuballocatorError>

Creates a new suballocation within the region.

Arguments
  • layout - The layout of the allocation.

  • allocation_type - The type of resources that can be bound to the allocation.

  • buffer_image_granularity - The buffer-image granularity device property.

    This is provided as an argument here rather than on construction of the allocator to allow for optimizations: if you are only ever going to be creating allocations with the same allocation_type using this allocator, then you may hard-code this to DeviceAlignment::MIN, in which case, after inlining, the logic for aligning the allocation to the buffer-image-granularity based on the allocation type of surrounding allocations can be optimized out.

    You don’t need to consider the buffer-image granularity for instance when suballocating a buffer, or when suballocating a DeviceMemory block that’s only ever going to be used for optimal images. However, if you do allocate both linear and non-linear resources and don’t specify the buffer-image granularity device property here, you will get undefined behavior down the line. Note that AllocationType::Unknown counts as both linear and non-linear at the same time: if you always use this as the allocation_type using this allocator, then it is valid to set this to DeviceAlignment::MIN, but you must ensure all allocations are aligned to the buffer-image granularity at minimum.

source

unsafe fn deallocate(&self, suballocation: Suballocation)

Deallocates the given suballocation.

Safety
  • suballocation must refer to a currently allocated suballocation of self.
source

fn free_size(&self) -> DeviceSize

Returns the total amount of free space that is left in the region.

source

fn cleanup(&mut self)

Tries to free some space, if applicable.

There must be no current allocations as they might get freed.

Trait Implementations§

source§

impl Debug for dyn Suballocator

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Implementors§