diff --git a/README.md b/README.md index ebc9a23..040cf22 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Additional features: - Custom memory pools: Create a pool with desired parameters (e.g. fixed or limited maximum size) and allocate memory out of it. - Linear allocator: Create a pool with linear algorithm and use it for much faster allocations and deallocations in free-at-once, stack, double stack, or ring buffer fashion. - Support for VK_KHR_dedicated_allocation extension: Just enable it and it will be used automatically by the library. -- Defragmentation: Call one function and let the library move data around to free some memory blocks and make your allocations better compacted. +- Defragmentation of GPU and CPU memory: Let the library move data around to free some memory blocks and make your allocations better compacted. - Lost allocations: Allocate memory with appropriate flags and let the library remove allocations that are not used for many frames to make room for new ones. - Statistics: Obtain detailed statistics about the amount of memory used, unused, number of allocated blocks, number of allocations etc. - globally, per memory heap, and per memory type. - Debug annotations: Associate string with name or opaque pointer to your own data with every allocation. diff --git a/docs/html/defragmentation.html b/docs/html/defragmentation.html index 4f232ec..221e9e2 100644 --- a/docs/html/defragmentation.html +++ b/docs/html/defragmentation.html @@ -69,14 +69,44 @@ $(function() {
Interleaved allocations and deallocations of many objects of varying size can cause fragmentation, 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 vmaDefragment(). Given set of allocations, this function can move them to compact used memory, ensure more continuous free space and possibly also free some VkDeviceMemory
. Currently it can work only on allocations made from memory type that is HOST_VISIBLE
and HOST_COHERENT
. Allocations are modified to point to the new VkDeviceMemory
and offset. Data in this memory is also memmove
-ed to the new place. However, if you have images or buffers bound to these allocations (and you certainly do), you need to destroy, recreate, and bind them to the new place in memory.
After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or VmaAllocationInfo::offset changes. You must query them again using vmaGetAllocationInfo() if you need them.
-If an allocation has been moved, data in memory is copied to new place automatically, but if it was bound to a buffer or an image, you must destroy that object yourself, create new one and bind it to the new memory pointed by the allocation. You must use vkDestroyBuffer()
, vkDestroyImage()
, vkCreateBuffer()
, vkCreateImage()
for that purpose and NOT vmaDestroyBuffer(), vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example:
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 still remains after this call. This is normal.
+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.
For further details, see documentation of function vmaDefragment().
+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.
Features deliberately excluded from the scope of this library:
VkCommandList
or VkQueue
and related synchronization is responsibility of the user.VkCommandBuffer
or VkQueue
and related synchronization is responsibility of the user.vkCreateBuffer()
and vkAllocateMemory()
.VkBufferCreateInfo
, VkImageCreateInfo
are not stored in VmaAllocation object.This is the complete list of members for VmaDefragmentationInfo2, including all inherited members.
pAllocations
array. More...pAllocations
array. More...pPools
array. More...memcpy()
, memmove()
. More...commandBuffer
. More...Parameters for defragmentation.
To be used with function vmaDefragmentationBegin().
size_t VmaDefragmentationInfo2::allocationCount | +uint32_t VmaDefragmentationInfo2::allocationCount |
Number of allocations in pAllocations
array.
VkCommandBuffer VmaDefragmentationInfo2::commandBuffer | +
Optional. Command buffer where GPU copy commands will be posted.
+If not null, it must be a valid command buffer handle that supports Transfer queue type. It must be in the recording state and outside of a render pass instance. You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
+Passing null means that only CPU defragmentation will be performed.
+Flags for defragmentation. Use VmaDefragmentationFlagBits enum.
+Reserved for future use. Should be 0.
Pointer to array of allocations that can be defragmented.
-The array should have allocationCount
elements. All other allocations are considered non-moveable during this defragmentation.
The array should have allocationCount
elements. The array should not contain nulls. Elements in the array should be unique - same allocation cannot occur twice. It is safe to pass allocations that are in the lost state - they are ignored. All allocations not present in this array are considered non-moveable during this defragmentation.
Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed (moved or lost) during defragmentation.
+Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
The array should have allocationCount
elements. You can pass null if you are not interested in this information.
uint32_t VmaDefragmentationInfo2::poolCount | +
Numer of pools in pPools
array.
VmaPool* VmaDefragmentationInfo2::pPools | +
Either null or pointer to array of pools to be defragmented.
+All the allocations in the specified pools can be moved during defragmentation and there is no way to check if they were really moved as in pAllocationsChanged
, so you must query all the allocations in all these pools for new VkDeviceMemory
and offset using vmaGetAllocationInfo() if you might need to recreate buffers and images bound to them.
The array should have poolCount
elements. The array should not contain nulls. Elements in the array should be unique - same pool cannot occur twice.
Using this array is equivalent to specifying all allocations from the pools in pAllocations
. It might be more efficient.
This is the complete list of members for VmaDefragmentationStats, including all inherited members.
VkDeviceMemory
objects that have been released to the system. More...Statistics returned by function vmaDefragment().
uint32_t VmaDefragmentationStats::allocationsLost | -
Number of allocations that became lost in the process of defragmentation.
- -Pointers to some Vulkan functions - a subset used by the library.
@@ -153,6 +155,20 @@ Public AttributesPFN_vkCmdCopyBuffer VmaVulkanFunctions::vkCmdCopyBuffer | +
@@ -597,7 +590,7 @@ Functions |
Flags to be used in vmaDefragmentationBegin().
+Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
Flags to be used in vmaDefragmentationBegin().
+Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
Enumerator | |
---|---|
VMA_DEFRAGMENTATION_CAN_MAKE_LOST_BIT | Add this flag to enable allocations created with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag to become lost during defragmentation process if the algorithm decides it is beneficial. - |
VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT | Add this flag to change defragmentation algorithm to fast rather than default (balanced). This algorithm will favor speed over quality of defragmentation. Defragmentation will be done as fast and move as little allocations and bytes as possible while still providing some benefits. - |
VMA_DEFRAGMENTATION_OPTIMAL_ALGORITHM_BIT | Add this flag to change defragmentation algorithm to optimal rather than default (balanced). This algorithm will favor quality of defragmentation over speed. Allocations will be as perfectly compacted as possible. - |
VMA_DEFRAGMENTATION_ALGORITHM_MASK | A bit mask to extract only |
VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM | |
Enumerator | |
VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM |
VK_SUCCESS
if completed, VK_INCOMPLETE
if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo
have been reached, negative error code in case of error.VK_SUCCESS
if completed, negative error code in case of error.This function works by moving allocations to different places (different VkDeviceMemory
objects and/or different offsets) in order to optimize memory usage. Only allocations that are in pAllocations
array can be moved. All other allocations are considered nonmovable in this call. Basic rules:
VK_SUCCESS
and *pContext == null
if defragmentation finished within this function call. VK_NOT_READY
and *pContext != null
if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.Use this function instead of old, deprecated vmaDefragment().
-It is important to note that between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
+Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
pInfo->pAllocations
, including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or accessing their data.pInfo->pAllocations
or any allocations that belong to pools passed as pInfo->pPools
, including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access their data.pStats
and pInfo->pAllocationsChanged
are undefined. They become valid after call to vmaDefragmentationEnd().pInfo->commandBuffer != VK_NULL_HANDLE
, you must submit that command buffer and make sure it finished execution before calling vmaDefragmentationEnd(). pInfo->commandBuffer
is not null, you must submit that command buffer and make sure it finished execution before calling vmaDefragmentationEnd().