Struct vulkano::buffer::allocator::SubbufferAllocator
source · 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,
impl<A> SubbufferAllocator<A>where A: MemoryAllocator,
sourcepub fn new(
memory_allocator: Arc<A>,
create_info: SubbufferAllocatorCreateInfo
) -> Self
pub fn new( memory_allocator: Arc<A>, create_info: SubbufferAllocatorCreateInfo ) -> Self
Creates a new SubbufferAllocator
.
sourcepub fn arena_size(&self) -> DeviceSize
pub fn arena_size(&self) -> DeviceSize
Returns the current size of the arenas.
sourcepub fn set_arena_size(&self, size: DeviceSize)
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.
sourcepub fn reserve(&self, size: DeviceSize) -> Result<(), MemoryAllocatorError>
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.
sourcepub fn allocate_sized<T>(&self) -> Result<Subbuffer<T>, MemoryAllocatorError>where
T: BufferContents,
pub fn allocate_sized<T>(&self) -> Result<Subbuffer<T>, MemoryAllocatorError>where T: BufferContents,
Allocates a subbuffer for sized data.