Vulkan Memory Allocator
Loading...
Searching...
No Matches
Interop with other graphics APIs

VMA provides some features that help with interoperability with other graphics APIs, e.g. OpenGL, Direct3D 11, Direct3D 12.

Exporting memory

On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 HANDLE of a VkDeviceMemory block, to be able to reference the memory on other Vulkan logical devices or instances, in multiple processes, and/or in multiple APIs. VMA offers support for it.

Initialization

1) Make sure the extension is defined in the code by including following header before including VMA:

#include <vulkan/vulkan_win32.h>

2) Check if "VK_KHR_external_memory_win32" is available among device extensions. Enable it when creating the VkDevice object.

3) Enable the usage of this extension in VMA by setting flag VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT when calling vmaCreateAllocator().

4) Make sure that VMA has access to the vkGetMemoryWin32HandleKHR function by either enabling VMA_DYNAMIC_VULKAN_FUNCTIONS macro or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly. For more information, see Importing Vulkan functions.

Preparations

You can find example usage among tests, in file "Tests.cpp", function TestWin32Handles().

To use the extenion, buffers need to be created with VkExternalMemoryBufferCreateInfoKHR attached to their pNext chain, and memory allocations need to be made with VkExportMemoryAllocateInfoKHR attached to their pNext chain. To make use of them, you need to use Custom memory pools. Example:

constexpr VkExternalMemoryHandleTypeFlagsKHR handleType =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
handleType
};
VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.
exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo exampleAllocCreateInfo = {};
exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// Find memory type index to use for the custom pool.
uint32_t memTypeIndex;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,
&exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);
// Check res...
// Create a custom pool.
constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {
VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,
nullptr,
handleType
};
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.memoryTypeIndex = memTypeIndex;
poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;
VmaPool pool;
res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyPool(g_Allocator, pool);
void vmaDestroyPool(VmaAllocator allocator, VmaPool pool)
Destroys VmaPool object and frees Vulkan device memory.
VkResult vmaCreatePool(VmaAllocator allocator, const VmaPoolCreateInfo *pCreateInfo, VmaPool *pPool)
Allocates Vulkan device memory and creates VmaPool object.
VkResult vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
@ VMA_MEMORY_USAGE_AUTO
Definition vk_mem_alloc.h:551
Parameters of new VmaAllocation.
Definition vk_mem_alloc.h:1292
VmaMemoryUsage usage
Intended usage of memory.
Definition vk_mem_alloc.h:1300
Describes parameter of created VmaPool.
Definition vk_mem_alloc.h:1343
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition vk_mem_alloc.h:1346
void *VkMemoryAllocateInfo pMemoryAllocateNext
Additional pNext chain to be attached to VkMemoryAllocateInfo used for every allocation made by this ...
Definition vk_mem_alloc.h:1395
Represents custom memory pool.

Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block. No copy is made internally. This is why variable exportMemAllocInfo is defined as static.

If you want to export all memory allocated by VMA from certain memory types, also dedicated allocations or other allocations made from default pools, an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes. It should point to an array with VkExternalMemoryHandleTypeFlagsKHR to be automatically passed by the library through VkExportMemoryAllocateInfoKHR on each allocation made from a specific memory type. You should not mix these two methods in a way that allows to apply both to the same memory type. Otherwise, VkExportMemoryAllocateInfoKHR structure would be attached twice to the pNext chain of VkMemoryAllocateInfo.

Memory allocation

Finally, you can create a buffer with an allocation out of the custom pool. The buffer should use same flags as the sample buffer used to find the memory type. It should also specify VkExternalMemoryBufferCreateInfoKHR in its pNext chain.

VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
handleType
};
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = // Your desired buffer size.
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufCreateInfo.pNext = &externalMemBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool; // It is enough to set this one member.
VkBuffer buf;
res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, don't forget to destroy it!
vmaDestroyBuffer(g_Allocator, buf, alloc);
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Creates a new VkBuffer, allocates and binds memory for it.
VmaPool pool
Pool that this allocation should be created in.
Definition vk_mem_alloc.h:1324
Represents single memory allocation.

If you need each allocation to have its own device memory block and start at offset 0, you can still do by using VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.

Exporting Win32 handle

After the allocation is created, you can acquire a Win32 HANDLE to the VkDeviceMemory block it belongs to. VMA function vmaGetMemoryWin32Handle2() is a replacement of the Vulkan function vkGetMemoryWin32HandleKHR.

HANDLE handle;
res = vmaGetMemoryWin32Handle2(g_Allocator, alloc, handleType, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
VkResult vmaGetMemoryWin32Handle2(VmaAllocator allocator, VmaAllocation allocation, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE hTargetProcess, HANDLE *pHandle)
Given an allocation, returns Win32 handle that may be imported by other processes or APIs.

Documentation of the VK_KHR_external_memory_win32 extension states that:

If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.

This is ensured automatically inside VMA. If VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT is used as the handle type, or other NT handle types, the library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed. Every time you call vmaGetMemoryWin32Handle2(), VMA calls DuplicateHandle and returns a new handle that you need to close. For further information, please check the documentation of this function.

Custom alignment

Buffers or images exported to a different API like OpenGL may require a different alignment, higher than the one used by the library automatically, queried from functions like vkGetBufferMemoryRequirements. To impose such alignment:

You can create Custom memory pools for such allocations. Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation to be made out of this pool. The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image from a function like vkGetBufferMemoryRequirements, which is called by VMA automatically.

If you want to create a buffer with a specific minimum alignment out of default pools, you can use special function vmaCreateBufferWithAlignment(), which takes additional parameter minAlignment.

Note the problem of alignment affects only resources placed inside bigger VkDeviceMemory blocks and not dedicated allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block. You can ensure that an allocation is created as dedicated by using VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.

Extended allocation information

If you want to rely on VMA to allocate your buffers and images inside larger memory blocks, but you need to know the size of the entire block and whether the allocation was made with its own dedicated memory, use function vmaGetAllocationInfo2() to retrieve extended allocation information in structure VmaAllocationInfo2, which provides extra members: blockSize and dedicatedMemory.

Importing memory

Importing external memory requires attaching an extra structure like VkImportMemoryWin32HandleInfoKHR to the pNext chain of VkMemoryAllocateInfo structure. VMA offers support for it by providing functions that allocate memory, create a buffer or an image always with a dedicated VkDeviceMemory block and accept custom pNext pointer: vmaAllocateDedicatedMemory(), vmaCreateDedicatedBuffer(), vmaCreateDedicatedImage(). Example:

constexpr VkExternalMemoryHandleTypeFlagBits handleType =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR };
externalMemBufCreateInfo.handleTypes = handleType;
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.pNext = &externalMemBufCreateInfo; // !!!
bufCreateInfo.size = ...
bufCreateInfo.usage = ...
VkImportMemoryWin32HandleInfoKHR importInfo = {
VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR };
importInfo.handleType = handleType;
importInfo.handle = myExternalHandleToImport;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
VkBuffer buf = VK_NULL_HANDLE;
VmaAllocation alloc = VK_NULL_HANDLE;
VkResult res = vmaCreateDedicatedBuffer(allocator, &bufCreateInfo, &allocCreateInfo,
&importInfo, // pMemoryAllocateNext !!!
&buf, &alloc, nullptr);
// Check res...
VkResult vmaCreateDedicatedBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, void *(VkMemoryAllocateInfo) pMemoryAllocateNext, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Creates a dedicated buffer while offering extra parameter pMemoryAllocateNext.