Vulkan Memory Allocator
Defragmentation

Interleaved allocations and deallocations of many objects of varying size can cause fragmentation over time, which can lead to a situation where the library is unable to find a continuous range of free memory for a new allocation despite there is enough free space, just scattered across many small free ranges between existing allocations.

To mitigate this problem, you can use defragmentation feature. It doesn't happen automatically though and needs your cooperation, because VMA is a low level library that only allocates memory. It cannot recreate buffers and images in a new place as it doesn't remember the contents of VkBufferCreateInfo / VkImageCreateInfo structures. It cannot copy their contents as it doesn't record any commands to a command buffer.

Example:

VmaDefragmentationInfo defragInfo = {};
defragInfo.pool = myPool;
VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);
// Check res...
for(;;)
{
res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);
if(res == VK_SUCCESS)
break;
else if(res == VK_INCOMPLETE)
{
for(uint32_t i = 0; i < pass.moveCount; ++i)
{
//- Inspect pass.pMoves[i].srcAllocation, identify what buffer or image it represents.
//- Recreate this buffer or image at pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.
//- Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.
}
//- Make sure the copy commands finished executing.
//- Update appropriate descriptors to point to the new places.
res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);
if(res == VK_SUCCESS)
break;
else if(res != VK_INCOMPLETE)
// Handle error...
}
else
// Handle error...
}
vmaEndDefragmentation(allocator, defragCtx, nullptr);
VkResult vmaEndDefragmentation(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationStats *pStats)
Ends defragmentation process.
VkResult vmaBeginDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationPassMoveInfo *pPassInfo)
Starts single defragmentation pass.
VkResult vmaBeginDefragmentation(VmaAllocator allocator, const VmaDefragmentationInfo *pInfo, VmaDefragmentationContext *pContext)
Begins defragmentation process.
VkResult vmaEndDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationPassMoveInfo *pPassInfo)
Ends single defragmentation pass.
@ VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT
Definition: vk_mem_alloc.h:700
An opaque object that represents started defragmentation process.
Parameters for defragmentation.
Definition: vk_mem_alloc.h:1381
VmaPool pool
Custom pool to be defragmented.
Definition: vk_mem_alloc.h:1388
VmaDefragmentationFlags flags
Use combination of VmaDefragmentationFlagBits.
Definition: vk_mem_alloc.h:1383
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:1421
uint32_t moveCount
Number of elements in the pMoves array.
Definition: vk_mem_alloc.h:1423

You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool (like in the example above) or all the default pools by setting this member to null.

Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter. Defragmentation algorithm tries to move all suitable allocations. You can, however, refuse to move some of them inside a defragmentation pass, by setting pass.pMoves[i].operation to VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. However, this is not recommended and may result in suboptimal packing of the allocations after defragmentation. If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.

You can also decide to destroy an allocation instead of moving it. You should then set pass.pMoves[i].operation to VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.

You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.

It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA usage, possibly from multiple threads, with the exception that allocations returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.