Merge branch 'master' into sparse_binding_example

# Conflicts:
#	docs/html/vk__mem__alloc_8h.html
#	docs/html/vk__mem__alloc_8h_source.html
#	src/Tests.cpp
#	src/VulkanSample.cpp
#	src/vk_mem_alloc.h
diff --git a/README.md b/README.md
index f82caa9..ebc9a23 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
 Memory allocation and resource (buffer and image) creation in Vulkan is difficult (comparing to older graphics API-s, like D3D11 or OpenGL) for several reasons:
 
 - It requires a lot of boilerplate code, just like everything else in Vulkan, because it is a low-level and high-performance API.
-- There is additional level of indirection: `VkDeviceMemory` is allocated separately from creating `VkBuffer`/`VkImage` and they must be bound together. The binding cannot be changed later - resource must be recreated.
+- There is additional level of indirection: `VkDeviceMemory` is allocated separately from creating `VkBuffer`/`VkImage` and they must be bound together.
 - Driver must be queried for supported memory heaps and memory types. Different IHVs provide different types of it.
 - It is recommended practice to allocate bigger chunks of memory and assign parts of them to particular resources.
 
@@ -41,7 +41,7 @@
 - Configuration: Fill optional members of CreateInfo structure to provide custom CPU memory allocator, pointers to Vulkan functions and other parameters.
 - Customization: Predefine appropriate macros to provide your own implementation of all external facilities used by the library, from assert, mutex, and atomic, to vector and linked list. 
 - Support for memory mapping, reference-counted internally. Support for persistently mapped memory: Just allocate with appropriate flag and you get access to mapped pointer.
-- Support for non-coherent memory. Functions that flush/invalidate memory. nonCoherentAtomSize is respected automatically.
+- Support for non-coherent memory. Functions that flush/invalidate memory. `nonCoherentAtomSize` is respected automatically.
 - Custom memory pools: Create a pool with desired parameters (e.g. fixed or limited maximum size) and allocate memory out of it.
 - Linear allocator: Create a pool with linear algorithm and use it for much faster allocations and deallocations in free-at-once, stack, double stack, or ring buffer fashion.
 - Support for VK_KHR_dedicated_allocation extension: Just enable it and it will be used automatically by the library.
@@ -60,7 +60,7 @@
 - Public interface in C, in same convention as Vulkan API. Implementation in C++.
 - Error handling implemented by returning `VkResult` error codes - same way as in Vulkan.
 - Interface documented using Doxygen-style comments.
-- Platform-independent, but developed and tested on Windows using Visual Studio. Continuous integration setup for Windows and Linux. Tested also on Android and MacOS.
+- Platform-independent, but developed and tested on Windows using Visual Studio. Continuous integration setup for Windows and Linux. Tested also on Android, MacOS, and other platforms.
 
 # Example
 
@@ -94,12 +94,16 @@
 # Software using this library
 
 - **[Filament](https://github.com/google/filament)** - physically based rendering engine for Android, Windows, Linux and macOS, from Google. Apache License 2.0.
+- **[Skia](https://github.com/google/skia)** - complete 2D graphic library for drawing Text, Geometries, and Images, from Google.
+- **[PowerVR SDK](https://github.com/powervr-graphics/Native_SDK)** - C++ cross-platform 3D graphics SDK, from Imagination. License: MIT.
 - **[Anvil](https://github.com/GPUOpen-LibrariesAndSDKs/Anvil)** - cross-platform framework for Vulkan. License: MIT.
 - **[vkDOOM3](https://github.com/DustinHLand/vkDOOM3)** - Vulkan port of GPL DOOM 3 BFG Edition. License: GNU GPL.
 - **[Lightweight Java Game Library (LWJGL)](https://www.lwjgl.org/)** - includes binding of the library for Java. License: BSD.
 - **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0.
 - **[VK9](https://github.com/disks86/VK9)** - Direct3D 9 compatibility layer using Vulkan. Zlib lincese.
 
+[Many other projects on GitHub](https://github.com/search?q=AMD_VULKAN_MEMORY_ALLOCATOR_H&type=Code) and some game development studios that use Vulkan in their games.
+
 # See also
 
 - **[Awesome Vulkan](https://github.com/vinjn/awesome-vulkan)** - a curated list of awesome Vulkan libraries, debuggers and resources.
diff --git a/docs/Recording file format.md b/docs/Recording file format.md
index b01eaf4..875c638 100644
--- a/docs/Recording file format.md
+++ b/docs/Recording file format.md
@@ -23,7 +23,7 @@
 VmaReplay application supports all older versions.

 Current version is:

 

-    1,3

+    1,4

 

 # Configuration

 

@@ -204,6 +204,11 @@
 

 - pool : pointer

 

+**vmaResizeAllocation** (min format version: 1.4)

+

+- allocation : pointer

+- newSize : uint64

+

 # Data types

 

 **bool**

@@ -228,7 +233,7 @@
 # Example file

 

     Vulkan Memory Allocator,Calls recording

-    1,3

+    1,4

     Config,Begin

     PhysicalDevice,apiVersion,4198477

     PhysicalDevice,driverVersion,8388653

@@ -284,4 +289,4 @@
     12552,0.695,0,vmaDestroyImage,000001D85B8B1620

     12552,0.695,0,vmaDestroyBuffer,000001D85B8B16C0

     12552,0.695,0,vmaDestroyBuffer,000001D85B8B1A80

-    12552,0.695,0,vmaDestroyAllocator
\ No newline at end of file
+    12552,0.695,0,vmaDestroyAllocator

diff --git a/docs/html/general_considerations.html b/docs/html/general_considerations.html
index b3e7dea..3dc7017 100644
--- a/docs/html/general_considerations.html
+++ b/docs/html/general_considerations.html
@@ -110,9 +110,12 @@
 Features not supported</h1>
 <p>Features deliberately excluded from the scope of this library:</p>
 <ul>
-<li>Support for sparse binding and sparse residency. You can still use these features (when supported by the device) with VMA. You just need to do it yourself. Any explicit support for sparse binding/residency would rather require another, higher-level library on top of VMA.</li>
+<li>Support for sparse binding and sparse residency. You can still use these features (when supported by the device) with VMA. You just need to do it yourself. Allocate memory pages with <a class="el" href="vk__mem__alloc_8h.html#abf28077dbf82d0908b8acbe8ee8dd9b8" title="General purpose memory allocation. ">vmaAllocateMemory()</a>. Any explicit support for sparse binding/residency would rather require another, higher-level library on top of VMA.</li>
 <li>Data transfer - issuing commands that transfer data between buffers or images, any usage of <code>VkCommandList</code> or <code>VkQueue</code> and related synchronization is responsibility of the user.</li>
 <li>Allocations for imported/exported external memory. They tend to require explicit memory type index and dedicated allocation anyway, so they don't interact with main features of this library. Such special purpose allocations should be made manually, using <code>vkCreateBuffer()</code> and <code>vkAllocateMemory()</code>.</li>
+<li>Recreation of buffers and images. Although the library has functions for buffer and image creation (<a class="el" href="vk__mem__alloc_8h.html#ac72ee55598617e8eecca384e746bab51">vmaCreateBuffer()</a>, <a class="el" href="vk__mem__alloc_8h.html#a02a94f25679275851a53e82eacbcfc73" title="Function similar to vmaCreateBuffer(). ">vmaCreateImage()</a>), you need to recreate these objects yourself after defragmentation. That's because the big structures <code>VkBufferCreateInfo</code>, <code>VkImageCreateInfo</code> are not stored in <a class="el" href="struct_vma_allocation.html" title="Represents single memory allocation. ">VmaAllocation</a> object.</li>
+<li>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.</li>
+<li>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.</li>
 <li>Support for any programming languages other than C/C++. Bindings to other languages are welcomed as external projects. </li>
 </ul>
 </div></div><!-- contents -->
diff --git a/docs/html/globals.html b/docs/html/globals.html
index 5b2d293..ac78f54 100644
--- a/docs/html/globals.html
+++ b/docs/html/globals.html
@@ -322,7 +322,7 @@
 : <a class="el" href="vk__mem__alloc_8h.html#ad63b2113c0bfdbeade1cb498f5a8580d">vk_mem_alloc.h</a>
 </li>
 <li>VmaPoolCreateFlagBits
-: <a class="el" href="vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7">vk_mem_alloc.h</a>
+: <a class="el" href="vk__mem__alloc_8h.html#a8f93195158e0e2ac80ca352064e71c1f">vk_mem_alloc.h</a>
 </li>
 <li>VmaPoolCreateFlags
 : <a class="el" href="vk__mem__alloc_8h.html#a2770e325ea42e087c1b91fdf46d0292a">vk_mem_alloc.h</a>
@@ -342,6 +342,9 @@
 <li>VmaRecordSettings
 : <a class="el" href="vk__mem__alloc_8h.html#a0ab61e87ff6365f1d59915eadc37a9f0">vk_mem_alloc.h</a>
 </li>
+<li>vmaResizeAllocation()
+: <a class="el" href="vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696">vk_mem_alloc.h</a>
+</li>
 <li>vmaSetAllocationUserData()
 : <a class="el" href="vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f">vk_mem_alloc.h</a>
 </li>
diff --git a/docs/html/globals_func.html b/docs/html/globals_func.html
index 36c7782..203a2c4 100644
--- a/docs/html/globals_func.html
+++ b/docs/html/globals_func.html
@@ -169,6 +169,9 @@
 <li>vmaMapMemory()
 : <a class="el" href="vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069">vk_mem_alloc.h</a>
 </li>
+<li>vmaResizeAllocation()
+: <a class="el" href="vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696">vk_mem_alloc.h</a>
+</li>
 <li>vmaSetAllocationUserData()
 : <a class="el" href="vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f">vk_mem_alloc.h</a>
 </li>
diff --git a/docs/html/memory_mapping.html b/docs/html/memory_mapping.html
index e464c14..c584e46 100644
--- a/docs/html/memory_mapping.html
+++ b/docs/html/memory_mapping.html
@@ -82,6 +82,7 @@
 <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"><a class="code" href="struct_vma_allocation.html">VmaAllocation</a> 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, &amp;bufCreateInfo, &amp;allocCreateInfo, &amp;buf, &amp;alloc, &amp;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>, &amp;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>
 <ul>
 <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 <a class="el" href="vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305cca9066b52c5a7079bb74a69aaf8b92ff67">VMA_MEMORY_USAGE_CPU_TO_GPU</a>), 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>
+<li>On Mac/MoltenVK there is a known bug - <a href="https://github.com/KhronosGroup/MoltenVK/issues/175">Issue #175</a> which requires unmapping before GPU can see updated texture.</li>
 <li>Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.</li>
 </ul>
 <h1><a class="anchor" id="memory_mapping_cache_control"></a>
diff --git a/docs/html/quick_start.html b/docs/html/quick_start.html
index bc21235..628fd09 100644
--- a/docs/html/quick_start.html
+++ b/docs/html/quick_start.html
@@ -79,6 +79,7 @@
 <li>In exacly one CPP file define following macro before this include. It enables also internal definitions.</li>
 </ol>
 <div class="fragment"><div class="line"><span class="preprocessor">#define VMA_IMPLEMENTATION</span></div><div class="line"><span class="preprocessor">#include &quot;vk_mem_alloc.h&quot;</span></div></div><!-- fragment --><p>It may be a good idea to create dedicated CPP file just for this purpose.</p>
+<p>Note on language: This library is written in C++, but has C-compatible interface. Thus you can include and use <a class="el" href="vk__mem__alloc_8h.html">vk_mem_alloc.h</a> in C or C++ code, but full implementation with <code>VMA_IMPLEMENTATION</code> macro must be compiled as C++, NOT as C.</p>
 <p>Please note that this library includes header <code>&lt;vulkan/vulkan.h&gt;</code>, which in turn includes <code>&lt;windows.h&gt;</code> on Windows. If you need some specific macros defined before including these headers (like <code>WIN32_LEAN_AND_MEAN</code> or <code>WINVER</code> for Windows, <code>VK_USE_PLATFORM_WIN32_KHR</code> for Vulkan), you must define them before every <code>#include</code> of this library.</p>
 <h1><a class="anchor" id="quick_start_initialization"></a>
 Initialization</h1>
diff --git a/docs/html/search/all_10.js b/docs/html/search/all_10.js
index fea5d80..4a2fb02 100644
--- a/docs/html/search/all_10.js
+++ b/docs/html/search/all_10.js
@@ -109,6 +109,7 @@
   ['vmarecordflagbits',['VmaRecordFlagBits',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2',1,'VmaRecordFlagBits():&#160;vk_mem_alloc.h'],['../vk__mem__alloc_8h.html#ade20b626a6635ce1bf30ea53dea774e4',1,'VmaRecordFlagBits():&#160;vk_mem_alloc.h']]],
   ['vmarecordflags',['VmaRecordFlags',['../vk__mem__alloc_8h.html#af3929a1a4547c592fc0b0e55ef452828',1,'vk_mem_alloc.h']]],
   ['vmarecordsettings',['VmaRecordSettings',['../struct_vma_record_settings.html',1,'VmaRecordSettings'],['../vk__mem__alloc_8h.html#a0ab61e87ff6365f1d59915eadc37a9f0',1,'VmaRecordSettings():&#160;vk_mem_alloc.h']]],
+  ['vmaresizeallocation',['vmaResizeAllocation',['../vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696',1,'vk_mem_alloc.h']]],
   ['vmasetallocationuserdata',['vmaSetAllocationUserData',['../vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f',1,'vk_mem_alloc.h']]],
   ['vmasetcurrentframeindex',['vmaSetCurrentFrameIndex',['../vk__mem__alloc_8h.html#ade56bf8dc9f5a5eaddf5f119ed525236',1,'vk_mem_alloc.h']]],
   ['vmastatinfo',['VmaStatInfo',['../struct_vma_stat_info.html',1,'VmaStatInfo'],['../vk__mem__alloc_8h.html#a810b009a788ee8aac72a25b42ffbe31c',1,'VmaStatInfo():&#160;vk_mem_alloc.h']]],
diff --git a/docs/html/search/functions_0.js b/docs/html/search/functions_0.js
index 53a1d34..e812f3d 100644
--- a/docs/html/search/functions_0.js
+++ b/docs/html/search/functions_0.js
@@ -35,6 +35,7 @@
   ['vmainvalidateallocation',['vmaInvalidateAllocation',['../vk__mem__alloc_8h.html#a0d0eb0c1102268fa9a476d12ecbe4006',1,'vk_mem_alloc.h']]],
   ['vmamakepoolallocationslost',['vmaMakePoolAllocationsLost',['../vk__mem__alloc_8h.html#a736bd6cbda886f36c891727e73bd4024',1,'vk_mem_alloc.h']]],
   ['vmamapmemory',['vmaMapMemory',['../vk__mem__alloc_8h.html#ad5bd1243512d099706de88168992f069',1,'vk_mem_alloc.h']]],
+  ['vmaresizeallocation',['vmaResizeAllocation',['../vk__mem__alloc_8h.html#a0ff488958ca72b28e545880463cb8696',1,'vk_mem_alloc.h']]],
   ['vmasetallocationuserdata',['vmaSetAllocationUserData',['../vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f',1,'vk_mem_alloc.h']]],
   ['vmasetcurrentframeindex',['vmaSetCurrentFrameIndex',['../vk__mem__alloc_8h.html#ade56bf8dc9f5a5eaddf5f119ed525236',1,'vk_mem_alloc.h']]],
   ['vmatouchallocation',['vmaTouchAllocation',['../vk__mem__alloc_8h.html#a43d8ba9673c846f049089a5029d5c73a',1,'vk_mem_alloc.h']]],
diff --git a/docs/html/search/variables_c.html b/docs/html/search/variables_c.html
new file mode 100644
index 0000000..75709df
--- /dev/null
+++ b/docs/html/search/variables_c.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html><head><title></title>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta name="generator" content="Doxygen 1.8.14"/>
+<link rel="stylesheet" type="text/css" href="search.css"/>
+<script type="text/javascript" src="variables_c.js"></script>
+<script type="text/javascript" src="search.js"></script>
+</head>
+<body class="SRPage">
+<div id="SRIndex">
+<div class="SRStatus" id="Loading">Loading...</div>
+<div id="SRResults"></div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
+createResults();
+/* @license-end */
+--></script>
+<div class="SRStatus" id="Searching">Searching...</div>
+<div class="SRStatus" id="NoMatches">No Matches</div>
+<script type="text/javascript"><!--
+/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
+document.getElementById("Loading").style.display="none";
+document.getElementById("NoMatches").style.display="none";
+var searchResults = new SearchResults("searchResults");
+searchResults.Search();
+/* @license-end */
+--></script>
+</div>
+</body>
+</html>
diff --git a/docs/html/search/variables_c.js b/docs/html/search/variables_c.js
new file mode 100644
index 0000000..e7b9e7f
--- /dev/null
+++ b/docs/html/search/variables_c.js
@@ -0,0 +1,20 @@
+var searchData=
+[
+  ['vkallocatememory',['vkAllocateMemory',['../struct_vma_vulkan_functions.html#a2943bf99dfd784a0e8f599d987e22e6c',1,'VmaVulkanFunctions']]],
+  ['vkbindbuffermemory',['vkBindBufferMemory',['../struct_vma_vulkan_functions.html#a94fc4f3a605d9880bb3c0ba2c2fc80b2',1,'VmaVulkanFunctions']]],
+  ['vkbindimagememory',['vkBindImageMemory',['../struct_vma_vulkan_functions.html#a1338d96a128a5ade648b8d934907c637',1,'VmaVulkanFunctions']]],
+  ['vkcmdcopybuffer',['vkCmdCopyBuffer',['../struct_vma_vulkan_functions.html#ae5c0db8c89a3b82593dc16aa6a49fa3a',1,'VmaVulkanFunctions']]],
+  ['vkcreatebuffer',['vkCreateBuffer',['../struct_vma_vulkan_functions.html#ae8084315a25006271a2edfc3a447519f',1,'VmaVulkanFunctions']]],
+  ['vkcreateimage',['vkCreateImage',['../struct_vma_vulkan_functions.html#a23ebe70be515b9b5010a1d691200e325',1,'VmaVulkanFunctions']]],
+  ['vkdestroybuffer',['vkDestroyBuffer',['../struct_vma_vulkan_functions.html#a7e054606faddb07f0e8556f3ed317d45',1,'VmaVulkanFunctions']]],
+  ['vkdestroyimage',['vkDestroyImage',['../struct_vma_vulkan_functions.html#a90b898227039b1dcb3520f6e91f09ffa',1,'VmaVulkanFunctions']]],
+  ['vkflushmappedmemoryranges',['vkFlushMappedMemoryRanges',['../struct_vma_vulkan_functions.html#a33c322f4c4ad2810f8a9c97a277572f9',1,'VmaVulkanFunctions']]],
+  ['vkfreememory',['vkFreeMemory',['../struct_vma_vulkan_functions.html#a4c658701778564d62034255b5dda91b4',1,'VmaVulkanFunctions']]],
+  ['vkgetbuffermemoryrequirements',['vkGetBufferMemoryRequirements',['../struct_vma_vulkan_functions.html#a5b92901df89a4194b0d12f6071d4d143',1,'VmaVulkanFunctions']]],
+  ['vkgetimagememoryrequirements',['vkGetImageMemoryRequirements',['../struct_vma_vulkan_functions.html#a475f6f49f8debe4d10800592606d53f4',1,'VmaVulkanFunctions']]],
+  ['vkgetphysicaldevicememoryproperties',['vkGetPhysicalDeviceMemoryProperties',['../struct_vma_vulkan_functions.html#a60d25c33bba06bb8592e6875cbaa9830',1,'VmaVulkanFunctions']]],
+  ['vkgetphysicaldeviceproperties',['vkGetPhysicalDeviceProperties',['../struct_vma_vulkan_functions.html#a77b7a74082823e865dd6546623468f96',1,'VmaVulkanFunctions']]],
+  ['vkinvalidatemappedmemoryranges',['vkInvalidateMappedMemoryRanges',['../struct_vma_vulkan_functions.html#a5c1093bc32386a8060c37c9f282078a1',1,'VmaVulkanFunctions']]],
+  ['vkmapmemory',['vkMapMemory',['../struct_vma_vulkan_functions.html#ab5c1f38dea3a2cf00dc9eb4f57218c49',1,'VmaVulkanFunctions']]],
+  ['vkunmapmemory',['vkUnmapMemory',['../struct_vma_vulkan_functions.html#acc798589736f0becb317fc2196c1d8b9',1,'VmaVulkanFunctions']]]
+];
diff --git a/docs/html/struct_vma_allocator_create_info.html b/docs/html/struct_vma_allocator_create_info.html
index 25fd89f..564e562 100644
--- a/docs/html/struct_vma_allocator_create_info.html
+++ b/docs/html/struct_vma_allocator_create_info.html
@@ -216,7 +216,7 @@
 <li>If user tries to allocate more memory from that heap using this allocator, the allocation fails with <code>VK_ERROR_OUT_OF_DEVICE_MEMORY</code>.</li>
 <li>If the limit is smaller than heap size reported in <code>VkMemoryHeap::size</code>, the value of this limit will be reported instead when using <a class="el" href="vk__mem__alloc_8h.html#ab88db292a17974f911182543fda52d19">vmaGetMemoryProperties()</a>.</li>
 </ul>
-<p>Warning! Using this feature may not be equivalent to installing a GPU with smaller amount of memory, because graphics driver doesn't necessary fail new allocations with <code>VK_ERROR_OUT_OF_DEVICE_MEMORY</code> result when memory capacity is exceeded. It may return success and just silently migrate some device memory blocks to system RAM. </p>
+<p>Warning! Using this feature may not be equivalent to installing a GPU with smaller amount of memory, because graphics driver doesn't necessary fail new allocations with <code>VK_ERROR_OUT_OF_DEVICE_MEMORY</code> result when memory capacity is exceeded. It may return success and just silently migrate some device memory blocks to system RAM. This driver behavior can also be controlled using VK_AMD_memory_overallocation_behavior extension. </p>
 
 </div>
 </div>
diff --git a/src/Tests.cpp b/src/Tests.cpp
index c8445f9..3a5c78b 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -1195,6 +1195,56 @@
     }

 }

 

+static void TestInvalidAllocations()

+{

+    VkResult res;

+

+    VmaAllocationCreateInfo allocCreateInfo = {};

+    allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;

+

+    // Try to allocate 0 bytes.

+    {

+        VkMemoryRequirements memReq = {};

+        memReq.size = 0; // !!!

+        memReq.alignment = 4;

+        memReq.memoryTypeBits = UINT32_MAX;

+        VmaAllocation alloc = VK_NULL_HANDLE;

+        res = vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &alloc, nullptr);

+        TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && alloc == VK_NULL_HANDLE);

+    }

+

+    // Try to create buffer with size = 0.

+    {

+        VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };

+        bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

+        bufCreateInfo.size = 0; // !!!

+        VkBuffer buf = VK_NULL_HANDLE;

+        VmaAllocation alloc = VK_NULL_HANDLE;

+        res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);

+        TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && buf == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);

+    }

+

+    // Try to create image with one dimension = 0.

+    {

+        VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };

+        imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;

+        imageCreateInfo.format = VK_FORMAT_B8G8R8A8_UNORM;

+        imageCreateInfo.extent.width = 128;

+        imageCreateInfo.extent.height = 0; // !!!

+        imageCreateInfo.extent.depth = 1;

+        imageCreateInfo.mipLevels = 1;

+        imageCreateInfo.arrayLayers = 1;

+        imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;

+        imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;

+        imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;

+        imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;

+        VkImage image = VK_NULL_HANDLE;

+        VmaAllocation alloc = VK_NULL_HANDLE;

+        res = vmaCreateImage(g_hAllocator, &imageCreateInfo, &allocCreateInfo, &image, &alloc, nullptr);

+        TEST(res == VK_ERROR_VALIDATION_FAILED_EXT && image == VK_NULL_HANDLE && alloc == VK_NULL_HANDLE);

+    }

+}

+

 static void TestMemoryRequirements()

 {

     VkResult res;

@@ -1299,6 +1349,8 @@
     }

 

     TestUserData();

+

+    TestInvalidAllocations();

 }

 

 void TestHeapSizeLimit()

@@ -2659,6 +2711,159 @@
     vmaDestroyPool(g_hAllocator, pool);

 }

 

+static void TestResize()

+{

+    wprintf(L"Testing vmaResizeAllocation...\n");

+

+    const VkDeviceSize KILOBYTE = 1024ull;

+    const VkDeviceSize MEGABYTE = KILOBYTE * 1024;

+

+    VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };

+    bufCreateInfo.size = 2 * MEGABYTE;

+    bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

+

+    VmaAllocationCreateInfo allocCreateInfo = {};

+    allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;

+    

+    uint32_t memTypeIndex = UINT32_MAX;

+    TEST( vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &memTypeIndex) == VK_SUCCESS );

+

+    VmaPoolCreateInfo poolCreateInfo = {};

+    poolCreateInfo.flags = VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT;

+    poolCreateInfo.blockSize = 8 * MEGABYTE;

+    poolCreateInfo.minBlockCount = 1;

+    poolCreateInfo.maxBlockCount = 1;

+    poolCreateInfo.memoryTypeIndex = memTypeIndex;

+

+    VmaPool pool;

+    TEST( vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) == VK_SUCCESS );

+

+    allocCreateInfo.pool = pool;

+

+    // Fill 8 MB pool with 4 * 2 MB allocations.

+    VmaAllocation allocs[4] = {};

+

+    VkMemoryRequirements memReq = {};

+    memReq.memoryTypeBits = UINT32_MAX;

+    memReq.alignment = 4;

+    memReq.size = bufCreateInfo.size;

+

+    VmaAllocationInfo allocInfo = {};

+

+    for(uint32_t i = 0; i < 4; ++i)

+    {

+        TEST( vmaAllocateMemory(g_hAllocator, &memReq, &allocCreateInfo, &allocs[i], nullptr) == VK_SUCCESS );

+    }

+

+    // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 2MB

+

+    // Case: Resize to the same size always succeeds.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS);

+        vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);

+        TEST(allocInfo.size == 2ull * 1024 * 1024);

+    }

+

+    // Case: Shrink allocation at the end.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS );

+        vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);

+        TEST(allocInfo.size == 1ull * 1024 * 1024);

+    }

+    

+    // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB

+

+    // Case: Shrink allocation before free space.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 512 * KILOBYTE) == VK_SUCCESS );

+        vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);

+        TEST(allocInfo.size == 512 * KILOBYTE);

+    }

+

+    // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB

+

+    // Case: Shrink allocation before next allocation.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 1 * MEGABYTE) == VK_SUCCESS );

+        vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);

+        TEST(allocInfo.size == 1 * MEGABYTE);

+    }

+

+    // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 0.5MB, free 1.5MB

+

+    // Case: Grow allocation while there is even more space available.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 1 * MEGABYTE) == VK_SUCCESS );

+        vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);

+        TEST(allocInfo.size == 1 * MEGABYTE);

+    }

+

+    // Now it's: a0 1MB, free 1 MB, a1 2MB, a2 2MB, a3 1MB, free 1MB

+

+    // Case: Grow allocation while there is exact amount of free space available.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 2 * MEGABYTE) == VK_SUCCESS );

+        vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);

+        TEST(allocInfo.size == 2 * MEGABYTE);

+    }

+

+    // Now it's: a0 2MB, a1 2MB, a2 2MB, a3 1MB, free 1MB

+

+    // Case: Fail to grow when there is not enough free space due to next allocation.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[0], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY );

+        vmaGetAllocationInfo(g_hAllocator, allocs[0], &allocInfo);

+        TEST(allocInfo.size == 2 * MEGABYTE);

+    }

+

+    // Case: Fail to grow when there is not enough free space due to end of memory block.

+    {

+        TEST( vmaResizeAllocation(g_hAllocator, allocs[3], 3 * MEGABYTE) == VK_ERROR_OUT_OF_POOL_MEMORY );

+        vmaGetAllocationInfo(g_hAllocator, allocs[3], &allocInfo);

+        TEST(allocInfo.size == 1 * MEGABYTE);

+    }

+

+    for(uint32_t i = 4; i--; )

+    {

+        vmaFreeMemory(g_hAllocator, allocs[i]);

+    }

+

+    vmaDestroyPool(g_hAllocator, pool);

+

+    // Test dedicated allocation

+    {

+        VmaAllocationCreateInfo dedicatedAllocCreateInfo = {};

+        dedicatedAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;

+        dedicatedAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;

+

+        VmaAllocation dedicatedAlloc = VK_NULL_HANDLE;

+        TEST( vmaAllocateMemory(g_hAllocator, &memReq, &dedicatedAllocCreateInfo, &dedicatedAlloc, nullptr) == VK_SUCCESS );

+

+        // Case: Resize to the same size always succeeds.

+        {

+            TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 2 * MEGABYTE) == VK_SUCCESS);

+            vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);

+            TEST(allocInfo.size == 2ull * 1024 * 1024);

+        }

+

+        // Case: Shrinking fails.

+        {

+            TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 1 * MEGABYTE) < VK_SUCCESS);

+            vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);

+            TEST(allocInfo.size == 2ull * 1024 * 1024);

+        }

+

+        // Case: Growing fails.

+        {

+            TEST( vmaResizeAllocation(g_hAllocator, dedicatedAlloc, 3 * MEGABYTE) < VK_SUCCESS);

+            vmaGetAllocationInfo(g_hAllocator, dedicatedAlloc, &allocInfo);

+            TEST(allocInfo.size == 2ull * 1024 * 1024);

+        }

+

+        vmaFreeMemory(g_hAllocator, dedicatedAlloc);

+    }

+}

+

 static bool ValidatePattern(const void* pMemory, size_t size, uint8_t pattern)

 {

     const uint8_t* pBytes = (const uint8_t*)pMemory;

@@ -4320,7 +4525,7 @@
         // ########################################

         // ########################################

         

-        BasicTestBuddyAllocator();

+        TestResize();

         return;

     }

 

@@ -4332,6 +4537,7 @@
 #else

     TestPool_SameSize();

     TestHeapSizeLimit();

+    TestResize();

 #endif

 #if VMA_DEBUG_INITIALIZE_ALLOCATIONS

     TestAllocationsInitialization();

diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp
index c324245..515c1ce 100644
--- a/src/VmaReplay/Common.cpp
+++ b/src/VmaReplay/Common.cpp
@@ -12,6 +12,11 @@
         while(currLineEnd < m_NumBytes && m_Data[currLineEnd] != '\n')

             ++currLineEnd;

         out.end = m_Data + currLineEnd;

+        // Ignore trailing '\r' to support Windows end of line.

+        if(out.end > out.beg && *(out.end - 1) == '\r')

+        {

+            --out.end;

+        }

         m_NextLineBeg = currLineEnd + 1; // Past '\n'

         ++m_NextLineIndex;

         return true;

diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp
index 9635b03..a9acfa5 100644
--- a/src/VmaReplay/VmaReplay.cpp
+++ b/src/VmaReplay/VmaReplay.cpp
@@ -82,6 +82,7 @@
     TouchAllocation,

     GetAllocationInfo,

     MakePoolAllocationsLost,

+    ResizeAllocation,

     Count

 };

 static const char* VMA_FUNCTION_NAMES[] = {

@@ -104,6 +105,7 @@
     "vmaTouchAllocation",

     "vmaGetAllocationInfo",

     "vmaMakePoolAllocationsLost",

+    "vmaResizeAllocation",

 };

 static_assert(

     _countof(VMA_FUNCTION_NAMES) == (size_t)VMA_FUNCTION::Count,

@@ -143,7 +145,7 @@
 static bool ValidateFileVersion()

 {

     if(GetVersionMajor(g_FileVersion) == 1 &&

-        GetVersionMinor(g_FileVersion) <= 3)

+        GetVersionMinor(g_FileVersion) <= 4)

     {

         return true;

     }

@@ -1015,6 +1017,7 @@
     void ExecuteTouchAllocation(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteGetAllocationInfo(size_t lineNumber, const CsvSplit& csvSplit);

     void ExecuteMakePoolAllocationsLost(size_t lineNumber, const CsvSplit& csvSplit);

+    void ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit);

 

     void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit);

 };

@@ -1156,6 +1159,8 @@
             ExecuteGetAllocationInfo(lineNumber, csvSplit);

         else if(StrRangeEq(functionName, "vmaMakePoolAllocationsLost"))

             ExecuteMakePoolAllocationsLost(lineNumber, csvSplit);

+        else if(StrRangeEq(functionName, "vmaResizeAllocation"))

+            ExecuteResizeAllocation(lineNumber, csvSplit);

         else

         {

             if(IssueWarning())

@@ -2599,6 +2604,45 @@
     }

 }

 

+void Player::ExecuteResizeAllocation(size_t lineNumber, const CsvSplit& csvSplit)

+{

+    m_Stats.RegisterFunctionCall(VMA_FUNCTION::ResizeAllocation);

+

+    if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, false))

+    {

+        uint64_t origPtr = 0;

+        uint64_t newSize = 0;

+

+        if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) &&

+            StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), newSize))

+        {

+            if(origPtr != 0)

+            {

+                const auto it = m_Allocations.find(origPtr);

+                if(it != m_Allocations.end())

+                {

+                    vmaResizeAllocation(m_Allocator, it->second.allocation, newSize);

+                    UpdateMemStats();

+                }

+                else

+                {

+                    if(IssueWarning())

+                    {

+                        printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr);

+                    }

+                }

+            }

+        }

+        else

+        {

+            if(IssueWarning())

+            {

+                printf("Line %zu: Invalid parameters for vmaResizeAllocation.\n", lineNumber);

+            }

+        }

+    }

+}

+

 ////////////////////////////////////////////////////////////////////////////////

 // Main functions

 

diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp
index 7c78469..0da5be2 100644
--- a/src/VulkanSample.cpp
+++ b/src/VulkanSample.cpp
@@ -1345,6 +1345,15 @@
         allocatorInfo.pAllocationCallbacks = &cpuAllocationCallbacks;

     }

 

+    // Uncomment to enable recording to CSV file.

+    /*

+    {

+        VmaRecordSettings recordSettings = {};

+        recordSettings.pFilePath = "VulkanSample.csv";

+        allocatorInfo.pRecordSettings = &recordSettings;

+    }

+    */

+

     ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) );

 

     // Retrieve queues (don't need to be destroyed).

diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 119a63e..eebeb0f 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -124,6 +124,10 @@
 

 It may be a good idea to create dedicated CPP file just for this purpose.

 

+Note on language: This library is written in C++, but has C-compatible interface.

+Thus you can include and use vk_mem_alloc.h in C or C++ code, but full

+implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.

+

 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn

 includes `<windows.h>` on Windows. If you need some specific macros defined

 before including these headers (like `WIN32_LEAN_AND_MEAN` or

@@ -387,7 +391,9 @@
   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. 

+  being submitted.

+- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)

+  which requires unmapping before GPU can see updated texture.

 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.

 

 \section memory_mapping_cache_control Cache control

@@ -1444,7 +1450,8 @@
 

 - Support for sparse binding and sparse residency. You can still use these

   features (when supported by the device) with VMA. You just need to do it

-  yourself. Any explicit support for sparse binding/residency would rather

+  yourself. Allocate memory pages with vmaAllocateMemory().

+  Any explicit support for sparse binding/residency would rather

   require another, higher-level library on top of VMA.

 - Data transfer - issuing commands that transfer data between buffers or images, any usage of

   `VkCommandList` or `VkQueue` and related synchronization is responsibility of the user.

@@ -1452,6 +1459,18 @@
   explicit memory type index and dedicated allocation anyway, so they don't

   interact with main features of this library. Such special purpose allocations

   should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.

+- 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's 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.

+- 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.

 - Support for any programming languages other than C/C++.

   Bindings to other languages are welcomed as external projects.

 

@@ -1669,7 +1688,8 @@
     smaller amount of memory, because graphics driver doesn't necessary fail new

     allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is

     exceeded. It may return success and just silently migrate some device memory

-    blocks to system RAM.

+    blocks to system RAM. This driver behavior can also be controlled using

+    VK_AMD_memory_overallocation_behavior extension.

     */

     const VkDeviceSize* pHeapSizeLimit;

     /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.

@@ -1932,6 +1952,9 @@
     VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,

     /** Allocation strategy that chooses first suitable free range for the

     allocation.

+

+    "First" doesn't necessarily means the one with smallest offset in memory,

+    but rather the one that is easiest and fastest to find.

     */

     VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,

 

@@ -2402,6 +2425,31 @@
     size_t allocationCount,

     VmaAllocation* pAllocations);

 

+/** \brief Tries to resize an allocation in place, if there is enough free memory after it.

+

+Tries to change allocation's size without moving or reallocating it.

+You can both shrink and grow allocation size.

+When growing, it succeeds only when the allocation belongs to a memory block with enough

+free space after it.

+

+Returns `VK_SUCCESS` if allocation's size has been successfully changed.

+Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.

+

+After successful call to this function, VmaAllocationInfo::size of this allocation changes.

+All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.

+

+- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.

+- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.

+- Resizing dedicated allocations, as well as allocations created in pools that use linear

+  or buddy algorithm, is not supported.

+  The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.

+  Support may be added in the future.

+*/

+VkResult vmaResizeAllocation(

+    VmaAllocator allocator,

+    VmaAllocation allocation,

+    VkDeviceSize newSize);

+

 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.

 

 Current paramters of given allocation are returned in `pAllocationInfo`.

@@ -2826,7 +2874,19 @@
    #define VMA_NULL   nullptr

 #endif

 

-#if defined(__APPLE__) || defined(__ANDROID__)

+#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)

+#include <cstdlib>

+void *aligned_alloc(size_t alignment, size_t size)

+{

+    // alignment must be >= sizeof(void*)

+    if(alignment < sizeof(void*))

+    {

+        alignment = sizeof(void*);

+    }

+

+    return memalign(alignment, size);

+}

+#elif defined(__APPLE__) || defined(__ANDROID__)

 #include <cstdlib>

 void *aligned_alloc(size_t alignment, size_t size)

 {

@@ -4520,7 +4580,9 @@
     void ChangeBlockAllocation(

         VmaAllocator hAllocator,

         VmaDeviceMemoryBlock* block,

-        VkDeviceSize offset);

+        VkDeviceSize offset); 

+

+    void ChangeSize(VkDeviceSize newSize);

 

     // pMappedData not null means allocation is created with MAPPED flag.

     void InitDedicatedAllocation(

@@ -4782,6 +4844,9 @@
     virtual void Free(const VmaAllocation allocation) = 0;

     virtual void FreeAtOffset(VkDeviceSize offset) = 0;

 

+    // Tries to resize (grow or shrink) space for given allocation, in place.

+    virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }

+

 protected:

     const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }

 

@@ -4861,6 +4926,8 @@
     virtual void Free(const VmaAllocation allocation);

     virtual void FreeAtOffset(VkDeviceSize offset);

 

+    virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);

+

 private:

     uint32_t m_FreeCount;

     VkDeviceSize m_SumFreeSize;

@@ -5623,6 +5690,10 @@
         VmaAllocation allocation);

     void RecordFreeMemory(uint32_t frameIndex,

         VmaAllocation allocation);

+    void RecordResizeAllocation(

+        uint32_t frameIndex,

+        VmaAllocation allocation,

+        VkDeviceSize newSize);

     void RecordSetAllocationUserData(uint32_t frameIndex,

         VmaAllocation allocation,

         const void* pUserData);

@@ -5792,6 +5863,10 @@
         size_t allocationCount,

         const VmaAllocation* pAllocations);

 

+    VkResult ResizeAllocation(

+        const VmaAllocation alloc,

+        VkDeviceSize newSize);

+

     void CalculateStats(VmaStats* pStats);

 

 #if VMA_STATS_STRING_ENABLED

@@ -6338,6 +6413,12 @@
     m_BlockAllocation.m_Offset = offset;

 }

 

+void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)

+{

+    VMA_ASSERT(newSize > 0);

+    m_Size = newSize;

+}

+

 VkDeviceSize VmaAllocation_T::GetOffset() const

 {

     switch(m_Type)

@@ -7264,6 +7345,133 @@
     VMA_ASSERT(0 && "Not found!");

 }

 

+bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)

+{

+    typedef VmaSuballocationList::iterator iter_type;

+    for(iter_type suballocItem = m_Suballocations.begin();

+        suballocItem != m_Suballocations.end();

+        ++suballocItem)

+    {

+        VmaSuballocation& suballoc = *suballocItem;

+        if(suballoc.hAllocation == alloc)

+        {

+            iter_type nextItem = suballocItem;

+            ++nextItem;

+

+            // Should have been ensured on higher level.

+            VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);

+

+            // Shrinking.

+            if(newSize < alloc->GetSize())

+            {

+                const VkDeviceSize sizeDiff = suballoc.size - newSize;

+

+                // There is next item.

+                if(nextItem != m_Suballocations.end())

+                {

+                    // Next item is free.

+                    if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)

+                    {

+                        // Grow this next item backward.

+                        UnregisterFreeSuballocation(nextItem);

+                        nextItem->offset -= sizeDiff;

+                        nextItem->size += sizeDiff;

+                        RegisterFreeSuballocation(nextItem);

+                    }

+                    // Next item is not free.

+                    else

+                    {

+                        // Create free item after current one.

+                        VmaSuballocation newFreeSuballoc;

+                        newFreeSuballoc.hAllocation = VK_NULL_HANDLE;

+                        newFreeSuballoc.offset = suballoc.offset + newSize;

+                        newFreeSuballoc.size = sizeDiff;

+                        newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

+                        iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);

+                        RegisterFreeSuballocation(newFreeSuballocIt);

+

+                        ++m_FreeCount;

+                    }

+                }

+                // This is the last item.

+                else

+                {

+                    // Create free item at the end.

+                    VmaSuballocation newFreeSuballoc;

+                    newFreeSuballoc.hAllocation = VK_NULL_HANDLE;

+                    newFreeSuballoc.offset = suballoc.offset + newSize;

+                    newFreeSuballoc.size = sizeDiff;

+                    newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;

+                    m_Suballocations.push_back(newFreeSuballoc);

+

+                    iter_type newFreeSuballocIt = m_Suballocations.end();

+                    RegisterFreeSuballocation(--newFreeSuballocIt);

+

+                    ++m_FreeCount;

+                }

+

+                suballoc.size = newSize;

+                m_SumFreeSize += sizeDiff;

+            }

+            // Growing.

+            else

+            {

+                const VkDeviceSize sizeDiff = newSize - suballoc.size;

+

+                // There is next item.

+                if(nextItem != m_Suballocations.end())

+                {

+                    // Next item is free.

+                    if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)

+                    {

+                        // There is not enough free space, including margin.

+                        if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)

+                        {

+                            return false;

+                        }

+

+                        // There is more free space than required.

+                        if(nextItem->size > sizeDiff)

+                        {

+                            // Move and shrink this next item.

+                            UnregisterFreeSuballocation(nextItem);

+                            nextItem->offset += sizeDiff;

+                            nextItem->size -= sizeDiff;

+                            RegisterFreeSuballocation(nextItem);

+                        }

+                        // There is exactly the amount of free space required.

+                        else

+                        {

+                            // Remove this next free item.

+                            UnregisterFreeSuballocation(nextItem);

+                            m_Suballocations.erase(nextItem);

+                            --m_FreeCount;

+                        }

+                    }

+                    // Next item is not free - there is no space to grow.

+                    else

+                    {

+                        return false;

+                    }

+                }

+                // This is the last item - there is no space to grow.

+                else

+                {

+                    return false;

+                }

+

+                suballoc.size = newSize;

+                m_SumFreeSize -= sizeDiff;

+            }

+

+            // We cannot call Validate() here because alloc object is updated to new size outside of this call.

+            return true;

+        }

+    }

+    VMA_ASSERT(0 && "Not found!");

+    return false;

+}

+

 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const

 {

     VkDeviceSize lastSize = 0;

@@ -11453,7 +11661,7 @@
 

     // Write header.

     fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");

-    fprintf(m_File, "%s\n", "1,3");

+    fprintf(m_File, "%s\n", "1,4");

 

     return VK_SUCCESS;

 }

@@ -11609,6 +11817,20 @@
     Flush();

 }

 

+void VmaRecorder::RecordResizeAllocation(

+    uint32_t frameIndex,

+    VmaAllocation allocation,

+    VkDeviceSize newSize)

+{

+    CallParams callParams;

+    GetBasicParams(callParams);

+

+    VmaMutexLock lock(m_FileMutex, m_UseMutex);

+    fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,

+        allocation, newSize);

+    Flush();

+}

+

 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,

     VmaAllocation allocation,

     const void* pUserData)

@@ -12478,6 +12700,10 @@
 

     VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));

 

+    if(vkMemReq.size == 0)

+    {

+        return VK_ERROR_VALIDATION_FAILED_EXT;

+    }

     if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&

         (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)

     {

@@ -12652,6 +12878,40 @@
     }

 }

 

+VkResult VmaAllocator_T::ResizeAllocation(

+    const VmaAllocation alloc,

+    VkDeviceSize newSize)

+{

+    if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)

+    {

+        return VK_ERROR_VALIDATION_FAILED_EXT;

+    }

+    if(newSize == alloc->GetSize())

+    {

+        return VK_SUCCESS;

+    }

+

+    switch(alloc->GetType())

+    {

+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:

+        return VK_ERROR_FEATURE_NOT_PRESENT;

+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:

+        if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))

+        {

+            alloc->ChangeSize(newSize);

+            VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());

+            return VK_SUCCESS;

+        }

+        else

+        {

+            return VK_ERROR_OUT_OF_POOL_MEMORY;

+        }

+    default:

+        VMA_ASSERT(0);

+        return VK_ERROR_VALIDATION_FAILED_EXT;

+    }

+}

+

 void VmaAllocator_T::CalculateStats(VmaStats* pStats)

 {

     // Initialize.

@@ -12714,7 +12974,7 @@
 {

     if(pAllocationsChanged != VMA_NULL)

     {

-        memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged));

+        memset(pAllocationsChanged, 0, allocationCount * sizeof(VkBool32));

     }

     if(pDefragmentationStats != VMA_NULL)

     {

@@ -14145,6 +14405,30 @@
     allocator->FreeMemory(allocationCount, pAllocations);

 }

 

+VkResult vmaResizeAllocation(

+    VmaAllocator allocator,

+    VmaAllocation allocation,

+    VkDeviceSize newSize)

+{

+    VMA_ASSERT(allocator && allocation);

+    

+    VMA_DEBUG_LOG("vmaResizeAllocation");

+    

+    VMA_DEBUG_GLOBAL_MUTEX_LOCK

+

+#if VMA_RECORDING_ENABLED

+    if(allocator->GetRecorder() != VMA_NULL)

+    {

+        allocator->GetRecorder()->RecordResizeAllocation(

+            allocator->GetCurrentFrameIndex(),

+            allocation,

+            newSize);

+    }

+#endif

+    

+    return allocator->ResizeAllocation(allocation, newSize);

+}

+

 void vmaGetAllocationInfo(

     VmaAllocator allocator,

     VmaAllocation allocation,

@@ -14376,6 +14660,11 @@
     VmaAllocationInfo* pAllocationInfo)

 {

     VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);

+

+    if(pBufferCreateInfo->size == 0)

+    {

+        return VK_ERROR_VALIDATION_FAILED_EXT;

+    }

     

     VMA_DEBUG_LOG("vmaCreateBuffer");

     

@@ -14520,6 +14809,15 @@
 {

     VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);

 

+    if(pImageCreateInfo->extent.width == 0 ||

+        pImageCreateInfo->extent.height == 0 ||

+        pImageCreateInfo->extent.depth == 0 ||

+        pImageCreateInfo->mipLevels == 0 ||

+        pImageCreateInfo->arrayLayers == 0)

+    {

+        return VK_ERROR_VALIDATION_FAILED_EXT;

+    }

+

     VMA_DEBUG_LOG("vmaCreateImage");

 

     VMA_DEBUG_GLOBAL_MUTEX_LOCK

diff --git a/tools/VmaDumpVis/VmaDumpVis.py b/tools/VmaDumpVis/VmaDumpVis.py
index a0d42ca..c1477c3 100644
--- a/tools/VmaDumpVis/VmaDumpVis.py
+++ b/tools/VmaDumpVis/VmaDumpVis.py
@@ -25,7 +25,7 @@
 from PIL import Image, ImageDraw, ImageFont

 

 

-PROGRAM_VERSION = 'VMA Dump Visualization 1.0.0'

+PROGRAM_VERSION = 'VMA Dump Visualization 2.0.0'

 IMG_SIZE_X = 800

 IMG_MARGIN = 8

 FONT_SIZE = 10

@@ -246,7 +246,7 @@
     index = 0

     for iPoolId, listPool in dictMemType['CustomPools'].items():

         for objBlock in listPool:

-            if 'Algorithm' in objBlock:

+            if 'Algorithm' in objBlock and objBlock['Algorithm']:

                 sAlgorithm = ' (Algorithm: %s)' % (objBlock['Algorithm']);

             else:

                 sAlgorithm = '';