Direct3D 12 Memory Allocator
Virtual allocator

As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". You can use it to allocate your own memory or other objects, even completely unrelated to D3D12. A common use case is sub-allocation of pieces of one large GPU buffer.

Creating virtual block

To use this functionality, there is no main "allocator" object. You don't need to have D3D12MA::Allocator object created. All you need to do is to create a separate D3D12MA::VirtualBlock object for each block of memory you want to be managed by the allocator:

  1. Fill in D3D12MA::ALLOCATOR_DESC structure.
  2. Call D3D12MA::CreateVirtualBlock. Get new D3D12MA::VirtualBlock object.

Example:

blockDesc.Size = 1048576; // 1 MB
HRESULT hr = CreateVirtualBlock(&blockDesc, &block);
Represents pure allocation algorithm and a data structure with allocations in some memory block,...
Definition: D3D12MemAlloc.h:1418
D3D12MA_API HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC *pDesc, VirtualBlock **ppVirtualBlock)
Creates new D3D12MA::VirtualBlock object and returns it through ppVirtualBlock.
Parameters of created D3D12MA::VirtualBlock object to be passed to CreateVirtualBlock().
Definition: D3D12MemAlloc.h:1324
UINT64 Size
Total size of the block.
Definition: D3D12MemAlloc.h:1332

Making virtual allocations

D3D12MA::VirtualBlock object contains internal data structure that keeps track of free and occupied regions using the same code as the main D3D12 memory allocator. A single allocation is identified by a lightweight structure D3D12MA::VirtualAllocation. You will also likely want to know the offset at which the allocation was made in the block.

In order to make an allocation:

  1. Fill in D3D12MA::VIRTUAL_ALLOCATION_DESC structure.
  2. Call D3D12MA::VirtualBlock::Allocate. Get new D3D12MA::VirtualAllocation value that identifies the allocation.

Example:

allocDesc.Size = 4096; // 4 KB
UINT64 allocOffset;
hr = block->Allocate(&allocDesc, &alloc, &allocOffset);
if(SUCCEEDED(hr))
{
// Use the 4 KB of your memory starting at allocOffset.
}
else
{
// Allocation failed - no space for it could be found. Handle this error!
}
HRESULT Allocate(const VIRTUAL_ALLOCATION_DESC *pDesc, VirtualAllocation *pAllocation, UINT64 *pOffset)
Creates new allocation.
Parameters of created virtual allocation to be passed to VirtualBlock::Allocate().
Definition: D3D12MemAlloc.h:1369
UINT64 Size
Size of the allocation.
Definition: D3D12MemAlloc.h:1376
Represents single memory allocation done inside VirtualBlock.
Definition: D3D12MemAlloc.h:441

Deallocation

When no longer needed, an allocation can be freed by calling D3D12MA::VirtualBlock::FreeAllocation.

When whole block is no longer needed, the block object can be released by calling block->Release(). All allocations must be freed before the block is destroyed, which is checked internally by an assert. However, if you don't want to call block->FreeAllocation for each allocation, you can use D3D12MA::VirtualBlock::Clear to free them all at once - a feature not available in normal D3D12 memory allocator.

Example:

block->FreeAllocation(alloc);
block->Release();
void FreeAllocation(VirtualAllocation allocation)
Frees the allocation.

Allocation parameters

You can attach a custom pointer to each allocation by using D3D12MA::VirtualBlock::SetAllocationPrivateData. Its default value is NULL. It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some larger data structure containing more information. Example:

struct CustomAllocData
{
std::string m_AllocName;
};
CustomAllocData* allocData = new CustomAllocData();
allocData->m_AllocName = "My allocation 1";
block->SetAllocationPrivateData(alloc, allocData);
void SetAllocationPrivateData(VirtualAllocation allocation, void *pPrivateData)
Changes custom pointer for an allocation to a new value.

The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function D3D12MA::VirtualBlock::GetAllocationInfo and inspecting returned structure D3D12MA::VIRTUAL_ALLOCATION_INFO. If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! Example:

VIRTUAL_ALLOCATION_INFO allocInfo;
block->GetAllocationInfo(alloc, &allocInfo);
delete (CustomAllocData*)allocInfo.pPrivateData;
block->FreeAllocation(alloc);
void GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO *pInfo) const
Returns information about an allocation - its offset, size and custom pointer.

Alignment and units

It feels natural to express sizes and offsets in bytes. If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment to request it. Example:

allocDesc.Size = 4096; // 4 KB
allocDesc.Alignment = 4; // Returned offset must be a multiply of 4 B
UINT64 allocOffset;
hr = block->Allocate(&allocDesc, &alloc, &allocOffset);
UINT64 Alignment
Required alignment of the allocation.
Definition: D3D12MemAlloc.h:1381

Alignments of different allocations made from one block may vary. However, if all alignments and sizes are always multiply of some size e.g. 4 B or sizeof(MyDataStruct), you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. It might be more convenient, but you need to make sure to use this new unit consistently in all the places:

Statistics

You can obtain brief statistics of a virtual block using D3D12MA::VirtualBlock::GetStatistics(). The function fills structure D3D12MA::Statistics - same as used by the normal D3D12 memory allocator. Example:

block->GetStatistics(&stats);
printf("My virtual block has %llu bytes used by %u virtual allocations\n",
void GetStatistics(Statistics *pStats) const
Retrieves basic statistics of the virtual block that are fast to calculate.
Calculated statistics of memory usage e.g. in a specific memory heap type, memory segment group,...
Definition: D3D12MemAlloc.h:320
UINT64 AllocationBytes
Total number of bytes occupied by all D3D12MA::Allocation objects.
Definition: D3D12MemAlloc.h:338
UINT AllocationCount
Number of D3D12MA::Allocation objects allocated.
Definition: D3D12MemAlloc.h:328

More detailed statistics can be obtained using function D3D12MA::VirtualBlock::CalculateStatistics(), but they are slower to calculate.

You can also request a full list of allocations and free regions as a string in JSON format by calling D3D12MA::VirtualBlock::BuildStatsString. Returned string must be later freed using D3D12MA::VirtualBlock::FreeStatsString. The format of this string may differ from the one returned by the main D3D12 allocator, but it is similar.

Additional considerations

Alternative, linear algorithm can be used with virtual allocator - see flag D3D12MA::VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR and documentation: Linear allocation algorithm.

Note that the "virtual allocator" functionality is implemented on a level of individual memory blocks. Keeping track of a whole collection of blocks, allocating new ones when out of free space, deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.