pub struct SubbufferAllocator<A = StandardMemoryAllocator> { /* private fields */ }
Expand description

Efficiently suballocates buffers into smaller subbuffers.

This allocator is especially suitable when you want to upload or download some data regularly (for example, at each frame for a video game).

Algorithm

The allocator keeps a pool of arenas. An arena is simply a buffer in which arena allocation takes place, also known as bump allocation or linear allocation. Every time you allocate, one of these arenas is suballocated. An arena is suballocated until it runs out of space, at which point a free one is taken from the pool. If there is no arena that is currently available, one will be allocated. After all subbuffers allocated from an arena are dropped, the arena is automatically returned to the arena pool for reuse. If you try to allocate a subbuffer larger than the current size of an arena, the arenas are automatically resized.

No memory is allocated when the allocator is created, be it on the Vulkan or Rust side. That only happens once you allocate a subbuffer.

Usage

Ideally, one arena should be able to fit all data you need to update per frame, so that each arena is submitted and freed once per frame. This way, the arena pool would also contain as many arenas as there are frames in flight on the thread. Otherwise, if your arenas are not able to fit everything each frame, what will likely happen is that each subbuffer will be allocated from an individual arena. This can impact efficiency both in terms of memory usage (because each arena has the same size, even if some of the subbuffers are way smaller) as well as performance, because the data could end up more physically separated in memory, which means the GPU would need to hop from place to place a lot more during a frame.

Ideally the result is something roughly like this:

+---------------------------------------------------------------------------------------------+
|                                        Memory Block                                         |
|-----+------+-----------------------+---------+-----------------------+------+---------+-----|
|     |      |     Frame 1 Arena     |         |     Frame 2 Arena     |      |         |     |
| ••• | Tex. |-------+-------+-------| Attach. |-------+-------+-------| Tex. | Attach. | ••• |
|     |      | Vert. | Indx. | Unif. |         | Vert. | Indx. | Unif. |      |         |     |
+-----+------+-------+-------+-------+---------+-------+-------+-------+------+---------+-----+

Download or device-only usage is much the same. Try to make the arenas fit all the data you need to store at once.

Examples

use vulkano::{
    buffer::{
        allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
        BufferUsage,
    },
    command_buffer::{
        AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
    },
    memory::allocator::MemoryTypeFilter,
    sync::GpuFuture,
};

// Create the buffer allocator.
let buffer_allocator = SubbufferAllocator::new(
    memory_allocator.clone(),
    SubbufferAllocatorCreateInfo {
        buffer_usage: BufferUsage::TRANSFER_SRC,
        memory_type_filter: MemoryTypeFilter::PREFER_HOST
            | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
        ..Default::default()
    },
);

for n in 0..25u32 {
    // Each loop allocates a new subbuffer and stores `data` in it.
    let data: [f32; 4] = [1.0, 0.5, n as f32 / 24.0, 0.0];
    let subbuffer = buffer_allocator.allocate_sized().unwrap();
    *subbuffer.write().unwrap() = data;

    // You can then use `subbuffer` as if it was an entirely separate buffer.
    AutoCommandBufferBuilder::primary(
        &command_buffer_allocator,
        queue.queue_family_index(),
        CommandBufferUsage::OneTimeSubmit,
    )
    .unwrap()
    // For the sake of the example we just call `update_buffer` on the buffer, even though
    // it is pointless to do that.
    .update_buffer(subbuffer.clone(), &[0.2, 0.3, 0.4, 0.5])
    .unwrap()
    .build().unwrap()
    .execute(queue.clone())
    .unwrap()
    .then_signal_fence_and_flush()
    .unwrap();
}

Implementations§

source§

impl<A> SubbufferAllocator<A>where A: MemoryAllocator,

source

pub fn new( memory_allocator: Arc<A>, create_info: SubbufferAllocatorCreateInfo ) -> Self

Creates a new SubbufferAllocator.

source

pub fn arena_size(&self) -> DeviceSize

Returns the current size of the arenas.

source

pub fn set_arena_size(&self, size: DeviceSize)

Sets the arena size to the provided size.

The next time you allocate a subbuffer, a new arena will be allocated with the new size, and all subsequently allocated arenas will also share the new size.

source

pub fn reserve(&self, size: DeviceSize) -> Result<(), MemoryAllocatorError>

Ensures that the size of the current arena is at least size.

If size is greater than the current arena size, then a new arena will be allocated with the new size, and all subsequently allocated arenas will also share the new size. Otherwise this has no effect.

source

pub fn allocate_sized<T>(&self) -> Result<Subbuffer<T>, MemoryAllocatorError>where T: BufferContents,

Allocates a subbuffer for sized data.

source

pub fn allocate_slice<T>( &self, len: DeviceSize ) -> Result<Subbuffer<[T]>, MemoryAllocatorError>where T: BufferContents,

Allocates a subbuffer for a slice.

Panics
  • Panics if len is zero.
source

pub fn allocate_unsized<T>( &self, len: DeviceSize ) -> Result<Subbuffer<T>, MemoryAllocatorError>where T: BufferContents + ?Sized,

Allocates a subbuffer for unsized data.

Panics
  • Panics if len is zero.
source

pub fn allocate( &self, layout: DeviceLayout ) -> Result<Subbuffer<[u8]>, MemoryAllocatorError>

Allocates a subbuffer with the given layout.

Panics
  • Panics if layout.alignment() exceeds 64.

Trait Implementations§

source§

impl<A: Debug> Debug for SubbufferAllocator<A>

source§

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

Formats the value using the given formatter. Read more
source§

impl<A> DeviceOwned for SubbufferAllocator<A>where A: MemoryAllocator,

source§

fn device(&self) -> &Arc<Device>

Returns the device that owns self.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.