Thread safety
- The library has no global state, so separate D3D12MA::Allocator objects can be used independently. In typical applications there should be no need to create multiple such objects though - one per
ID3D12Device
is enough.
- All calls to methods of D3D12MA::Allocator class are safe to be made from multiple threads simultaneously because they are synchronized internally when needed.
- When the allocator is created with D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED, calls to methods of D3D12MA::Allocator class must be made from a single thread or synchronized by the user. Using this flag may improve performance.
- D3D12MA::VirtualBlock is not safe to be used from multiple threads simultaneously.
Versioning and compatibility
The library uses Semantic Versioning, which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where:
- Incremented Patch version means a release is backward- and forward-compatible, introducing only some internal improvements, bug fixes, optimizations etc. or changes that are out of scope of the official API described in this documentation.
- Incremented Minor version means a release is backward-compatible, so existing code that uses the library should continue to work, while some new symbols could have been added: new structures, functions, new values in existing enums and bit flags, new structure members, but not new function parameters.
- Incrementing Major version means a release could break some backward compatibility.
All changes between official releases are documented in file "CHANGELOG.md".
- Warning
- Backward compatiblity is considered on the level of C++ source code, not binary linkage. Adding new members to existing structures is treated as backward compatible if initializing the new members to binary zero results in the old behavior. You should always fully initialize all library structures to zeros and not rely on their exact binary size.
Features not supported
Features deliberately excluded from the scope of this library:
- Descriptor allocation. Although also called "heaps", objects that represent descriptors are separate part of the D3D12 API from buffers and textures. You can still use Virtual allocator to manage descriptors and their ranges inside a descriptor heap.
- Support for reserved (tiled) resources. We don't recommend using them.
- Support for
ID3D12Device::Evict
and MakeResident
. We don't recommend using them. You can call them on the D3D12 objects manually. Plese keep in mind, however, that eviction happens on the level of entire ID3D12Heap
memory blocks and not individual buffers or textures which may be placed inside them.
- Handling CPU memory allocation failures. When dynamically creating small C++ objects in CPU memory (not the GPU memory), allocation failures are not handled gracefully, because that would complicate code significantly and is usually not needed in desktop PC applications anyway. Success of an allocation is just checked with an assert.
- Code free of any compiler warnings. There are many preprocessor macros that make some variables unused, function parameters unreferenced, or conditional expressions constant in some configurations. The code of this library should not be bigger or more complicated just to silence these warnings. It is recommended to disable such warnings instead.
- This is a C++ library. Bindings or ports to any other programming languages are welcome as external projects but are not going to be included into this repository.
Thread safety
- The library has no global state, so separate VmaAllocator objects can be used independently. There should be no need to create multiple such objects though - one per
VkDevice
is enough.
- By default, all calls to functions that take VmaAllocator as first parameter are safe to call from multiple threads simultaneously because they are synchronized internally when needed. This includes allocation and deallocation from default memory pool, as well as custom VmaPool.
- When the allocator is created with VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT flag, calls to functions that take such VmaAllocator object must be synchronized externally.
- Access to a VmaAllocation object must be externally synchronized. For example, you must not call vmaGetAllocationInfo() and vmaMapMemory() from different threads at the same time if you pass the same VmaAllocation object to these functions.
- VmaVirtualBlock is not safe to be used from multiple threads simultaneously.
Versioning and compatibility
The library uses Semantic Versioning, which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where:
- Incremented Patch version means a release is backward- and forward-compatible, introducing only some internal improvements, bug fixes, optimizations etc. or changes that are out of scope of the official API described in this documentation.
- Incremented Minor version means a release is backward-compatible, so existing code that uses the library should continue to work, while some new symbols could have been added: new structures, functions, new values in existing enums and bit flags, new structure members, but not new function parameters.
- Incrementing Major version means a release could break some backward compatibility.
All changes between official releases are documented in file "CHANGELOG.md".
- Warning
- Backward compatibility is considered on the level of C++ source code, not binary linkage. Adding new members to existing structures is treated as backward compatible if initializing the new members to binary zero results in the old behavior. You should always fully initialize all library structures to zeros and not rely on their exact binary size.
Validation layer warnings
When using this library, you can meet following types of warnings issued by Vulkan validation layer. They don't necessarily indicate a bug, so you may need to just ignore them.
- vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.
- It happens when VK_KHR_dedicated_allocation extension is enabled.
vkGetBufferMemoryRequirements2KHR
function is used instead, while validation layer seems to be unaware of it.
- Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.
- It happens when you map a buffer or image, because the library maps entire
VkDeviceMemory
block, where different types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
- Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.
Allocation algorithm
The library uses following algorithm for allocation, in order:
- Try to find free range of memory in existing blocks.
- If failed, try to create a new block of
VkDeviceMemory
, with preferred block size.
- If failed, try to create such block with size / 2, size / 4, size / 8.
- If failed, try to allocate separate
VkDeviceMemory
for this allocation, just like when you use VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
- If failed, choose other memory type that meets the requirements specified in VmaAllocationCreateInfo and go to point 1.
- If failed, return
VK_ERROR_OUT_OF_DEVICE_MEMORY
.
Features not supported
Features deliberately excluded from the scope of this library:
- Data transfer. Uploading (streaming) and downloading data of buffers and images between CPU and GPU memory and related synchronization is responsibility of the user. Defining some "texture" object that would automatically stream its data from a staging copy in CPU memory to GPU memory would rather be a feature of another, higher-level library implemented on top of VMA. VMA doesn't record any commands to a
VkCommandBuffer
. It just allocates memory.
- Recreation of buffers and images. Although the library has functions for buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to recreate these objects yourself after defragmentation. That is because the big structures
VkBufferCreateInfo
, VkImageCreateInfo
are not stored in VmaAllocation object.
- Handling CPU memory allocation failures. When dynamically creating small C++ objects in CPU memory (not Vulkan memory), allocation failures are not checked and handled gracefully, because that would complicate code significantly and is usually not needed in desktop PC applications anyway. Success of an allocation is just checked with an assert.
- Code free of any compiler warnings. Maintaining the library to compile and work correctly on so many different platforms is hard enough. Being free of any warnings, on any version of any compiler, is simply not feasible. There are many preprocessor macros that make some variables unused, function parameters unreferenced, or conditional expressions constant in some configurations. The code of this library should not be bigger or more complicated just to silence these warnings. It is recommended to disable such warnings instead.
- This is a C++ library with C interface. Bindings or ports to any other programming languages are welcome as external projects but are not going to be included into this repository.