Vulkan Memory Allocator
|
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: structure VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd(). Given set of allocations, this function can move them to compact used memory, ensure more continuous free space and possibly also free some VkDeviceMemory
blocks.
What the defragmentation does is:
VkDeviceMemory
and offset. After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or VmaAllocationInfo::offset changes. You must query them again using vmaGetAllocationInfo() if you need them.What it doesn't do, so you need to do it yourself:
vkDestroyBuffer()
, vkDestroyImage()
, vkCreateBuffer()
, vkCreateImage()
for that purpose and NOT vmaDestroyBuffer(), vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to destroy or create allocation objects!Following example demonstrates how you can run defragmentation on CPU. Only allocations created in memory types that are HOST_VISIBLE
can be defragmented. Others are ignored.
The way it works is:
memmove()
function.Filling VmaDefragmentationInfo2::pAllocationsChanged is optional. This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index has been modified during defragmentation. You can pass null, but you then need to query every allocation passed to defragmentation for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
If you use Custom memory pools, you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations to defragment all allocations in given pools. You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case. You can also combine both methods.
It is also possible to defragment allocations created in memory types that are not HOST_VISIBLE
. To do that, you need to pass a command buffer that meets requirements as described in VmaDefragmentationInfo2::commandBuffer. The way it works is:
vkCmdCopyBuffer()
to passed command buffer.Example:
You can combine these two methods by specifying non-zero maxGpu*
as well as maxCpu*
parameters. The library automatically chooses best method to defragment each memory pool.
You may try not to block your entire program to wait until defragmentation finishes, but do it in the background, as long as you carefully fullfill requirements described in function vmaDefragmentationBegin().
While using defragmentation, you may experience validation layer warnings, which you just need to ignore. See Validation layer warnings.
If you defragment allocations bound to images, these images should be created with VK_IMAGE_CREATE_ALIAS_BIT
flag, to make sure that new image created with same parameters and pointing to data copied to another memory region will interpret its contents consistently. Otherwise you may experience corrupted data on some implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
If you defragment allocations bound to images, new images to be bound to new memory region after defragmentation should be created with VK_IMAGE_LAYOUT_PREINITIALIZED
and then transitioned to their original layout from before defragmentation using an image memory barrier.
Please don't expect memory to be fully compacted after defragmentation. Algorithms inside are based on some heuristics that try to maximize number of Vulkan memory blocks to make totally empty to release them, as well as to maximimze continuous empty space inside remaining blocks, while minimizing the number and size of allocations that needs to be moved. Some fragmentation may still remain after this call. This is normal.