Module vulkano::memory

source ·
Expand description

Device memory allocation and memory pools.

By default, memory allocation is automatically handled by the vulkano library when you create a buffer or an image. But if you want more control, you have the possibility to customise the memory allocation strategy.

Memory types and heaps

A physical device is composed of one or more memory heaps. A memory heap is a pool of memory that can be allocated.

// Enumerating memory heaps.
for (index, heap) in physical_device.memory_properties().memory_heaps.iter().enumerate() {
    println!("Heap #{:?} has a capacity of {:?} bytes", index, heap.size);
}

However you can’t allocate directly from a memory heap. A memory heap is shared amongst one or multiple memory types, which you can allocate memory from. Each memory type has different characteristics.

A memory type may or may not be visible to the host. In other words, it may or may not be directly writable by the CPU. A memory type may or may not be device-local. A device-local memory type has a much quicker access time from the GPU than a non-device-local type. Note that non-device-local memory types are still accessible by the device, they are just slower.

// Enumerating memory types.
for ty in physical_device.memory_properties().memory_types.iter() {
    println!("Memory type belongs to heap #{:?}", ty.heap_index);
    println!("Property flags: {:?}", ty.property_flags);
}

Memory types are order from “best” to “worse”. In other words, the implementation prefers that you use the memory types that are earlier in the list. This means that selecting a memory type should always be done by enumerating them and taking the first one that matches our criteria.

In practice

In practice, desktop machines usually have two memory heaps: one that represents the RAM of the CPU, and one that represents the RAM of the GPU. The CPU’s RAM is host-accessible but not device-local, while the GPU’s RAM is not host-accessible but is device-local.

Mobile machines usually have a single memory heap that is “equally local” to both the CPU and the GPU. It is both host-accessible and device-local.

Allocating memory and memory pools

Allocating memory can be done by calling DeviceMemory::allocate().

Here is an example:

use vulkano::memory::{DeviceMemory, MemoryAllocateInfo};

// Taking the first memory type for the sake of this example.
let memory_type_index = 0;

let memory = DeviceMemory::allocate(
    device.clone(),
    MemoryAllocateInfo {
        allocation_size: 1024,
        memory_type_index,
        ..Default::default()
    },
).expect("Failed to allocate memory");

// The memory is automatically freed when `memory` is destroyed.

However allocating and freeing memory is very slow (up to several hundred milliseconds sometimes). Instead you are strongly encouraged to use a memory pool. A memory pool is not a Vulkan concept but a vulkano concept.

A memory pool is any object that implements the MemoryPool trait. You can implement that trait on your own structure and then use it when you create buffers and images so that they get memory from that pool. By default if you don’t specify any pool when creating a buffer or an image, an instance of StandardMemoryPool that is shared by the Device object is used.

Modules

  • In Vulkan, suballocation of DeviceMemory is left to the application, because every application has slightly different needs and one can not incorporate an allocator into the driver that would perform well in all cases. Vulkano stays true to this sentiment, but aims to reduce the burden on the user as much as possible. You have a toolbox of suballocators to choose from that cover all allocation algorithms, which you can compose into any kind of hierarchy you wish. This way you have maximum flexibility while still only using a few DeviceMemory blocks and not writing any of the very error-prone code.

Structs

Enums