Vulkan Memory Allocator: VK_KHR_external_memory_win32

Vulkan Memory Allocator

Loading...
Searching...
No Matches
VK_KHR_external_memory_win32

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:

// Define an example buffer and allocation parameters.
VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
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,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
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);
vmaDestroyPool
void vmaDestroyPool(VmaAllocator allocator, VmaPool pool)
Destroys VmaPool object and frees Vulkan device memory.
vmaCreatePool
VkResult vmaCreatePool(VmaAllocator allocator, const VmaPoolCreateInfo *pCreateInfo, VmaPool *pPool)
Allocates Vulkan device memory and creates VmaPool object.
vmaFindMemoryTypeIndexForBufferInfo
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
@ VMA_MEMORY_USAGE_AUTO
Definition vk_mem_alloc.h:550
VmaAllocationCreateInfo
Parameters of new VmaAllocation.
Definition vk_mem_alloc.h:1291
VmaAllocationCreateInfo::usage
VmaMemoryUsage usage
Intended usage of memory.
Definition vk_mem_alloc.h:1299
VmaPoolCreateInfo
Describes parameter of created VmaPool.
Definition vk_mem_alloc.h:1342
VmaPoolCreateInfo::memoryTypeIndex
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition vk_mem_alloc.h:1345
VmaPoolCreateInfo::pMemoryAllocateNext
void *VkMemoryAllocateInfo pMemoryAllocateNext
Additional pNext chain to be attached to VkMemoryAllocateInfo used for every allocation made by this ...
Definition vk_mem_alloc.h:1394
VmaPool
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`.

# 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,
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
};
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;
VmaAllocation alloc;
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);
vmaDestroyBuffer
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
vmaCreateBuffer
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.
VmaAllocationCreateInfo::pool
VmaPool pool
Pool that this allocation should be created in.
Definition vk_mem_alloc.h:1323
VmaAllocation
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 vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.

HANDLE handle;
res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);
// Check res...
// YOUR OTHER CODE COMES HERE....
// At the end, you must close the handle.
CloseHandle(handle);
vmaGetMemoryWin32Handle
VkResult vmaGetMemoryWin32Handle(VmaAllocator allocator, VmaAllocation allocation, 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. 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 vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.

For further information, please check documentation of the vmaGetMemoryWin32Handle() function.