<divclass="textblock"><p>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.</p>
<p>To mitigate this problem, you can use defragmentation feature: structure <aclass="el"href="struct_vma_defragmentation_info2.html"title="Parameters for defragmentation. ">VmaDefragmentationInfo2</a>, function <aclass="el"href="vk__mem__alloc_8h.html#a36ba776fd7fd5cb1e9359fdc0d8e6e8a"title="Begins defragmentation process. ">vmaDefragmentationBegin()</a>, <aclass="el"href="vk__mem__alloc_8h.html#a8774e20e91e245aae959ba63efa15dd2"title="Ends defragmentation process. ">vmaDefragmentationEnd()</a>. Given set of allocations, this function can move them to compact used memory, ensure more continuous free space and possibly also free some <code>VkDeviceMemory</code> blocks.</p>
<p>What the defragmentation does is:</p>
<ul>
<li>Updates <aclass="el"href="struct_vma_allocation.html"title="Represents single memory allocation. ">VmaAllocation</a> objects to point to new <code>VkDeviceMemory</code> and offset. After allocation has been moved, its <aclass="el"href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67"title="Handle to Vulkan memory object. ">VmaAllocationInfo::deviceMemory</a> and/or <aclass="el"href="struct_vma_allocation_info.html#a4a3c732388dbdc7a23f9365b00825268"title="Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation. ">VmaAllocationInfo::offset</a> changes. You must query them again using <aclass="el"href="vk__mem__alloc_8h.html#a86dd08aba8633bfa4ad0df2e76481d8b"title="Returns current information about specified allocation and atomically marks it as used in current fra...">vmaGetAllocationInfo()</a> if you need them.</li>
<li>Moves actual data in memory.</li>
</ul>
<p>What it doesn't do, so you need to do it yourself:</p>
<ul>
<li>Recreate buffers and images that were bound to allocations that were defragmented and bind them with their new places in memory. You must use <code>vkDestroyBuffer()</code>, <code>vkDestroyImage()</code>, <code>vkCreateBuffer()</code>, <code>vkCreateImage()</code> for that purpose and NOT <aclass="el"href="vk__mem__alloc_8h.html#a0d9f4e4ba5bf9aab1f1c746387753d77"title="Destroys Vulkan buffer and frees allocated memory. ">vmaDestroyBuffer()</a>, <aclass="el"href="vk__mem__alloc_8h.html#ae50d2cb3b4a3bfd4dd40987234e50e7e"title="Destroys Vulkan image and frees allocated memory. ">vmaDestroyImage()</a>, <aclass="el"href="vk__mem__alloc_8h.html#ac72ee55598617e8eecca384e746bab51">vmaCreateBuffer()</a>, <aclass="el"href="vk__mem__alloc_8h.html#a02a94f25679275851a53e82eacbcfc73"title="Function similar to vmaCreateBuffer(). ">vmaCreateImage()</a>, because you don't need to destroy or create allocation objects!</li>
<li>Recreate views and update descriptors that point to these buffers and images.</li>
</ul>
<h1><aclass="anchor"id="defragmentation_cpu"></a>
Defragmenting CPU memory</h1>
<p>Following example demonstrates how you can run defragmentation on CPU. Only allocations created in memory types that are <code>HOST_VISIBLE</code> can be defragmented. Others are ignored.</p>
<p>The way it works is:</p>
<ul>
<li>It temporarily maps entire memory blocks when necessary.</li>
<li>It moves data using <code>memmove()</code> function.</li>
</ul>
<divclass="fragment"><divclass="line"><spanclass="comment">// Given following variables already initialized:</span></div><divclass="line">VkDevice device;</div><divclass="line"><aclass="code"href="struct_vma_allocator.html">VmaAllocator</a> allocator;</div><divclass="line">std::vector<VkBuffer> buffers;</div><divclass="line">std::vector<VmaAllocation> allocations;</div><divclass="line"></div><divclass="line"></div><divclass="line"><spanclass="keyword">const</span> uint32_t allocCount = (uint32_t)allocations.size();</div><divclass="line">std::vector<VkBool32> allocationsChanged(allocCount);</div><divclass="line"></div><divclass="line"><aclass="code"href="struct_vma_defragmentation_info2.html">VmaDefragmentationInfo2</a> defragInfo = {};</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a3cf86ab32c1da779b4923d301a3056ba">allocationCount</a> = allocCount;</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a8943f8d65969ce8e2189a1cdf3205e96">pAllocations</a> = allocations.data();</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a76d51a644dc7f5405d0cdd0025ecd0cc">pAllocationsChanged</a> = allocationsChanged.data();</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#af78e1ea40c22d85137b65f6b384a4d0a">maxCpuBytesToMove</a> = VK_WHOLE_SIZE; <spanclass="comment">// No limit.</span></div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a94c2c7223d52878445a8cccce396b671">maxCpuAllocationsToMove</a> = UINT32_MAX; <spanclass="comment">// No limit.</span></div><divclass="line"></div><divclass="line"><aclass="code"href="struct_vma_defragmentation_context.html">VmaDefragmentationContext</a> defragCtx;</div><divclass="line"><aclass="code"href="vk__mem__alloc_8h.html#a36ba776fd7fd5cb1e9359fdc0d8e6e8a">vmaDefragmentationBegin</a>(allocator, &defragInfo, <spanclass="keyword">nullptr</span>, &defragCtx);</div><divclass="line"><aclass="code"href="vk__mem__alloc_8h.html#a8774e20e91e245aae959ba63efa15dd2">vmaDefragmentationEnd</a>(allocator, defragCtx);</div><divclass="line"></div><divclass="line"><spanclass="keywordflow">for</span>(uint32_t i = 0; i < allocCount; ++i)</div><divclass="line">{</div><divclass="line"><spanclass="keywordflow">if</span>(allocationsChanged[i])</div><divclass="line"> {</div><divclass="line"><spanclass="comment">// Destroy buffer that is immutably bound to memory region which is no longer valid.</span></div><divclass="line"> vkDestroyBuffer(device, buffers[i], <spanclass="keyword">nullptr</span>);</div><divclass="line"></div><divclass="line"><spanclass="comment">// Create new buffer with same parameters.</span></div><divclass="line"> VkBufferCreateInfo bufferInfo = ...;</div><divclass="line"> vkCreateBuffer(device, &bufferInfo, <spanclass="keyword">nullptr</span>, &buffers[i]);</div><divclass="line"></div><divclass="line"><spanclass="comment">// You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.</span></div><divclass="line"></div><divclass="line"><spanclass="comment">// Bind new buffer to new memory region. Data contained in it is already moved.</span></div><divclass="line"><aclass="code"href="struct_vma_allocation_info.html">VmaAllocationInfo</a> allocInfo;</div><divclass="line"><aclass="code"href="vk__mem__alloc_8h.html#a86dd08aba8633bfa4ad0df2e76481d8b">vmaGetAllocationInfo</a>(allocator, allocations[i], &allocInfo);</div><divclass="line"> vkBindBufferMemory(device, buffers[i], allocInfo.<aclass="code"href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67">deviceMemory</a>, allocInfo.<aclass="code"href="struct_vma_allocation_info.html#a4a3c732388dbdc7a23f9365b00825268">offset</a>);</div><divclass="line"> }</div><divclass="line"
<p>If you use <aclass="el"href="choosing_memory_type.html#choosing_memory_type_custom_memory_pools">Custom memory pools</a>, you can fill <aclass="el"href="struct_vma_defragmentation_info2.html#a7e70aa2a1081d849dcc7829b19d3ec9d"title="Numer of pools in pPools array. ">VmaDefragmentationInfo2::poolCount</a> and <aclass="el"href="struct_vma_defragmentation_info2.html#a0b3effd57f3fcdeb2ed62210b4ef20e1"title="Either null or pointer to array of pools to be defragmented. ">VmaDefragmentationInfo2::pPools</a> instead of <aclass="el"href="struct_vma_defragmentation_info2.html#a3cf86ab32c1da779b4923d301a3056ba"title="Number of allocations in pAllocations array. ">VmaDefragmentationInfo2::allocationCount</a> and <aclass="el"href="struct_vma_defragmentation_info2.html#a8943f8d65969ce8e2189a1cdf3205e96"title="Pointer to array of allocations that can be defragmented. ">VmaDefragmentationInfo2::pAllocations</a> to defragment all allocations in given pools. You cannot use <aclass="el"href="struct_vma_defragmentation_info2.html#a76d51a644dc7f5405d0cdd0025ecd0cc"title="Optional, output. Pointer to array that will be filled with information whether the allocation at cer...">VmaDefragmentationInfo2::pAllocationsChanged</a> in that case. You can also combine both methods.</p>
<h1><aclass="anchor"id="defragmentation_gpu"></a>
Defragmenting GPU memory</h1>
<p>It is also possible to defragment allocations created in memory types that are not <code>HOST_VISIBLE</code>. To do that, you need to pass a command buffer that meets requirements as described in <aclass="el"href="struct_vma_defragmentation_info2.html#a7f71f39590c5316771493d2333f9c1bd"title="Optional. Command buffer where GPU copy commands will be posted. ">VmaDefragmentationInfo2::commandBuffer</a>. The way it works is:</p>
<ul>
<li>It creates temporary buffers and binds them to entire memory blocks when necessary.</li>
<li>It issues <code>vkCmdCopyBuffer()</code> to passed command buffer.</li>
</ul>
<p>Example:</p>
<divclass="fragment"><divclass="line"><spanclass="comment">// Given following variables already initialized:</span></div><divclass="line">VkDevice device;</div><divclass="line"><aclass="code"href="struct_vma_allocator.html">VmaAllocator</a> allocator;</div><divclass="line">VkCommandBuffer commandBuffer;</div><divclass="line">std::vector<VkBuffer> buffers;</div><divclass="line">std::vector<VmaAllocation> allocations;</div><divclass="line"></div><divclass="line"></div><divclass="line"><spanclass="keyword">const</span> uint32_t allocCount = (uint32_t)allocations.size();</div><divclass="line">std::vector<VkBool32> allocationsChanged(allocCount);</div><divclass="line"></div><divclass="line">VkCommandBufferBeginInfo cmdBufBeginInfo = ...;</div><divclass="line">vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);</div><divclass="line"></div><divclass="line"><aclass="code"href="struct_vma_defragmentation_info2.html">VmaDefragmentationInfo2</a> defragInfo = {};</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a3cf86ab32c1da779b4923d301a3056ba">allocationCount</a> = allocCount;</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a8943f8d65969ce8e2189a1cdf3205e96">pAllocations</a> = allocations.data();</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a76d51a644dc7f5405d0cdd0025ecd0cc">pAllocationsChanged</a> = allocationsChanged.data();</div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a4ddbc898d0afe1518f863a3763628f08">maxGpuBytesToMove</a> = VK_WHOLE_SIZE; <spanclass="comment">// Notice it's "GPU" this time.</span></div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a40d53d33e71ba0b66f844ed63c05a3f6">maxGpuAllocationsToMove</a> = UINT32_MAX; <spanclass="comment">// Notice it's "GPU" this time.</span></div><divclass="line">defragInfo.<aclass="code"href="struct_vma_defragmentation_info2.html#a7f71f39590c5316771493d2333f9c1bd">commandBuffer</a> = commandBuffer;</div><divclass="line"></div><divclass="line"><aclass="code"href="struct_vma_defragmentation_context.html">VmaDefragmentationContext</a> defragCtx;</div><divclass="line"><aclass="code"href="vk__mem__alloc_8h.html#a36ba776fd7fd5cb1e9359fdc0d8e6e8a">vmaDefragmentationBegin</a>(allocator, &defragInfo, <spanclass="keyword">nullptr</span>, &defragCtx);</div><divclass="line"></div><divclass="line">vkEndCommandBuffer(commandBuffer);</div><divclass="line"></div><divclass="line"><spanclass="comment">// Submit commandBuffer.</span></div><divclass="line"><spanclass="comment">// Wait for a fence that ensures commandBuffer execution finished.</span></div><divclass="line"></div><divclass="line"><aclass="code"href="vk__mem__alloc_8h.html#a8774e20e91e245aae959ba63efa15dd2">vmaDefragmentationEnd</a>(allocator, defragCtx);</div><divclass="line"></div><divclass="line"><spanclass="keywordflow">for</span>(uint32_t i = 0; i < allocCount; ++i)</div><divclass="line">{</div><divclass="line"><spanclass="keywordflow">if</span>(allocationsChanged[i])</div><divclass="line"> {</div><divclass="line"><spanclass="comment">// Destroy buffer that is immutably bound to memory region which is no longer valid.</span></div><divclass="line"> vkDestroyBuffer(device, buffers[i], <spanclass="keyword">nullptr</span>);</div><divclass="line"></div><divclass="line"><spanclass="comment">// Create new buffer with same parameters.</span></div><divclass="line"> VkBufferCreateInfo bufferInfo = ...;</div><divclass="line"> vkCreateBuffer(device, &bufferInfo, <spanclass="keyword">nullptr</span>, &buffers[i]);</div><divclass="line"></div><divclass="line"><spanclass="comment">// You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.</span></div><divclass="line"></d
<p>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 <aclass="el"href="vk__mem__alloc_8h.html#a36ba776fd7fd5cb1e9359fdc0d8e6e8a"title="Begins defragmentation process. ">vmaDefragmentationBegin()</a>.</p>
<p>While using defragmentation, you may experience validation layer warnings, which you just need to ignore. See <aclass="el"href="general_considerations.html#general_considerations_validation_layer_warnings">Validation layer warnings</a>.</p>
<p>If you defragment allocations bound to images, these images should be created with <code>VK_IMAGE_CREATE_ALIAS_BIT</code> 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.</p>
<p>If you defragment allocations bound to images, new images to be bound to new memory region after defragmentation should be created with <code>VK_IMAGE_LAYOUT_PREINITIALIZED</code> and then transitioned to their original layout from before defragmentation using an image memory barrier.</p>
<p>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 need to be moved. Some fragmentation may still remain - this is normal.</p>
<p>If you want to implement your own, custom defragmentation algorithm, there is infrastructure prepared for that, but it is not exposed through the library API - you need to hack its source code. Here are steps needed to do this:</p>
<oltype="1">
<li>Main thing you need to do is to define your own class derived from base abstract class <code>VmaDefragmentationAlgorithm</code> and implement your version of its pure virtual method. See definition and comments of this class for details.</li>
<li>Your code needs to interact with device memory block metadata. If you need more access to its data than it's provided by its public interface, declare your new class as a friend class e.g. in class <code>VmaBlockMetadata_Generic</code>.</li>
<li>If you want to create a flag that would enable your algorithm or pass some additional flags to configure it, define some enum with such flags and use them in <aclass="el"href="struct_vma_defragmentation_info2.html#a53e844ee5633e229cf6daf14b2d9fff9"title="Reserved for future use. Should be 0. ">VmaDefragmentationInfo2::flags</a>.</li>
<li>Modify function <code>VmaBlockVectorDefragmentationContext::Begin</code> to create object of your new class whenever needed. </li>