Vulkan Memory Allocator
|
To "map memory" in Vulkan means to obtain a CPU pointer to VkDeviceMemory
, to be able to read from it or write to it in CPU code. Mapping is possible only of memory allocated from a memory type that has VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
flag. Functions vkMapMemory()
, vkUnmapMemory()
are designed for this purpose. You can use them directly with memory allocated by this library, but it is not recommended because of following issue: Mapping the same VkDeviceMemory
block multiple times is illegal - only one mapping at a time is allowed. This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. Because of this, Vulkan Memory Allocator provides following facilities:
The library provides following functions for mapping of a specific VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). They are safer and more convenient to use than standard Vulkan functions. You can map an allocation multiple times simultaneously - mapping is reference-counted internally. You can also map different allocations simultaneously regardless of whether they use the same VkDeviceMemory
block. They way it's implemented is that the library always maps entire memory block, not just region of the allocation. For further details, see description of vmaMapMemory() function. Example:
Kepping your memory persistently mapped is generally OK in Vulkan. You don't need to unmap it before using its data on the GPU. The library provides a special feature designed for that: Allocations made with VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in VmaAllocationCreateInfo::flags stay mapped all the time, so you can just access CPU pointer to it any time without a need to call any "map" or "unmap" function. Example:
There are some exceptions though, when you should consider mapping memory only for a short period of time:
DEVICE_LOCAL + HOST_VISIBLE
memory (selected when you use VMA_MEMORY_USAGE_CPU_TO_GPU), then whenever a memory block allocated from this memory type stays mapped for the time of any call to vkQueueSubmit()
or vkQueuePresentKHR()
, this block is migrated by WDDM to system RAM, which degrades performance. It doesn't matter if that particular memory block is actually used by the command buffer being submitted.Memory in Vulkan doesn't need to be unmapped before using it on GPU, but unless a memory types has VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
flag set, you need to manually invalidate cache before reading of mapped pointer using function vkvkInvalidateMappedMemoryRanges()
and flush cache after writing to mapped pointer using function vkFlushMappedMemoryRanges()
. Example:
Please note that memory allocated with VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be host coherent.
Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) currently provide VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
flag on all memory types that are VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
, so on this platform you may not need to bother.
It may happen that your allocation ends up in memory that is HOST_VISIBLE
(available for mapping) despite it wasn't explicitly requested. For example, application may work on integrated graphics with unified memory (like Intel) or allocation from video memory might have failed, so the library chose system memory as fallback.
You can detect this case and map such allocation to access its memory on CPU directly, instead of launching a transfer operation. In order to do that: inspect allocInfo.memoryType
, call vmaGetMemoryTypeProperties(), and look for VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
flag in properties of that memory type.
You can even use VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations that are not necessarily HOST_VISIBLE
(e.g. using VMA_MEMORY_USAGE_GPU_ONLY). If the allocation ends up in memory type that is HOST_VISIBLE
, it will be persistently mapped and you can use it directly. If not, the flag is just ignored. Example: