diff --git a/README.md b/README.md index 15d9cae9..aa0c4c3f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Easy to integrate Vulkan memory allocation library. **Changelog:** See [CHANGELOG.md](CHANGELOG.md) -**Product page:** [Vulkan Memory Allocator on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) +**Product page:** [Vulkan Memory Allocator on GPUOpen](https://gpuopen.com/vulkan-memory-allocator/) [](http://isitmaintained.com/project/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator "Average time to resolve an issue") @@ -44,7 +44,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 Vulkan 1.0...1.4. -- Support for extensions (and equivalent functionality included in new Vulkan versions): +- Support for extensions (and equivalent functionality included in new core Vulkan versions): - VK_KHR_dedicated_allocation: Just enable it and it will be used automatically by the library. - VK_KHR_bind_memory2. - VK_KHR_maintenance4. @@ -60,7 +60,7 @@ Additional features: - JSON dump: Obtain a string in JSON format with detailed map of internal state, including list of allocations, their string names, and gaps between them. - Convert this JSON dump into a picture to visualize your memory. See [tools/GpuMemDumpVis](tools/GpuMemDumpVis/README.md). - Debugging incorrect memory usage: Enable initialization of all allocated memory with a bit pattern to detect usage of uninitialized or freed memory. Enable validation of a magic number after every allocation to detect out-of-bounds memory corruption. -- Support for interoperability with OpenGL. +- Support for interoperability with OpenGL, Direct3D, and other graphics APIs through external memory export. - Virtual allocator: Interface for using core allocation algorithm to allocate any custom data, e.g. pieces of one large buffer. # Prerequisites diff --git a/docs/html/allocation_annotation.html b/docs/html/allocation_annotation.html index dc19902a..67b42d56 100644 --- a/docs/html/allocation_annotation.html +++ b/docs/html/allocation_annotation.html @@ -3,15 +3,13 @@
- +You can annotate allocations with your own information, e.g. for debugging purposes. To do that, fill VmaAllocationCreateInfo::pUserData field when creating an allocation. It is an opaque void* pointer. You can use it e.g. as a pointer, some handle, index, key, ordinal number or any other value that would associate the allocation with your custom metadata. It is useful to identify appropriate data structures in your engine given VmaAllocation, e.g. when doing Defragmentation.
You can annotate allocations with your own information, e.g. for debugging purposes. To do that, fill VmaAllocationCreateInfo::pUserData field when creating an allocation. It is an opaque void* pointer. You can use it e.g. as a pointer, some handle, index, key, ordinal number or any other value that would associate the allocation with your custom metadata. It is useful to identify appropriate data structures in your engine given VmaAllocation, e.g. when doing Defragmentation.
The pointer may be later retrieved as VmaAllocationInfo::pUserData:
It can also be changed using function vmaSetAllocationUserData().
-Values of (non-zero) allocations' pUserData are printed in JSON report created by vmaBuildStatsString() in hexadecimal form.
Values of (non-zero) allocations' pUserData are printed in JSON report created by vmaBuildStatsString() in hexadecimal form.
+An allocation can also carry a null-terminated string, giving a name to the allocation. To set it, call vmaSetAllocationName(). The library creates internal copy of the string, so the pointer you pass doesn't need to be valid for whole lifetime of the allocation. You can free it after the call.
vkAllocateMemory and vkFreeMemory Physical devices in Vulkan support various combinations of memory heaps and types. Help with choosing correct and optimal memory type for your specific resource is one of the key features of this library. You can use it by filling appropriate members of VmaAllocationCreateInfo structure, as described below. You can also combine multiple methods.
@@ -96,15 +83,15 @@When using 3. or 4., the library internally queries Vulkan for memory types supported for that buffer or image (function vkGetBufferMemoryRequirements()) and uses only one of these types.
If no memory type can be found that meets all the requirements, these functions return VK_ERROR_FEATURE_NOT_PRESENT.
When using 3. or 4., the library internally queries Vulkan for memory types supported for that buffer or image (function vkGetBufferMemoryRequirements()) and uses only one of these types.
+If no memory type can be found that meets all the requirements, these functions return VK_ERROR_FEATURE_NOT_PRESENT.
You can leave VmaAllocationCreateInfo structure completely filled with zeros. It means no requirements are specified for memory type. It is valid, although not very useful.
-The easiest way to specify memory requirements is to fill member VmaAllocationCreateInfo::usage using one of the values of enum VmaMemoryUsage. It defines high level, common usage types. Since version 3 of the library, it is recommended to use VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.
-For example, if you want to create a uniform buffer that will be filled using transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can do it using following code. The buffer will most likely end up in a memory type with VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT to be fast to access by the GPU device.
For example, if you want to create a uniform buffer that will be filled using transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can do it using following code. The buffer will most likely end up in a memory type with VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT to be fast to access by the GPU device.
If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory on systems with discrete graphics card that have the memories separate, you can use VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or VMA_MEMORY_USAGE_AUTO_PREFER_HOST.
-When using VMA_MEMORY_USAGE_AUTO* while you want to map the allocated memory, you also need to specify one of the host access flags: VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. This will help the library decide about preferred memory type to ensure it has VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT so you can map it.
For example, a staging buffer that will be filled via mapped pointer and then used as a source of transfer to the buffer described previously can be created like this. It will likely end up in a memory type that is HOST_VISIBLE and HOST_COHERENT but not HOST_CACHED (meaning uncached, write-combined) and not DEVICE_LOCAL (meaning system RAM).
When using VMA_MEMORY_USAGE_AUTO* while you want to map the allocated memory, you also need to specify one of the host access flags: VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. This will help the library decide about preferred memory type to ensure it has VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT so you can map it.
+For example, a staging buffer that will be filled via mapped pointer and then used as a source of transfer to the buffer described previously can be created like this. It will likely end up in a memory type that is HOST_VISIBLE and HOST_COHERENT but not HOST_CACHED (meaning uncached, write-combined) and not DEVICE_LOCAL (meaning system RAM).
For more examples of creating different kinds of resources, see chapter Recommended usage patterns. See also: Memory mapping.
-Usage values VMA_MEMORY_USAGE_AUTO* are legal to use only when the library knows about the resource being created by having VkBufferCreateInfo / VkImageCreateInfo passed, so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc. If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting memory type, as described below.
VMA_MEMORY_USAGE_GPU_ONLY, VMA_MEMORY_USAGE_CPU_ONLY, VMA_MEMORY_USAGE_CPU_TO_GPU, VMA_MEMORY_USAGE_GPU_TO_CPU, VMA_MEMORY_USAGE_CPU_COPY) are still available and work same way as in previous versions of the library for backward compatibility, but they are deprecated.Usage values VMA_MEMORY_USAGE_AUTO* are legal to use only when the library knows about the resource being created by having VkBufferCreateInfo / VkImageCreateInfo passed, so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc. If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting memory type, as described below.
+You can specify more detailed requirements by filling members VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags with a combination of bits from enum VkMemoryPropertyFlags. For example, if you want to create a buffer that will be persistently mapped on host (so it must be HOST_VISIBLE) and preferably will also be HOST_COHERENT and HOST_CACHED, use following code:
You can specify more detailed requirements by filling members VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags with a combination of bits from enum VkMemoryPropertyFlags. For example, if you want to create a buffer that will be persistently mapped on host (so it must be HOST_VISIBLE) and preferably will also be HOST_COHERENT and HOST_CACHED, use following code:
A memory type is chosen that has all the required flags and as many preferred flags set as possible.
Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags, plus some extra "magic" (heuristics).
-If you inspected memory types available on the physical device and you have a preference for memory types that you want to use, you can fill member VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set means that a memory type with that index is allowed to be used for the allocation. Special value 0, just like UINT32_MAX, means there are no restrictions to memory type index.
If you inspected memory types available on the physical device and you have a preference for memory types that you want to use, you can fill member VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set means that a memory type with that index is allowed to be used for the allocation. Special value 0, just like UINT32_MAX, means there are no restrictions to memory type index.
Please note that this member is NOT just a memory type index. Still you can use it to choose just one, specific memory type. For example, if you already determined that your buffer should be created in memory type 2, use following code:
You can also use this parameter to exclude some memory types. If you inspect memory heaps and types available on the current physical device and you determine that for some reason you don't want to use a specific memory type for the allocation, you can enable automatic memory type selection but exclude certain memory type or types by setting all bits of memoryTypeBits to 1 except the ones you choose.
You can also use this parameter to exclude some memory types. If you inspect memory heaps and types available on the current physical device and you determine that for some reason you don't want to use a specific memory type for the allocation, you can enable automatic memory type selection but exclude certain memory type or types by setting all bits of memoryTypeBits to 1 except the ones you choose.
If you allocate from custom memory pool, all the ways of specifying memory requirements described above are not applicable and the aforementioned members of VmaAllocationCreateInfo structure are ignored. Memory type is selected explicitly when creating the pool and then used to make all the allocations from that pool. For further details, see Custom memory pools.
-Memory for allocations is reserved out of larger block of VkDeviceMemory allocated from Vulkan internally. That is the main feature of this whole library. You can still request a separate memory block to be created for an allocation, just like you would do in a trivial solution without using any allocator. In that case, a buffer or image is always bound to that memory at offset 0. This is called a "dedicated allocation". You can explicitly request it by using flag VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. The library can also internally decide to use dedicated allocation in some cases, e.g.:
Memory for allocations is reserved out of larger block of VkDeviceMemory allocated from Vulkan internally. That is the main feature of this whole library. You can still request a separate memory block to be created for an allocation, just like you would do in a trivial solution without using any allocator. In that case, a buffer or image is always bound to that memory at offset 0. This is called a "dedicated allocation". You can explicitly request it by using flag VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. The library can also internally decide to use dedicated allocation in some cases, e.g.:
Please check "CONFIGURATION SECTION" in the code to find macros that you can define before each include of this file or change directly in this file to provide your own implementation of basic facilities like assert, min() and max() functions, mutex, atomic etc.
For example, define VMA_ASSERT(expr) before including the library to provide custom implementation of the assertion, compatible with your project. By default it is defined to standard C assert(expr) in _DEBUG configuration and empty otherwise.
Similarly, you can define VMA_LEAK_LOG_FORMAT macro to enable printing of leaked (unfreed) allocations, including their names and other parameters. Example:
Please check "CONFIGURATION SECTION" in the code to find macros that you can define before each include of this file or change directly in this file to provide your own implementation of basic facilities like assert, min() and max() functions, mutex, atomic etc.
+For example, define VMA_ASSERT(expr) before including the library to provide custom implementation of the assertion, compatible with your project. By default it is defined to standard C assert(expr) in _DEBUG configuration and empty otherwise.
+Similarly, you can define VMA_LEAK_LOG_FORMAT macro to enable printing of leaked (unfreed) allocations, including their names and other parameters. Example:
There are multiple ways to import pointers to Vulkan functions in the library. In the simplest case you don't need to do anything. If the compilation or linking of your program or the initialization of the VmaAllocator doesn't work for you, you can try to reconfigure it.
First, the allocator tries to fetch pointers to Vulkan functions linked statically, like this:
If you want to disable this feature, set configuration macro: #define VMA_STATIC_VULKAN_FUNCTIONS 0.
Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. You can fetch them e.g. using functions vkGetInstanceProcAddr and vkGetDeviceProcAddr or by using a helper library like volk.
Third, VMA tries to fetch remaining pointers that are still null by calling vkGetInstanceProcAddr and vkGetDeviceProcAddr on its own. You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr. Other pointers will be fetched automatically. If you want to disable this feature, set configuration macro: #define VMA_DYNAMIC_VULKAN_FUNCTIONS 0.
Finally, all the function pointers required by the library (considering selected Vulkan version and enabled extensions) are checked with VMA_ASSERT if they are not null.
If you want to disable this feature, set configuration macro: #define VMA_STATIC_VULKAN_FUNCTIONS 0.
+Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. You can fetch them e.g. using functions vkGetInstanceProcAddr and vkGetDeviceProcAddr or by using a helper library like volk.
+Third, VMA tries to fetch remaining pointers that are still null by calling vkGetInstanceProcAddr and vkGetDeviceProcAddr on its own. You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr. Other pointers will be fetched automatically. If you want to disable this feature, set configuration macro: #define VMA_DYNAMIC_VULKAN_FUNCTIONS 0.
+Finally, all the function pointers required by the library (considering selected Vulkan version and enabled extensions) are checked with VMA_ASSERT if they are not null.
+If you use custom allocator for CPU memory rather than default operator new and delete from C++, you can make this library using your allocator as well by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These functions will be passed to Vulkan, as well as used by the library itself to make any CPU-side allocations.
If you use custom allocator for CPU memory rather than default operator new and delete from C++, you can make this library using your allocator as well by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These functions will be passed to Vulkan, as well as used by the library itself to make any CPU-side allocations.
+The library makes calls to vkAllocateMemory() and vkFreeMemory() internally. You can setup callbacks to be informed about these calls, e.g. for the purpose of gathering some statistics. To do it, fill optional member VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
The library makes calls to vkAllocateMemory() and vkFreeMemory() internally. You can setup callbacks to be informed about these calls, e.g. for the purpose of gathering some statistics. To do it, fill optional member VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
+When device memory of certain heap runs out of free space, new allocations may fail (returning error code) or they may succeed, silently pushing some existing_ memory blocks from GPU VRAM to system RAM (which degrades performance). This behavior is implementation-dependent - it depends on GPU vendor and graphics driver.
On AMD cards it can be controlled while creating Vulkan device object by using VK_AMD_memory_overallocation_behavior extension, if available.
@@ -122,7 +109,7 @@A memory pool contains a number of VkDeviceMemory blocks. The library automatically creates and manages default pool for each memory type available on the device. Default memory pool automatically grows in size. Size of allocated blocks is also variable and managed automatically. You are using default pools whenever you leave VmaAllocationCreateInfo::pool = null.
A memory pool contains a number of VkDeviceMemory blocks. The library automatically creates and manages default pool for each memory type available on the device. Default memory pool automatically grows in size. Size of allocated blocks is also variable and managed automatically. You are using default pools whenever you leave VmaAllocationCreateInfo::pool = null.
You can create custom pool and allocate memory out of it. It can be useful if you want to:
pNext chain.To use custom memory pools:
usage.Example:
You have to free all allocations made from this pool before destroying it.
New versions of this library support creating dedicated allocations in custom pools. It is supported only when VmaPoolCreateInfo::blockSize = 0. To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and VmaAllocationCreateInfo::flags to VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
-When creating a pool, you must explicitly specify memory type index. To find the one suitable for your buffers or images, you can use helper functions vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). You need to provide structures with example parameters of buffers or images that you are going to create in that pool.
When creating buffers/images allocated in that pool, provide following parameters:
VkBufferCreateInfo: Prefer to pass same parameters as above. Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. Using different VK_BUFFER_USAGE_ flags may work, but you shouldn't create images in a pool intended for buffers or the other way around.pool member. Other members are ignored anyway.Custom pools are commonly overused by VMA users. While it may feel natural to keep some logical groups of resources separate in memory, in most cases it does more harm than good. Using custom pool shouldn't be your first choice. Instead, please make all allocations from default pools first and only use custom pools if you can prove and measure that it is beneficial in some way, e.g. it results in lower memory usage, better performance, etc.
Using custom pools has disadvantages:
VkDeviceMemory blocks. Some of them may be partially or even completely empty. Spreading allocations across multiple pools increases the amount of wasted (allocated but unbound) memory.Many of the common concerns can be addressed in a different way than using custom pools:
bufferImageGranularity limit automatically.nonCoherentAtomSize limit automatically. It also maps only those VkDeviceMemory blocks that need to map any allocation. It even tries to keep mappable and non-mappable allocations in separate blocks to minimize the amount of mapped memory.(1U << myMemoryTypeIndex) instead.Each Vulkan memory block managed by this library has accompanying metadata that keeps track of used and unused regions. By default, the metadata structure and algorithm tries to find best place for new allocations among free regions to optimize memory usage. This way you can allocate and free objects in any order.

Sometimes there is a need to use simpler, linear allocation algorithm. You can create custom pool that uses such algorithm by adding flag VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating VmaPool object. Then an alternative metadata management is used. It always creates new allocations after last one and doesn't reuse free regions after allocations freed in the middle. It results in better allocation performance and less memory consumed by metadata.

With this one flag, you can create a custom pool that can be used in many ways: free-at-once, stack, double stack, and ring buffer. See below for details. You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
-In a pool that uses linear algorithm, you still need to free all the allocations individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free them in any order. New allocations are always made after last one - free space in the middle is not reused. However, when you release all the allocation and the pool becomes empty, allocation starts from the beginning again. This way you can use linear algorithm to speed up creation of allocations that you are going to release all at once.

This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount value that allows multiple memory blocks.
-When you free an allocation that was created last, its space can be reused. Thanks to this, if you always release allocations in the order opposite to their creation (LIFO - Last In First Out), you can achieve behavior of a stack.

This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount value that allows multiple memory blocks.
-The space reserved by a custom pool with linear algorithm may be used by two stacks:
To make allocation from the upper stack, add flag VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT to VmaAllocationCreateInfo::flags.

Double stack is available only in pools with one memory block - VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
-When the two stacks' ends meet so there is not enough space between them for a new allocation, such allocation fails with usual VK_ERROR_OUT_OF_DEVICE_MEMORY error.
When the two stacks' ends meet so there is not enough space between them for a new allocation, such allocation fails with usual VK_ERROR_OUT_OF_DEVICE_MEMORY error.
+When you free some allocations from the beginning and there is not enough free space for a new one at the end of a pool, allocator's "cursor" wraps around to the beginning and starts allocation there. Thanks to this, if you always release allocations in the same order as you created them (FIFO - First In First Out), you can achieve behavior of a ring buffer / queue.

If you suspect a bug with memory usage, like usage of uninitialized memory or memory being overwritten out of bounds of an allocation, you can use debug features of this library to verify this.
-If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, you can enable automatic memory initialization to verify this. To do it, define macro VMA_DEBUG_INITIALIZE_ALLOCATIONS to 1.
If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, you can enable automatic memory initialization to verify this. To do it, define macro VMA_DEBUG_INITIALIZE_ALLOCATIONS to 1.
It makes memory of new allocations initialized to bit pattern 0xDCDCDCDC. Before an allocation is destroyed, its memory is filled with bit pattern 0xEFEFEFEF. Memory is automatically mapped and unmapped if necessary.
It makes memory of new allocations initialized to bit pattern 0xDCDCDCDC. Before an allocation is destroyed, its memory is filled with bit pattern 0xEFEFEFEF. Memory is automatically mapped and unmapped if necessary.
If you find these values while debugging your program, good chances are that you incorrectly read Vulkan memory that is allocated but not initialized, or already freed, respectively.
-Memory initialization works only with memory types that are HOST_VISIBLE and with allocations that can be mapped. It works also with dedicated allocations.
Memory initialization works only with memory types that are HOST_VISIBLE and with allocations that can be mapped. It works also with dedicated allocations.
+By default, allocations are laid out in memory blocks next to each other if possible (considering required alignment, bufferImageGranularity, and nonCoherentAtomSize).
By default, allocations are laid out in memory blocks next to each other if possible (considering required alignment, bufferImageGranularity, and nonCoherentAtomSize).

Define macro VMA_DEBUG_MARGIN to some non-zero value (e.g. 16) to enforce specified number of bytes as a margin after every allocation.
Define macro VMA_DEBUG_MARGIN to some non-zero value (e.g. 16) to enforce specified number of bytes as a margin after every allocation.

Margins appear in JSON dump as part of free space.
Note that enabling margins increases memory usage and fragmentation.
Margins do not apply to Virtual allocator.
-You can additionally define macro VMA_DEBUG_DETECT_CORRUPTION to 1 to enable validation of contents of the margins.
You can additionally define macro VMA_DEBUG_DETECT_CORRUPTION to 1 to enable validation of contents of the margins.
When this feature is enabled, number of bytes specified as VMA_DEBUG_MARGIN (it must be multiply of 4) after every allocation is filled with a magic number. This idea is also know as "canary". Memory is automatically mapped and unmapped if necessary.
This number is validated automatically when the allocation is destroyed. If it is not equal to the expected value, VMA_ASSERT() is executed. It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, which indicates a serious bug.
When this feature is enabled, number of bytes specified as VMA_DEBUG_MARGIN (it must be multiply of 4) after every allocation is filled with a magic number. This idea is also know as "canary". Memory is automatically mapped and unmapped if necessary.
+This number is validated automatically when the allocation is destroyed. If it is not equal to the expected value, VMA_ASSERT() is executed. It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, which indicates a serious bug.
You can also explicitly request checking margins of all allocations in all memory blocks that belong to specified memory types by using function vmaCheckCorruption(), or in memory blocks that belong to specified custom pool, by using function vmaCheckPoolCorruption().
-Margin validation (corruption detection) works only for memory types that are HOST_VISIBLE and HOST_COHERENT.
Margin validation (corruption detection) works only for memory types that are HOST_VISIBLE and HOST_COHERENT.
+At allocation and allocator destruction time VMA checks for unfreed and unmapped blocks using VMA_ASSERT_LEAK(). This macro defaults to an assertion, triggering a typically fatal error in Debug builds, and doing nothing in Release builds. You can provide your own definition of VMA_ASSERT_LEAK() to change this behavior.
At memory block destruction time VMA lists out all unfreed allocations using the VMA_LEAK_LOG_FORMAT() macro, which defaults to VMA_DEBUG_LOG_FORMAT, which in turn defaults to a no-op. If you're having trouble with leaks - for example, the aforementioned assertion triggers, but you don't quite know why -, overriding this macro to print out the the leaking blocks, combined with assigning individual names to allocations using vmaSetAllocationName(), can greatly aid in fixing them.
At allocation and allocator destruction time VMA checks for unfreed and unmapped blocks using VMA_ASSERT_LEAK(). This macro defaults to an assertion, triggering a typically fatal error in Debug builds, and doing nothing in Release builds. You can provide your own definition of VMA_ASSERT_LEAK() to change this behavior.
+At memory block destruction time VMA lists out all unfreed allocations using the VMA_LEAK_LOG_FORMAT() macro, which defaults to VMA_DEBUG_LOG_FORMAT, which in turn defaults to a no-op. If you're having trouble with leaks - for example, the aforementioned assertion triggers, but you don't quite know why -, overriding this macro to print out the the leaking blocks, combined with assigning individual names to allocations using vmaSetAllocationName(), can greatly aid in fixing them.
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.
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:
Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage() create/destroy an allocation and a buffer/image at once, these are just a shortcut for creating the resource, allocating memory, and binding them together. Defragmentation works on memory allocations only. You must handle the rest manually. Defragmentation is an iterative process that should repreat "passes" as long as related functions return VK_INCOMPLETE not VK_SUCCESS. In each pass:
Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage() create/destroy an allocation and a buffer/image at once, these are just a shortcut for creating the resource, allocating memory, and binding them together. Defragmentation works on memory allocations only. You must handle the rest manually. Defragmentation is an iterative process that should repreat "passes" as long as related functions return VK_INCOMPLETE not VK_SUCCESS. In each pass:
VkDeviceMemory + offset using vmaGetAllocationInfo().VkDeviceMemory blocks that became empty.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. 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.
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. 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.
Inside a pass, for each allocation that should be moved:
vkCmdCopyBuffer(), vkCmdCopyImage().HOST_VISIBLE and HOST_CACHED memory, you can copy its data on the CPU using memcpy().pass.pMoves[i].operation to VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. This will cancel the move.pass.pMoves[i].operation to VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.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.
-Defragmentation is always performed in each pool separately. Allocations are never moved between different Vulkan memory types. The size of the destination memory reserved for a moved allocation is the same as the original one. Alignment of an allocation as it was determined using vkGetBufferMemoryRequirements() etc. is also respected after defragmentation. Buffers/images should be recreated with the same VkBufferCreateInfo / VkImageCreateInfo parameters as the original ones.
Defragmentation is always performed in each pool separately. Allocations are never moved between different Vulkan memory types. The size of the destination memory reserved for a moved allocation is the same as the original one. Alignment of an allocation as it was determined using vkGetBufferMemoryRequirements() etc. is also respected after defragmentation. Buffers/images should be recreated with the same VkBufferCreateInfo / VkImageCreateInfo parameters as the original ones.
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.
Mapping is preserved on allocations that are moved during defragmentation. Whether through VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried using VmaAllocationInfo::pMappedData.
@@ -217,7 +204,7 @@VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT. VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and VK_MEMORY_PROPERTY_HOST_COHERENT_BIT. VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, prefers VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT. VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT. VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, prefers VK_MEMORY_PROPERTY_HOST_CACHED_BIT.
+Files | vk_mem_alloc.h | | | vk_mem_alloc.h | |||||