|
66 | 66 | <div class="title">Memory mapping </div> </div>
|
67 | 67 | </div><!--header-->
|
68 | 68 | <div class="contents">
|
69 |
| -<div class="textblock"><h1><a class="anchor" id="persistently_mapped_memory"></a> |
| 69 | +<div class="textblock"><p>To "map memory" in Vulkan means to obtain a CPU pointer to <code>VkDeviceMemory</code>, 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 <code>VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT</code> flag. Functions <code>vkMapMemory()</code>, <code>vkUnmapMemory()</code> 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 <code>VkDeviceMemory</code> 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:</p> |
| 70 | +<h1><a class="anchor" id="memory_mapping_mapping_functions"></a> |
| 71 | +Mapping functions</h1> |
| 72 | +<p>The library provides following functions for mapping of a specific <code>VmaAllocation</code>: <a class="el" href="vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069" title="Maps memory represented by given allocation and returns pointer to it. ">vmaMapMemory()</a>, <a class="el" href="vk__mem__alloc_8h.html#a9bc268595cb33f6ec4d519cfce81ff45" title="Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). ">vmaUnmapMemory()</a>. 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 <code>VkDeviceMemory</code> 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 <a class="el" href="vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069" title="Maps memory represented by given allocation and returns pointer to it. ">vmaMapMemory()</a> function. Example:</p> |
| 73 | +<div class="fragment"><div class="line"><span class="comment">// Having these objects initialized:</span></div><div class="line"></div><div class="line"><span class="keyword">struct </span>ConstantBuffer</div><div class="line">{</div><div class="line"> ...</div><div class="line">};</div><div class="line">ConstantBuffer constantBufferData;</div><div class="line"></div><div class="line">VmaAllocator allocator;</div><div class="line">VmaBuffer constantBuffer;</div><div class="line">VmaAllocation constantBufferAllocation;</div><div class="line"></div><div class="line"><span class="comment">// You can map and fill your buffer using following code:</span></div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* mappedData;</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069">vmaMapMemory</a>(allocator, constantBufferAllocation, &mappedData);</div><div class="line">memcpy(mappedData, &constantBufferData, <span class="keyword">sizeof</span>(constantBufferData));</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#a9bc268595cb33f6ec4d519cfce81ff45">vmaUnmapMemory</a>(allocator, constantBufferAllocation);</div></div><!-- fragment --><h1><a class="anchor" id="memory_mapping_persistently_mapped_memory"></a> |
70 | 74 | Persistently mapped memory</h1>
|
71 |
| -<p>If you need to map memory on host, it may happen that two allocations are assigned to the same <code>VkDeviceMemory</code> block, so if you map them both at the same time, it will cause error because mapping single memory block multiple times is illegal in Vulkan.</p> |
72 |
| -<p>TODO update this...</p> |
73 |
| -<p>It is safer, more convenient and more efficient to use special feature designed for that: persistently mapped memory. Allocations made with <code>VMA_ALLOCATION_CREATE_MAPPED_BIT</code> flag set in <a class="el" href="struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b" title="Use VmaAllocationCreateFlagBits enum. ">VmaAllocationCreateInfo::flags</a> are returned from device memory blocks that stay mapped all the time, so you can just access CPU pointer to it. <a class="el" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2" title="Pointer to the beginning of this allocation as mapped data. ">VmaAllocationInfo::pMappedData</a> pointer is already offseted to the beginning of particular allocation. Example:</p> |
74 |
| -<div class="fragment"><div class="line">VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };</div><div class="line">bufCreateInfo.size = 1024;</div><div class="line">bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;</div><div class="line"></div><div class="line"><a class="code" href="struct_vma_allocation_create_info.html">VmaAllocationCreateInfo</a> allocCreateInfo = {};</div><div class="line">allocCreateInfo.<a class="code" href="struct_vma_allocation_create_info.html#accb8b06b1f677d858cb9af20705fa910">usage</a> = <a class="code" href="vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305cca40bdf4cddeffeb12f43d45ca1286e0a5">VMA_MEMORY_USAGE_CPU_ONLY</a>;</div><div class="line">allocCreateInfo.<a class="code" href="struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b">flags</a> = <a class="code" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a11da372cc3a82931c5e5d6146cd9dd1f">VMA_ALLOCATION_CREATE_MAPPED_BIT</a>;</div><div class="line"></div><div class="line">VkBuffer buf;</div><div class="line">VmaAllocation alloc;</div><div class="line"><a class="code" href="struct_vma_allocation_info.html">VmaAllocationInfo</a> allocInfo;</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#ac72ee55598617e8eecca384e746bab51">vmaCreateBuffer</a>(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);</div><div class="line"></div><div class="line"><span class="comment">// Buffer is immediately mapped. You can access its memory.</span></div><div class="line">memcpy(allocInfo.<a class="code" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2">pMappedData</a>, myData, 1024);</div></div><!-- fragment --><p>Memory in Vulkan doesn't need to be unmapped before using it e.g. for transfers, but if you are not sure whether it's <code>HOST_COHERENT</code> (here is surely is because it's created with <code>VMA_MEMORY_USAGE_CPU_ONLY</code>), you should check it. If it's not, you should call <code>vkInvalidateMappedMemoryRanges()</code> before reading and <code>vkFlushMappedMemoryRanges()</code> after writing to mapped memory on CPU. Example:</p> |
75 |
| -<div class="fragment"><div class="line">VkMemoryPropertyFlags memFlags;</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#a8701444752eb5de4464adb5a2b514bca">vmaGetMemoryTypeProperties</a>(allocator, allocInfo.<a class="code" href="struct_vma_allocation_info.html#a7f6b0aa58c135e488e6b40a388dad9d5">memoryType</a>, &memFlags);</div><div class="line"><span class="keywordflow">if</span>((memFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)</div><div class="line">{</div><div class="line"> VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };</div><div class="line"> memRange.memory = allocInfo.<a class="code" href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67">deviceMemory</a>;</div><div class="line"> memRange.offset = allocInfo.<a class="code" href="struct_vma_allocation_info.html#a4a3c732388dbdc7a23f9365b00825268">offset</a>;</div><div class="line"> memRange.size = allocInfo.<a class="code" href="struct_vma_allocation_info.html#aac76d113a6a5ccbb09fea00fb25fd18f">size</a>;</div><div class="line"> vkFlushMappedMemoryRanges(device, 1, &memRange);</div><div class="line">}</div></div><!-- fragment --><h1><a class="anchor" id="amd_perf_note"></a> |
76 |
| -Note on performance</h1> |
77 |
| -<p>There is a situation that you should be careful about. It happens only if all of following conditions are met:</p> |
78 |
| -<ol type="1"> |
79 |
| -<li>You use AMD GPU.</li> |
80 |
| -<li>You use the memory type that is both <code>DEVICE_LOCAL</code> and <code>HOST_VISIBLE</code> (used when you specify <code>VMA_MEMORY_USAGE_CPU_TO_GPU</code>).</li> |
81 |
| -<li>Operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2).</li> |
82 |
| -</ol> |
83 |
| -<p>Then whenever a <code>VkDeviceMemory</code> block allocated from this memory type is mapped for the time of any call to <code>vkQueueSubmit()</code> or <code>vkQueuePresentKHR()</code>, 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.</p> |
84 |
| -<p>To avoid this problem, either make sure to unmap all allocations made from this memory type before your Submit and Present, or use <code>VMA_MEMORY_USAGE_GPU_ONLY</code> and transfer from a staging buffer in <code>VMA_MEMORY_USAGE_CPU_ONLY</code>, which can safely stay mapped all the time. </p> |
| 75 | +<p>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 <code>VMA_ALLOCATION_CREATE_MAPPED_BIT</code> flag set in <a class="el" href="struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b" title="Use VmaAllocationCreateFlagBits enum. ">VmaAllocationCreateInfo::flags</a> 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:</p> |
| 76 | +<div class="fragment"><div class="line">VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };</div><div class="line">bufCreateInfo.size = <span class="keyword">sizeof</span>(ConstantBuffer);</div><div class="line">bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;</div><div class="line"></div><div class="line"><a class="code" href="struct_vma_allocation_create_info.html">VmaAllocationCreateInfo</a> allocCreateInfo = {};</div><div class="line">allocCreateInfo.<a class="code" href="struct_vma_allocation_create_info.html#accb8b06b1f677d858cb9af20705fa910">usage</a> = <a class="code" href="vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305cca40bdf4cddeffeb12f43d45ca1286e0a5">VMA_MEMORY_USAGE_CPU_ONLY</a>;</div><div class="line">allocCreateInfo.<a class="code" href="struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b">flags</a> = <a class="code" href="vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597a11da372cc3a82931c5e5d6146cd9dd1f">VMA_ALLOCATION_CREATE_MAPPED_BIT</a>;</div><div class="line"></div><div class="line">VkBuffer buf;</div><div class="line">VmaAllocation alloc;</div><div class="line"><a class="code" href="struct_vma_allocation_info.html">VmaAllocationInfo</a> allocInfo;</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#ac72ee55598617e8eecca384e746bab51">vmaCreateBuffer</a>(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);</div><div class="line"></div><div class="line"><span class="comment">// Buffer is already mapped. You can access its memory.</span></div><div class="line">memcpy(allocInfo.<a class="code" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2">pMappedData</a>, &constantBufferData, <span class="keyword">sizeof</span>(constantBufferData));</div></div><!-- fragment --><p>There are some exceptions though, when you should consider mapping memory only for a short period of time:</p> |
| 77 | +<ul> |
| 78 | +<li>When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2), device is discrete AMD GPU, and memory type is the special 256 MiB pool of <code>DEVICE_LOCAL + HOST_VISIBLE</code> memory (selected when you use <code>VMA_MEMORY_USAGE_CPU_TO_GPU</code>), then whenever a memory block allocated from this memory type stays mapped for the time of any call to <code>vkQueueSubmit()</code> or <code>vkQueuePresentKHR()</code>, 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.</li> |
| 79 | +<li>Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.</li> |
| 80 | +</ul> |
| 81 | +<h1><a class="anchor" id="memory_mapping_cache_control"></a> |
| 82 | +Cache control</h1> |
| 83 | +<p>Memory in Vulkan doesn't need to be unmapped before using it on GPU, but unless a memory types has <code>VK_MEMORY_PROPERTY_HOST_COHERENT_BIT</code> flag set, you need to manually invalidate cache before reading of mapped pointer using function <code>vkvkInvalidateMappedMemoryRanges()</code> and flush cache after writing to mapped pointer using function <code>vkFlushMappedMemoryRanges()</code>. Example:</p> |
| 84 | +<div class="fragment"><div class="line">memcpy(allocInfo.<a class="code" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2">pMappedData</a>, &constantBufferData, <span class="keyword">sizeof</span>(constantBufferData));</div><div class="line"></div><div class="line">VkMemoryPropertyFlags memFlags;</div><div class="line"><a class="code" href="vk__mem__alloc_8h.html#a8701444752eb5de4464adb5a2b514bca">vmaGetMemoryTypeProperties</a>(allocator, allocInfo.<a class="code" href="struct_vma_allocation_info.html#a7f6b0aa58c135e488e6b40a388dad9d5">memoryType</a>, &memFlags);</div><div class="line"><span class="keywordflow">if</span>((memFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)</div><div class="line">{</div><div class="line"> VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };</div><div class="line"> memRange.memory = allocInfo.<a class="code" href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67">deviceMemory</a>;</div><div class="line"> memRange.offset = allocInfo.<a class="code" href="struct_vma_allocation_info.html#a4a3c732388dbdc7a23f9365b00825268">offset</a>;</div><div class="line"> memRange.size = allocInfo.<a class="code" href="struct_vma_allocation_info.html#aac76d113a6a5ccbb09fea00fb25fd18f">size</a>;</div><div class="line"> vkFlushMappedMemoryRanges(device, 1, &memRange);</div><div class="line">}</div></div><!-- fragment --><p>Please note that memory allocated with <code>VMA_MEMORY_USAGE_CPU_ONLY</code> is guaranteed to be host coherent.</p> |
| 85 | +<p>Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) currently provide <code>VK_MEMORY_PROPERTY_HOST_COHERENT_BIT</code> flag on all memory types that are <code>VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT</code>, so on this platform you may not need to bother. </p> |
85 | 86 | </div></div><!-- contents -->
|
86 | 87 | <!-- start footer part -->
|
87 | 88 | <hr class="footer"/><address class="footer"><small>
|
|
0 commit comments