Vulkan Memory Allocator
vk_mem_alloc.h
Go to the documentation of this file.
1 //
2 // Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25 
2003 #if VMA_RECORDING_ENABLED
2004  #include <chrono>
2005  #if defined(_WIN32)
2006  #include <windows.h>
2007  #else
2008  #include <sstream>
2009  #include <thread>
2010  #endif
2011 #endif
2012 
2013 #ifdef __cplusplus
2014 extern "C" {
2015 #endif
2016 
2017 /*
2018 Define this macro to 0/1 to disable/enable support for recording functionality,
2019 available through VmaAllocatorCreateInfo::pRecordSettings.
2020 */
2021 #ifndef VMA_RECORDING_ENABLED
2022  #define VMA_RECORDING_ENABLED 0
2023 #endif
2024 
2025 #ifndef NOMINMAX
2026  #define NOMINMAX // For windows.h
2027 #endif
2028 
2029 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
2030  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
2031  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
2032  extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
2033  extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
2034  extern PFN_vkAllocateMemory vkAllocateMemory;
2035  extern PFN_vkFreeMemory vkFreeMemory;
2036  extern PFN_vkMapMemory vkMapMemory;
2037  extern PFN_vkUnmapMemory vkUnmapMemory;
2038  extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
2039  extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
2040  extern PFN_vkBindBufferMemory vkBindBufferMemory;
2041  extern PFN_vkBindImageMemory vkBindImageMemory;
2042  extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
2043  extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
2044  extern PFN_vkCreateBuffer vkCreateBuffer;
2045  extern PFN_vkDestroyBuffer vkDestroyBuffer;
2046  extern PFN_vkCreateImage vkCreateImage;
2047  extern PFN_vkDestroyImage vkDestroyImage;
2048  extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
2049  #if VMA_VULKAN_VERSION >= 1001000
2050  extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
2051  extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
2052  extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
2053  extern PFN_vkBindImageMemory2 vkBindImageMemory2;
2054  extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
2055  #endif // #if VMA_VULKAN_VERSION >= 1001000
2056 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
2057 
2058 #ifndef VULKAN_H_
2059  #include <vulkan/vulkan.h>
2060 #endif
2061 
2062 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
2063 // where AAA = major, BBB = minor, CCC = patch.
2064 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
2065 #if !defined(VMA_VULKAN_VERSION)
2066  #if defined(VK_VERSION_1_2)
2067  #define VMA_VULKAN_VERSION 1002000
2068  #elif defined(VK_VERSION_1_1)
2069  #define VMA_VULKAN_VERSION 1001000
2070  #else
2071  #define VMA_VULKAN_VERSION 1000000
2072  #endif
2073 #endif
2074 
2075 #if !defined(VMA_DEDICATED_ALLOCATION)
2076  #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
2077  #define VMA_DEDICATED_ALLOCATION 1
2078  #else
2079  #define VMA_DEDICATED_ALLOCATION 0
2080  #endif
2081 #endif
2082 
2083 #if !defined(VMA_BIND_MEMORY2)
2084  #if VK_KHR_bind_memory2
2085  #define VMA_BIND_MEMORY2 1
2086  #else
2087  #define VMA_BIND_MEMORY2 0
2088  #endif
2089 #endif
2090 
2091 #if !defined(VMA_MEMORY_BUDGET)
2092  #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
2093  #define VMA_MEMORY_BUDGET 1
2094  #else
2095  #define VMA_MEMORY_BUDGET 0
2096  #endif
2097 #endif
2098 
2099 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
2100 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
2101  #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
2102  #define VMA_BUFFER_DEVICE_ADDRESS 1
2103  #else
2104  #define VMA_BUFFER_DEVICE_ADDRESS 0
2105  #endif
2106 #endif
2107 
2108 // Define these macros to decorate all public functions with additional code,
2109 // before and after returned type, appropriately. This may be useful for
2110 // exporing the functions when compiling VMA as a separate library. Example:
2111 // #define VMA_CALL_PRE __declspec(dllexport)
2112 // #define VMA_CALL_POST __cdecl
2113 #ifndef VMA_CALL_PRE
2114  #define VMA_CALL_PRE
2115 #endif
2116 #ifndef VMA_CALL_POST
2117  #define VMA_CALL_POST
2118 #endif
2119 
2120 // Define this macro to decorate pointers with an attribute specifying the
2121 // length of the array they point to if they are not null.
2122 //
2123 // The length may be one of
2124 // - The name of another parameter in the argument list where the pointer is declared
2125 // - The name of another member in the struct where the pointer is declared
2126 // - The name of a member of a struct type, meaning the value of that member in
2127 // the context of the call. For example
2128 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2129 // this means the number of memory heaps available in the device associated
2130 // with the VmaAllocator being dealt with.
2131 #ifndef VMA_LEN_IF_NOT_NULL
2132  #define VMA_LEN_IF_NOT_NULL(len)
2133 #endif
2134 
2135 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2136 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2137 #ifndef VMA_NULLABLE
2138  #ifdef __clang__
2139  #define VMA_NULLABLE _Nullable
2140  #else
2141  #define VMA_NULLABLE
2142  #endif
2143 #endif
2144 
2145 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2146 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2147 #ifndef VMA_NOT_NULL
2148  #ifdef __clang__
2149  #define VMA_NOT_NULL _Nonnull
2150  #else
2151  #define VMA_NOT_NULL
2152  #endif
2153 #endif
2154 
2155 // If non-dispatchable handles are represented as pointers then we can give
2156 // then nullability annotations
2157 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2158  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2159  #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2160  #else
2161  #define VMA_NOT_NULL_NON_DISPATCHABLE
2162  #endif
2163 #endif
2164 
2165 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2166  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2167  #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2168  #else
2169  #define VMA_NULLABLE_NON_DISPATCHABLE
2170  #endif
2171 #endif
2172 
2182 VK_DEFINE_HANDLE(VmaAllocator)
2183 
2184 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2186  VmaAllocator VMA_NOT_NULL allocator,
2187  uint32_t memoryType,
2188  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2189  VkDeviceSize size,
2190  void* VMA_NULLABLE pUserData);
2192 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2193  VmaAllocator VMA_NOT_NULL allocator,
2194  uint32_t memoryType,
2195  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2196  VkDeviceSize size,
2197  void* VMA_NULLABLE pUserData);
2198 
2212  void* VMA_NULLABLE pUserData;
2214 
2310 
2313 typedef VkFlags VmaAllocatorCreateFlags;
2314 
2319 typedef struct VmaVulkanFunctions {
2320  PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2321  PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2322  PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2323  PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2324  PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2325  PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2326  PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2327  PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2328  PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2329  PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2330  PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2331  PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2332  PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2333  PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2334  PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2335  PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2336  PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2337 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2338  PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2339  PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2340 #endif
2341 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2342  PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2343  PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2344 #endif
2345 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2346  PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2347 #endif
2349 
2351 typedef enum VmaRecordFlagBits {
2358 
2359  VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2361 typedef VkFlags VmaRecordFlags;
2362 
2364 typedef struct VmaRecordSettings
2365 {
2375  const char* VMA_NOT_NULL pFilePath;
2377 
2380 {
2384 
2385  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2387 
2388  VkDevice VMA_NOT_NULL device;
2390 
2393 
2394  const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2396 
2436  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2437 
2449  const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2454  VkInstance VMA_NOT_NULL instance;
2465 
2467 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2468  const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2469  VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2470 
2472 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2473  VmaAllocator VMA_NULLABLE allocator);
2474 
2477 typedef struct VmaAllocatorInfo
2478 {
2483  VkInstance VMA_NOT_NULL instance;
2488  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2493  VkDevice VMA_NOT_NULL device;
2495 
2501 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2502 
2507 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2508  VmaAllocator VMA_NOT_NULL allocator,
2509  const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2510 
2515 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2516  VmaAllocator VMA_NOT_NULL allocator,
2517  const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2518 
2525 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2526  VmaAllocator VMA_NOT_NULL allocator,
2527  uint32_t memoryTypeIndex,
2528  VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2529 
2538 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2539  VmaAllocator VMA_NOT_NULL allocator,
2540  uint32_t frameIndex);
2541 
2544 typedef struct VmaStatInfo
2545 {
2547  uint32_t blockCount;
2553  VkDeviceSize usedBytes;
2555  VkDeviceSize unusedBytes;
2556  VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2557  VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2559 
2561 typedef struct VmaStats
2562 {
2563  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2564  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2567 
2577 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2578  VmaAllocator VMA_NOT_NULL allocator,
2579  VmaStats* VMA_NOT_NULL pStats);
2580 
2583 typedef struct VmaBudget
2584 {
2587  VkDeviceSize blockBytes;
2588 
2598  VkDeviceSize allocationBytes;
2599 
2608  VkDeviceSize usage;
2609 
2619  VkDeviceSize budget;
2621 
2632 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2633  VmaAllocator VMA_NOT_NULL allocator,
2634  VmaBudget* VMA_NOT_NULL pBudget);
2635 
2636 #ifndef VMA_STATS_STRING_ENABLED
2637 #define VMA_STATS_STRING_ENABLED 1
2638 #endif
2639 
2640 #if VMA_STATS_STRING_ENABLED
2641 
2643 
2645 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2646  VmaAllocator VMA_NOT_NULL allocator,
2647  char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2648  VkBool32 detailedMap);
2649 
2650 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2651  VmaAllocator VMA_NOT_NULL allocator,
2652  char* VMA_NULLABLE pStatsString);
2653 
2654 #endif // #if VMA_STATS_STRING_ENABLED
2655 
2664 VK_DEFINE_HANDLE(VmaPool)
2665 
2666 typedef enum VmaMemoryUsage
2667 {
2729 
2730  VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2732 
2742 
2807 
2823 
2833 
2840 
2844 
2846 {
2859  VkMemoryPropertyFlags requiredFlags;
2864  VkMemoryPropertyFlags preferredFlags;
2872  uint32_t memoryTypeBits;
2878  VmaPool VMA_NULLABLE pool;
2885  void* VMA_NULLABLE pUserData;
2887 
2904 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2905  VmaAllocator VMA_NOT_NULL allocator,
2906  uint32_t memoryTypeBits,
2907  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2908  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2909 
2922 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2923  VmaAllocator VMA_NOT_NULL allocator,
2924  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2925  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2926  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2927 
2940 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2941  VmaAllocator VMA_NOT_NULL allocator,
2942  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2943  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2944  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2945 
2966 
2983 
2994 
3000 
3003 typedef VkFlags VmaPoolCreateFlags;
3004 
3007 typedef struct VmaPoolCreateInfo {
3022  VkDeviceSize blockSize;
3051 
3054 typedef struct VmaPoolStats {
3057  VkDeviceSize size;
3060  VkDeviceSize unusedSize;
3073  VkDeviceSize unusedRangeSizeMax;
3076  size_t blockCount;
3078 
3085 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
3086  VmaAllocator VMA_NOT_NULL allocator,
3087  const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
3088  VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
3089 
3092 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
3093  VmaAllocator VMA_NOT_NULL allocator,
3094  VmaPool VMA_NULLABLE pool);
3095 
3102 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3103  VmaAllocator VMA_NOT_NULL allocator,
3104  VmaPool VMA_NOT_NULL pool,
3105  VmaPoolStats* VMA_NOT_NULL pPoolStats);
3106 
3113 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3114  VmaAllocator VMA_NOT_NULL allocator,
3115  VmaPool VMA_NOT_NULL pool,
3116  size_t* VMA_NULLABLE pLostAllocationCount);
3117 
3132 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3133 
3140 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3141  VmaAllocator VMA_NOT_NULL allocator,
3142  VmaPool VMA_NOT_NULL pool,
3143  const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3144 
3150 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3151  VmaAllocator VMA_NOT_NULL allocator,
3152  VmaPool VMA_NOT_NULL pool,
3153  const char* VMA_NULLABLE pName);
3154 
3179 VK_DEFINE_HANDLE(VmaAllocation)
3180 
3181 
3183 typedef struct VmaAllocationInfo {
3188  uint32_t memoryType;
3197  VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3202  VkDeviceSize offset;
3213  VkDeviceSize size;
3222  void* VMA_NULLABLE pMappedData;
3227  void* VMA_NULLABLE pUserData;
3229 
3240 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3241  VmaAllocator VMA_NOT_NULL allocator,
3242  const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3243  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3244  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3245  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3246 
3266 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3267  VmaAllocator VMA_NOT_NULL allocator,
3268  const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3269  const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3270  size_t allocationCount,
3271  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3272  VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3273 
3280 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3281  VmaAllocator VMA_NOT_NULL allocator,
3282  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3283  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3284  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3285  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3286 
3288 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3289  VmaAllocator VMA_NOT_NULL allocator,
3290  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3291  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3292  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3293  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3294 
3299 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3300  VmaAllocator VMA_NOT_NULL allocator,
3301  const VmaAllocation VMA_NULLABLE allocation);
3302 
3313 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3314  VmaAllocator VMA_NOT_NULL allocator,
3315  size_t allocationCount,
3316  const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3317 
3325 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3326  VmaAllocator VMA_NOT_NULL allocator,
3327  VmaAllocation VMA_NOT_NULL allocation,
3328  VkDeviceSize newSize);
3329 
3346 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3347  VmaAllocator VMA_NOT_NULL allocator,
3348  VmaAllocation VMA_NOT_NULL allocation,
3349  VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3350 
3365 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3366  VmaAllocator VMA_NOT_NULL allocator,
3367  VmaAllocation VMA_NOT_NULL allocation);
3368 
3382 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3383  VmaAllocator VMA_NOT_NULL allocator,
3384  VmaAllocation VMA_NOT_NULL allocation,
3385  void* VMA_NULLABLE pUserData);
3386 
3397 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3398  VmaAllocator VMA_NOT_NULL allocator,
3399  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3400 
3439 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3440  VmaAllocator VMA_NOT_NULL allocator,
3441  VmaAllocation VMA_NOT_NULL allocation,
3442  void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3443 
3452 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3453  VmaAllocator VMA_NOT_NULL allocator,
3454  VmaAllocation VMA_NOT_NULL allocation);
3455 
3477 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3478  VmaAllocator VMA_NOT_NULL allocator,
3479  VmaAllocation VMA_NOT_NULL allocation,
3480  VkDeviceSize offset,
3481  VkDeviceSize size);
3482 
3504 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3505  VmaAllocator VMA_NOT_NULL allocator,
3506  VmaAllocation VMA_NOT_NULL allocation,
3507  VkDeviceSize offset,
3508  VkDeviceSize size);
3509 
3524 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3525  VmaAllocator VMA_NOT_NULL allocator,
3526  uint32_t allocationCount,
3527  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3528  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3529  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3530 
3545 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3546  VmaAllocator VMA_NOT_NULL allocator,
3547  uint32_t allocationCount,
3548  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3549  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3550  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3551 
3568 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3569 
3576 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3577 
3578 typedef enum VmaDefragmentationFlagBits {
3583 typedef VkFlags VmaDefragmentationFlags;
3584 
3589 typedef struct VmaDefragmentationInfo2 {
3604  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3610  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3613  uint32_t poolCount;
3629  const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3634  VkDeviceSize maxCpuBytesToMove;
3644  VkDeviceSize maxGpuBytesToMove;
3658  VkCommandBuffer VMA_NULLABLE commandBuffer;
3660 
3663  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3664  VkDeviceSize offset;
3666 
3672  uint32_t moveCount;
3673  VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3675 
3680 typedef struct VmaDefragmentationInfo {
3685  VkDeviceSize maxBytesToMove;
3692 
3694 typedef struct VmaDefragmentationStats {
3696  VkDeviceSize bytesMoved;
3698  VkDeviceSize bytesFreed;
3704 
3734 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3735  VmaAllocator VMA_NOT_NULL allocator,
3736  const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3737  VmaDefragmentationStats* VMA_NULLABLE pStats,
3738  VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3739 
3745 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3746  VmaAllocator VMA_NOT_NULL allocator,
3747  VmaDefragmentationContext VMA_NULLABLE context);
3748 
3749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3750  VmaAllocator VMA_NOT_NULL allocator,
3751  VmaDefragmentationContext VMA_NULLABLE context,
3752  VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3753 );
3754 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3755  VmaAllocator VMA_NOT_NULL allocator,
3756  VmaDefragmentationContext VMA_NULLABLE context
3757 );
3758 
3799 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3800  VmaAllocator VMA_NOT_NULL allocator,
3801  const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3802  size_t allocationCount,
3803  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3804  const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3805  VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3806 
3819 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3820  VmaAllocator VMA_NOT_NULL allocator,
3821  VmaAllocation VMA_NOT_NULL allocation,
3822  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3823 
3834 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3835  VmaAllocator VMA_NOT_NULL allocator,
3836  VmaAllocation VMA_NOT_NULL allocation,
3837  VkDeviceSize allocationLocalOffset,
3838  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3839  const void* VMA_NULLABLE pNext);
3840 
3853 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3854  VmaAllocator VMA_NOT_NULL allocator,
3855  VmaAllocation VMA_NOT_NULL allocation,
3856  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3857 
3868 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3869  VmaAllocator VMA_NOT_NULL allocator,
3870  VmaAllocation VMA_NOT_NULL allocation,
3871  VkDeviceSize allocationLocalOffset,
3872  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3873  const void* VMA_NULLABLE pNext);
3874 
3901 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3902  VmaAllocator VMA_NOT_NULL allocator,
3903  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3904  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3905  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3906  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3907  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3908 
3920 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3921  VmaAllocator VMA_NOT_NULL allocator,
3922  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3923  VmaAllocation VMA_NULLABLE allocation);
3924 
3926 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3927  VmaAllocator VMA_NOT_NULL allocator,
3928  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3929  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3930  VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3931  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3932  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3933 
3945 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3946  VmaAllocator VMA_NOT_NULL allocator,
3947  VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3948  VmaAllocation VMA_NULLABLE allocation);
3949 
3950 #ifdef __cplusplus
3951 }
3952 #endif
3953 
3954 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3955 
3956 // For Visual Studio IntelliSense.
3957 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3958 #define VMA_IMPLEMENTATION
3959 #endif
3960 
3961 #ifdef VMA_IMPLEMENTATION
3962 #undef VMA_IMPLEMENTATION
3963 
3964 #include <cstdint>
3965 #include <cstdlib>
3966 #include <cstring>
3967 #include <utility>
3968 
3969 /*******************************************************************************
3970 CONFIGURATION SECTION
3971 
3972 Define some of these macros before each #include of this header or change them
3973 here if you need other then default behavior depending on your environment.
3974 */
3975 
3976 /*
3977 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3978 internally, like:
3979 
3980  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3981 */
3982 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3983  #define VMA_STATIC_VULKAN_FUNCTIONS 1
3984 #endif
3985 
3986 /*
3987 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3988 internally, like:
3989 
3990  vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3991 */
3992 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3993  #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3994 #endif
3995 
3996 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3997 //#define VMA_USE_STL_CONTAINERS 1
3998 
3999 /* Set this macro to 1 to make the library including and using STL containers:
4000 std::pair, std::vector, std::list, std::unordered_map.
4001 
4002 Set it to 0 or undefined to make the library using its own implementation of
4003 the containers.
4004 */
4005 #if VMA_USE_STL_CONTAINERS
4006  #define VMA_USE_STL_VECTOR 1
4007  #define VMA_USE_STL_UNORDERED_MAP 1
4008  #define VMA_USE_STL_LIST 1
4009 #endif
4010 
4011 #ifndef VMA_USE_STL_SHARED_MUTEX
4012  // Compiler conforms to C++17.
4013  #if __cplusplus >= 201703L
4014  #define VMA_USE_STL_SHARED_MUTEX 1
4015  // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
4016  // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
4017  // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
4018  #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
4019  #define VMA_USE_STL_SHARED_MUTEX 1
4020  #else
4021  #define VMA_USE_STL_SHARED_MUTEX 0
4022  #endif
4023 #endif
4024 
4025 /*
4026 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
4027 Library has its own container implementation.
4028 */
4029 #if VMA_USE_STL_VECTOR
4030  #include <vector>
4031 #endif
4032 
4033 #if VMA_USE_STL_UNORDERED_MAP
4034  #include <unordered_map>
4035 #endif
4036 
4037 #if VMA_USE_STL_LIST
4038  #include <list>
4039 #endif
4040 
4041 /*
4042 Following headers are used in this CONFIGURATION section only, so feel free to
4043 remove them if not needed.
4044 */
4045 #include <cassert> // for assert
4046 #include <algorithm> // for min, max
4047 #include <mutex>
4048 
4049 #ifndef VMA_NULL
4050  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
4051  #define VMA_NULL nullptr
4052 #endif
4053 
4054 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
4055 #include <cstdlib>
4056 void *vma_aligned_alloc(size_t alignment, size_t size)
4057 {
4058  // alignment must be >= sizeof(void*)
4059  if(alignment < sizeof(void*))
4060  {
4061  alignment = sizeof(void*);
4062  }
4063 
4064  return memalign(alignment, size);
4065 }
4066 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
4067 #include <cstdlib>
4068 
4069 #if defined(__APPLE__)
4070 #include <AvailabilityMacros.h>
4071 #endif
4072 
4073 void *vma_aligned_alloc(size_t alignment, size_t size)
4074 {
4075 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
4076 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
4077  // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
4078  // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
4079  // MAC_OS_X_VERSION_10_16), even though the function is marked
4080  // availabe for 10.15. That's why the preprocessor checks for 10.16 but
4081  // the __builtin_available checks for 10.15.
4082  // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
4083  if (__builtin_available(macOS 10.15, iOS 13, *))
4084  return aligned_alloc(alignment, size);
4085 #endif
4086 #endif
4087  // alignment must be >= sizeof(void*)
4088  if(alignment < sizeof(void*))
4089  {
4090  alignment = sizeof(void*);
4091  }
4092 
4093  void *pointer;
4094  if(posix_memalign(&pointer, alignment, size) == 0)
4095  return pointer;
4096  return VMA_NULL;
4097 }
4098 #elif defined(_WIN32)
4099 void *vma_aligned_alloc(size_t alignment, size_t size)
4100 {
4101  return _aligned_malloc(size, alignment);
4102 }
4103 #else
4104 void *vma_aligned_alloc(size_t alignment, size_t size)
4105 {
4106  return aligned_alloc(alignment, size);
4107 }
4108 #endif
4109 
4110 // If your compiler is not compatible with C++11 and definition of
4111 // aligned_alloc() function is missing, uncommeting following line may help:
4112 
4113 //#include <malloc.h>
4114 
4115 // Normal assert to check for programmer's errors, especially in Debug configuration.
4116 #ifndef VMA_ASSERT
4117  #ifdef NDEBUG
4118  #define VMA_ASSERT(expr)
4119  #else
4120  #define VMA_ASSERT(expr) assert(expr)
4121  #endif
4122 #endif
4123 
4124 // Assert that will be called very often, like inside data structures e.g. operator[].
4125 // Making it non-empty can make program slow.
4126 #ifndef VMA_HEAVY_ASSERT
4127  #ifdef NDEBUG
4128  #define VMA_HEAVY_ASSERT(expr)
4129  #else
4130  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
4131  #endif
4132 #endif
4133 
4134 #ifndef VMA_ALIGN_OF
4135  #define VMA_ALIGN_OF(type) (__alignof(type))
4136 #endif
4137 
4138 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4139  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
4140 #endif
4141 
4142 #ifndef VMA_SYSTEM_FREE
4143  #if defined(_WIN32)
4144  #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
4145  #else
4146  #define VMA_SYSTEM_FREE(ptr) free(ptr)
4147  #endif
4148 #endif
4149 
4150 #ifndef VMA_MIN
4151  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4152 #endif
4153 
4154 #ifndef VMA_MAX
4155  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4156 #endif
4157 
4158 #ifndef VMA_SWAP
4159  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4160 #endif
4161 
4162 #ifndef VMA_SORT
4163  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4164 #endif
4165 
4166 #ifndef VMA_DEBUG_LOG
4167  #define VMA_DEBUG_LOG(format, ...)
4168  /*
4169  #define VMA_DEBUG_LOG(format, ...) do { \
4170  printf(format, __VA_ARGS__); \
4171  printf("\n"); \
4172  } while(false)
4173  */
4174 #endif
4175 
4176 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4177 #if VMA_STATS_STRING_ENABLED
4178  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4179  {
4180  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4181  }
4182  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4183  {
4184  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4185  }
4186  static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4187  {
4188  snprintf(outStr, strLen, "%p", ptr);
4189  }
4190 #endif
4191 
4192 #ifndef VMA_MUTEX
4193  class VmaMutex
4194  {
4195  public:
4196  void Lock() { m_Mutex.lock(); }
4197  void Unlock() { m_Mutex.unlock(); }
4198  bool TryLock() { return m_Mutex.try_lock(); }
4199  private:
4200  std::mutex m_Mutex;
4201  };
4202  #define VMA_MUTEX VmaMutex
4203 #endif
4204 
4205 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4206 #ifndef VMA_RW_MUTEX
4207  #if VMA_USE_STL_SHARED_MUTEX
4208  // Use std::shared_mutex from C++17.
4209  #include <shared_mutex>
4210  class VmaRWMutex
4211  {
4212  public:
4213  void LockRead() { m_Mutex.lock_shared(); }
4214  void UnlockRead() { m_Mutex.unlock_shared(); }
4215  bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4216  void LockWrite() { m_Mutex.lock(); }
4217  void UnlockWrite() { m_Mutex.unlock(); }
4218  bool TryLockWrite() { return m_Mutex.try_lock(); }
4219  private:
4220  std::shared_mutex m_Mutex;
4221  };
4222  #define VMA_RW_MUTEX VmaRWMutex
4223  #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4224  // Use SRWLOCK from WinAPI.
4225  // Minimum supported client = Windows Vista, server = Windows Server 2008.
4226  class VmaRWMutex
4227  {
4228  public:
4229  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4230  void LockRead() { AcquireSRWLockShared(&m_Lock); }
4231  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4232  bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4233  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4234  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4235  bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4236  private:
4237  SRWLOCK m_Lock;
4238  };
4239  #define VMA_RW_MUTEX VmaRWMutex
4240  #else
4241  // Less efficient fallback: Use normal mutex.
4242  class VmaRWMutex
4243  {
4244  public:
4245  void LockRead() { m_Mutex.Lock(); }
4246  void UnlockRead() { m_Mutex.Unlock(); }
4247  bool TryLockRead() { return m_Mutex.TryLock(); }
4248  void LockWrite() { m_Mutex.Lock(); }
4249  void UnlockWrite() { m_Mutex.Unlock(); }
4250  bool TryLockWrite() { return m_Mutex.TryLock(); }
4251  private:
4252  VMA_MUTEX m_Mutex;
4253  };
4254  #define VMA_RW_MUTEX VmaRWMutex
4255  #endif // #if VMA_USE_STL_SHARED_MUTEX
4256 #endif // #ifndef VMA_RW_MUTEX
4257 
4258 /*
4259 If providing your own implementation, you need to implement a subset of std::atomic.
4260 */
4261 #ifndef VMA_ATOMIC_UINT32
4262  #include <atomic>
4263  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4264 #endif
4265 
4266 #ifndef VMA_ATOMIC_UINT64
4267  #include <atomic>
4268  #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4269 #endif
4270 
4271 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4272 
4276  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4277 #endif
4278 
4279 #ifndef VMA_DEBUG_ALIGNMENT
4280 
4284  #define VMA_DEBUG_ALIGNMENT (1)
4285 #endif
4286 
4287 #ifndef VMA_DEBUG_MARGIN
4288 
4292  #define VMA_DEBUG_MARGIN (0)
4293 #endif
4294 
4295 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4296 
4300  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4301 #endif
4302 
4303 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4304 
4309  #define VMA_DEBUG_DETECT_CORRUPTION (0)
4310 #endif
4311 
4312 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4313 
4317  #define VMA_DEBUG_GLOBAL_MUTEX (0)
4318 #endif
4319 
4320 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4321 
4325  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4326 #endif
4327 
4328 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4329  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4331 #endif
4332 
4333 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4334  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4336 #endif
4337 
4338 #ifndef VMA_CLASS_NO_COPY
4339  #define VMA_CLASS_NO_COPY(className) \
4340  private: \
4341  className(const className&) = delete; \
4342  className& operator=(const className&) = delete;
4343 #endif
4344 
4345 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4346 
4347 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4348 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4349 
4350 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4351 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4352 
4353 /*******************************************************************************
4354 END OF CONFIGURATION
4355 */
4356 
4357 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4358 
4359 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4360 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4361 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4362 
4363 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4364 
4365 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4366  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4367 
4368 // Returns number of bits set to 1 in (v).
4369 static inline uint32_t VmaCountBitsSet(uint32_t v)
4370 {
4371  uint32_t c = v - ((v >> 1) & 0x55555555);
4372  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4373  c = ((c >> 4) + c) & 0x0F0F0F0F;
4374  c = ((c >> 8) + c) & 0x00FF00FF;
4375  c = ((c >> 16) + c) & 0x0000FFFF;
4376  return c;
4377 }
4378 
4379 /*
4380 Returns true if given number is a power of two.
4381 T must be unsigned integer number or signed integer but always nonnegative.
4382 For 0 returns true.
4383 */
4384 template <typename T>
4385 inline bool VmaIsPow2(T x)
4386 {
4387  return (x & (x-1)) == 0;
4388 }
4389 
4390 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4391 // Use types like uint32_t, uint64_t as T.
4392 template <typename T>
4393 static inline T VmaAlignUp(T val, T alignment)
4394 {
4395  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4396  return (val + alignment - 1) & ~(alignment - 1);
4397 }
4398 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4399 // Use types like uint32_t, uint64_t as T.
4400 template <typename T>
4401 static inline T VmaAlignDown(T val, T alignment)
4402 {
4403  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4404  return val & ~(alignment - 1);
4405 }
4406 
4407 // Division with mathematical rounding to nearest number.
4408 template <typename T>
4409 static inline T VmaRoundDiv(T x, T y)
4410 {
4411  return (x + (y / (T)2)) / y;
4412 }
4413 
4414 // Returns smallest power of 2 greater or equal to v.
4415 static inline uint32_t VmaNextPow2(uint32_t v)
4416 {
4417  v--;
4418  v |= v >> 1;
4419  v |= v >> 2;
4420  v |= v >> 4;
4421  v |= v >> 8;
4422  v |= v >> 16;
4423  v++;
4424  return v;
4425 }
4426 static inline uint64_t VmaNextPow2(uint64_t v)
4427 {
4428  v--;
4429  v |= v >> 1;
4430  v |= v >> 2;
4431  v |= v >> 4;
4432  v |= v >> 8;
4433  v |= v >> 16;
4434  v |= v >> 32;
4435  v++;
4436  return v;
4437 }
4438 
4439 // Returns largest power of 2 less or equal to v.
4440 static inline uint32_t VmaPrevPow2(uint32_t v)
4441 {
4442  v |= v >> 1;
4443  v |= v >> 2;
4444  v |= v >> 4;
4445  v |= v >> 8;
4446  v |= v >> 16;
4447  v = v ^ (v >> 1);
4448  return v;
4449 }
4450 static inline uint64_t VmaPrevPow2(uint64_t v)
4451 {
4452  v |= v >> 1;
4453  v |= v >> 2;
4454  v |= v >> 4;
4455  v |= v >> 8;
4456  v |= v >> 16;
4457  v |= v >> 32;
4458  v = v ^ (v >> 1);
4459  return v;
4460 }
4461 
4462 static inline bool VmaStrIsEmpty(const char* pStr)
4463 {
4464  return pStr == VMA_NULL || *pStr == '\0';
4465 }
4466 
4467 #if VMA_STATS_STRING_ENABLED
4468 
4469 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4470 {
4471  switch(algorithm)
4472  {
4474  return "Linear";
4476  return "Buddy";
4477  case 0:
4478  return "Default";
4479  default:
4480  VMA_ASSERT(0);
4481  return "";
4482  }
4483 }
4484 
4485 #endif // #if VMA_STATS_STRING_ENABLED
4486 
4487 #ifndef VMA_SORT
4488 
4489 template<typename Iterator, typename Compare>
4490 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4491 {
4492  Iterator centerValue = end; --centerValue;
4493  Iterator insertIndex = beg;
4494  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4495  {
4496  if(cmp(*memTypeIndex, *centerValue))
4497  {
4498  if(insertIndex != memTypeIndex)
4499  {
4500  VMA_SWAP(*memTypeIndex, *insertIndex);
4501  }
4502  ++insertIndex;
4503  }
4504  }
4505  if(insertIndex != centerValue)
4506  {
4507  VMA_SWAP(*insertIndex, *centerValue);
4508  }
4509  return insertIndex;
4510 }
4511 
4512 template<typename Iterator, typename Compare>
4513 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4514 {
4515  if(beg < end)
4516  {
4517  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4518  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4519  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4520  }
4521 }
4522 
4523 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4524 
4525 #endif // #ifndef VMA_SORT
4526 
4527 /*
4528 Returns true if two memory blocks occupy overlapping pages.
4529 ResourceA must be in less memory offset than ResourceB.
4530 
4531 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4532 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4533 */
4534 static inline bool VmaBlocksOnSamePage(
4535  VkDeviceSize resourceAOffset,
4536  VkDeviceSize resourceASize,
4537  VkDeviceSize resourceBOffset,
4538  VkDeviceSize pageSize)
4539 {
4540  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4541  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4542  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4543  VkDeviceSize resourceBStart = resourceBOffset;
4544  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4545  return resourceAEndPage == resourceBStartPage;
4546 }
4547 
4548 enum VmaSuballocationType
4549 {
4550  VMA_SUBALLOCATION_TYPE_FREE = 0,
4551  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4552  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4553  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4554  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4555  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4556  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4557 };
4558 
4559 /*
4560 Returns true if given suballocation types could conflict and must respect
4561 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4562 or linear image and another one is optimal image. If type is unknown, behave
4563 conservatively.
4564 */
4565 static inline bool VmaIsBufferImageGranularityConflict(
4566  VmaSuballocationType suballocType1,
4567  VmaSuballocationType suballocType2)
4568 {
4569  if(suballocType1 > suballocType2)
4570  {
4571  VMA_SWAP(suballocType1, suballocType2);
4572  }
4573 
4574  switch(suballocType1)
4575  {
4576  case VMA_SUBALLOCATION_TYPE_FREE:
4577  return false;
4578  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4579  return true;
4580  case VMA_SUBALLOCATION_TYPE_BUFFER:
4581  return
4582  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4583  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4584  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4585  return
4586  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4587  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4588  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4589  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4590  return
4591  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4592  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4593  return false;
4594  default:
4595  VMA_ASSERT(0);
4596  return true;
4597  }
4598 }
4599 
4600 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4601 {
4602 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4603  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4604  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4605  for(size_t i = 0; i < numberCount; ++i, ++pDst)
4606  {
4607  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4608  }
4609 #else
4610  // no-op
4611 #endif
4612 }
4613 
4614 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4615 {
4616 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4617  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4618  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4619  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4620  {
4621  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4622  {
4623  return false;
4624  }
4625  }
4626 #endif
4627  return true;
4628 }
4629 
4630 /*
4631 Fills structure with parameters of an example buffer to be used for transfers
4632 during GPU memory defragmentation.
4633 */
4634 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4635 {
4636  memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4637  outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4638  outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4639  outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4640 }
4641 
4642 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4643 struct VmaMutexLock
4644 {
4645  VMA_CLASS_NO_COPY(VmaMutexLock)
4646 public:
4647  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4648  m_pMutex(useMutex ? &mutex : VMA_NULL)
4649  { if(m_pMutex) { m_pMutex->Lock(); } }
4650  ~VmaMutexLock()
4651  { if(m_pMutex) { m_pMutex->Unlock(); } }
4652 private:
4653  VMA_MUTEX* m_pMutex;
4654 };
4655 
4656 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4657 struct VmaMutexLockRead
4658 {
4659  VMA_CLASS_NO_COPY(VmaMutexLockRead)
4660 public:
4661  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4662  m_pMutex(useMutex ? &mutex : VMA_NULL)
4663  { if(m_pMutex) { m_pMutex->LockRead(); } }
4664  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4665 private:
4666  VMA_RW_MUTEX* m_pMutex;
4667 };
4668 
4669 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4670 struct VmaMutexLockWrite
4671 {
4672  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4673 public:
4674  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4675  m_pMutex(useMutex ? &mutex : VMA_NULL)
4676  { if(m_pMutex) { m_pMutex->LockWrite(); } }
4677  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4678 private:
4679  VMA_RW_MUTEX* m_pMutex;
4680 };
4681 
4682 #if VMA_DEBUG_GLOBAL_MUTEX
4683  static VMA_MUTEX gDebugGlobalMutex;
4684  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4685 #else
4686  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4687 #endif
4688 
4689 // Minimum size of a free suballocation to register it in the free suballocation collection.
4690 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4691 
4692 /*
4693 Performs binary search and returns iterator to first element that is greater or
4694 equal to (key), according to comparison (cmp).
4695 
4696 Cmp should return true if first argument is less than second argument.
4697 
4698 Returned value is the found element, if present in the collection or place where
4699 new element with value (key) should be inserted.
4700 */
4701 template <typename CmpLess, typename IterT, typename KeyT>
4702 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4703 {
4704  size_t down = 0, up = (end - beg);
4705  while(down < up)
4706  {
4707  const size_t mid = (down + up) / 2;
4708  if(cmp(*(beg+mid), key))
4709  {
4710  down = mid + 1;
4711  }
4712  else
4713  {
4714  up = mid;
4715  }
4716  }
4717  return beg + down;
4718 }
4719 
4720 template<typename CmpLess, typename IterT, typename KeyT>
4721 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4722 {
4723  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4724  beg, end, value, cmp);
4725  if(it == end ||
4726  (!cmp(*it, value) && !cmp(value, *it)))
4727  {
4728  return it;
4729  }
4730  return end;
4731 }
4732 
4733 /*
4734 Returns true if all pointers in the array are not-null and unique.
4735 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4736 T must be pointer type, e.g. VmaAllocation, VmaPool.
4737 */
4738 template<typename T>
4739 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4740 {
4741  for(uint32_t i = 0; i < count; ++i)
4742  {
4743  const T iPtr = arr[i];
4744  if(iPtr == VMA_NULL)
4745  {
4746  return false;
4747  }
4748  for(uint32_t j = i + 1; j < count; ++j)
4749  {
4750  if(iPtr == arr[j])
4751  {
4752  return false;
4753  }
4754  }
4755  }
4756  return true;
4757 }
4758 
4759 template<typename MainT, typename NewT>
4760 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4761 {
4762  newStruct->pNext = mainStruct->pNext;
4763  mainStruct->pNext = newStruct;
4764 }
4765 
4767 // Memory allocation
4768 
4769 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4770 {
4771  void* result = VMA_NULL;
4772  if((pAllocationCallbacks != VMA_NULL) &&
4773  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4774  {
4775  result = (*pAllocationCallbacks->pfnAllocation)(
4776  pAllocationCallbacks->pUserData,
4777  size,
4778  alignment,
4779  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4780  }
4781  else
4782  {
4783  result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4784  }
4785  VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4786  return result;
4787 }
4788 
4789 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4790 {
4791  if((pAllocationCallbacks != VMA_NULL) &&
4792  (pAllocationCallbacks->pfnFree != VMA_NULL))
4793  {
4794  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4795  }
4796  else
4797  {
4798  VMA_SYSTEM_FREE(ptr);
4799  }
4800 }
4801 
4802 template<typename T>
4803 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4804 {
4805  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4806 }
4807 
4808 template<typename T>
4809 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4810 {
4811  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4812 }
4813 
4814 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4815 
4816 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4817 
4818 template<typename T>
4819 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4820 {
4821  ptr->~T();
4822  VmaFree(pAllocationCallbacks, ptr);
4823 }
4824 
4825 template<typename T>
4826 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4827 {
4828  if(ptr != VMA_NULL)
4829  {
4830  for(size_t i = count; i--; )
4831  {
4832  ptr[i].~T();
4833  }
4834  VmaFree(pAllocationCallbacks, ptr);
4835  }
4836 }
4837 
4838 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4839 {
4840  if(srcStr != VMA_NULL)
4841  {
4842  const size_t len = strlen(srcStr);
4843  char* const result = vma_new_array(allocs, char, len + 1);
4844  memcpy(result, srcStr, len + 1);
4845  return result;
4846  }
4847  else
4848  {
4849  return VMA_NULL;
4850  }
4851 }
4852 
4853 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4854 {
4855  if(str != VMA_NULL)
4856  {
4857  const size_t len = strlen(str);
4858  vma_delete_array(allocs, str, len + 1);
4859  }
4860 }
4861 
4862 // STL-compatible allocator.
4863 template<typename T>
4864 class VmaStlAllocator
4865 {
4866 public:
4867  const VkAllocationCallbacks* const m_pCallbacks;
4868  typedef T value_type;
4869 
4870  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4871  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4872 
4873  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4874  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4875 
4876  template<typename U>
4877  bool operator==(const VmaStlAllocator<U>& rhs) const
4878  {
4879  return m_pCallbacks == rhs.m_pCallbacks;
4880  }
4881  template<typename U>
4882  bool operator!=(const VmaStlAllocator<U>& rhs) const
4883  {
4884  return m_pCallbacks != rhs.m_pCallbacks;
4885  }
4886 
4887  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4888 };
4889 
4890 #if VMA_USE_STL_VECTOR
4891 
4892 #define VmaVector std::vector
4893 
4894 template<typename T, typename allocatorT>
4895 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4896 {
4897  vec.insert(vec.begin() + index, item);
4898 }
4899 
4900 template<typename T, typename allocatorT>
4901 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4902 {
4903  vec.erase(vec.begin() + index);
4904 }
4905 
4906 #else // #if VMA_USE_STL_VECTOR
4907 
4908 /* Class with interface compatible with subset of std::vector.
4909 T must be POD because constructors and destructors are not called and memcpy is
4910 used for these objects. */
4911 template<typename T, typename AllocatorT>
4912 class VmaVector
4913 {
4914 public:
4915  typedef T value_type;
4916 
4917  VmaVector(const AllocatorT& allocator) :
4918  m_Allocator(allocator),
4919  m_pArray(VMA_NULL),
4920  m_Count(0),
4921  m_Capacity(0)
4922  {
4923  }
4924 
4925  VmaVector(size_t count, const AllocatorT& allocator) :
4926  m_Allocator(allocator),
4927  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4928  m_Count(count),
4929  m_Capacity(count)
4930  {
4931  }
4932 
4933  // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4934  // value is unused.
4935  VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4936  : VmaVector(count, allocator) {}
4937 
4938  VmaVector(const VmaVector<T, AllocatorT>& src) :
4939  m_Allocator(src.m_Allocator),
4940  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4941  m_Count(src.m_Count),
4942  m_Capacity(src.m_Count)
4943  {
4944  if(m_Count != 0)
4945  {
4946  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4947  }
4948  }
4949 
4950  ~VmaVector()
4951  {
4952  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4953  }
4954 
4955  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4956  {
4957  if(&rhs != this)
4958  {
4959  resize(rhs.m_Count);
4960  if(m_Count != 0)
4961  {
4962  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4963  }
4964  }
4965  return *this;
4966  }
4967 
4968  bool empty() const { return m_Count == 0; }
4969  size_t size() const { return m_Count; }
4970  T* data() { return m_pArray; }
4971  const T* data() const { return m_pArray; }
4972 
4973  T& operator[](size_t index)
4974  {
4975  VMA_HEAVY_ASSERT(index < m_Count);
4976  return m_pArray[index];
4977  }
4978  const T& operator[](size_t index) const
4979  {
4980  VMA_HEAVY_ASSERT(index < m_Count);
4981  return m_pArray[index];
4982  }
4983 
4984  T& front()
4985  {
4986  VMA_HEAVY_ASSERT(m_Count > 0);
4987  return m_pArray[0];
4988  }
4989  const T& front() const
4990  {
4991  VMA_HEAVY_ASSERT(m_Count > 0);
4992  return m_pArray[0];
4993  }
4994  T& back()
4995  {
4996  VMA_HEAVY_ASSERT(m_Count > 0);
4997  return m_pArray[m_Count - 1];
4998  }
4999  const T& back() const
5000  {
5001  VMA_HEAVY_ASSERT(m_Count > 0);
5002  return m_pArray[m_Count - 1];
5003  }
5004 
5005  void reserve(size_t newCapacity, bool freeMemory = false)
5006  {
5007  newCapacity = VMA_MAX(newCapacity, m_Count);
5008 
5009  if((newCapacity < m_Capacity) && !freeMemory)
5010  {
5011  newCapacity = m_Capacity;
5012  }
5013 
5014  if(newCapacity != m_Capacity)
5015  {
5016  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
5017  if(m_Count != 0)
5018  {
5019  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5020  }
5021  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5022  m_Capacity = newCapacity;
5023  m_pArray = newArray;
5024  }
5025  }
5026 
5027  void resize(size_t newCount, bool freeMemory = false)
5028  {
5029  size_t newCapacity = m_Capacity;
5030  if(newCount > m_Capacity)
5031  {
5032  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
5033  }
5034  else if(freeMemory)
5035  {
5036  newCapacity = newCount;
5037  }
5038 
5039  if(newCapacity != m_Capacity)
5040  {
5041  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
5042  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
5043  if(elementsToCopy != 0)
5044  {
5045  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
5046  }
5047  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5048  m_Capacity = newCapacity;
5049  m_pArray = newArray;
5050  }
5051 
5052  m_Count = newCount;
5053  }
5054 
5055  void clear(bool freeMemory = false)
5056  {
5057  resize(0, freeMemory);
5058  }
5059 
5060  void insert(size_t index, const T& src)
5061  {
5062  VMA_HEAVY_ASSERT(index <= m_Count);
5063  const size_t oldCount = size();
5064  resize(oldCount + 1);
5065  if(index < oldCount)
5066  {
5067  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
5068  }
5069  m_pArray[index] = src;
5070  }
5071 
5072  void remove(size_t index)
5073  {
5074  VMA_HEAVY_ASSERT(index < m_Count);
5075  const size_t oldCount = size();
5076  if(index < oldCount - 1)
5077  {
5078  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
5079  }
5080  resize(oldCount - 1);
5081  }
5082 
5083  void push_back(const T& src)
5084  {
5085  const size_t newIndex = size();
5086  resize(newIndex + 1);
5087  m_pArray[newIndex] = src;
5088  }
5089 
5090  void pop_back()
5091  {
5092  VMA_HEAVY_ASSERT(m_Count > 0);
5093  resize(size() - 1);
5094  }
5095 
5096  void push_front(const T& src)
5097  {
5098  insert(0, src);
5099  }
5100 
5101  void pop_front()
5102  {
5103  VMA_HEAVY_ASSERT(m_Count > 0);
5104  remove(0);
5105  }
5106 
5107  typedef T* iterator;
5108 
5109  iterator begin() { return m_pArray; }
5110  iterator end() { return m_pArray + m_Count; }
5111 
5112 private:
5113  AllocatorT m_Allocator;
5114  T* m_pArray;
5115  size_t m_Count;
5116  size_t m_Capacity;
5117 };
5118 
5119 template<typename T, typename allocatorT>
5120 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5121 {
5122  vec.insert(index, item);
5123 }
5124 
5125 template<typename T, typename allocatorT>
5126 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5127 {
5128  vec.remove(index);
5129 }
5130 
5131 #endif // #if VMA_USE_STL_VECTOR
5132 
5133 template<typename CmpLess, typename VectorT>
5134 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5135 {
5136  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5137  vector.data(),
5138  vector.data() + vector.size(),
5139  value,
5140  CmpLess()) - vector.data();
5141  VmaVectorInsert(vector, indexToInsert, value);
5142  return indexToInsert;
5143 }
5144 
5145 template<typename CmpLess, typename VectorT>
5146 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5147 {
5148  CmpLess comparator;
5149  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5150  vector.begin(),
5151  vector.end(),
5152  value,
5153  comparator);
5154  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5155  {
5156  size_t indexToRemove = it - vector.begin();
5157  VmaVectorRemove(vector, indexToRemove);
5158  return true;
5159  }
5160  return false;
5161 }
5162 
5164 // class VmaSmallVector
5165 
5166 /*
5167 This is a vector (a variable-sized array), optimized for the case when the array is small.
5168 
5169 It contains some number of elements in-place, which allows it to avoid heap allocation
5170 when the actual number of elements is below that threshold. This allows normal "small"
5171 cases to be fast without losing generality for large inputs.
5172 */
5173 
5174 template<typename T, typename AllocatorT, size_t N>
5175 class VmaSmallVector
5176 {
5177 public:
5178  typedef T value_type;
5179 
5180  VmaSmallVector(const AllocatorT& allocator) :
5181  m_Count(0),
5182  m_DynamicArray(allocator)
5183  {
5184  }
5185  VmaSmallVector(size_t count, const AllocatorT& allocator) :
5186  m_Count(count),
5187  m_DynamicArray(count > N ? count : 0, allocator)
5188  {
5189  }
5190  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5191  VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5192  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5193  VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5194 
5195  bool empty() const { return m_Count == 0; }
5196  size_t size() const { return m_Count; }
5197  T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5198  const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5199 
5200  T& operator[](size_t index)
5201  {
5202  VMA_HEAVY_ASSERT(index < m_Count);
5203  return data()[index];
5204  }
5205  const T& operator[](size_t index) const
5206  {
5207  VMA_HEAVY_ASSERT(index < m_Count);
5208  return data()[index];
5209  }
5210 
5211  T& front()
5212  {
5213  VMA_HEAVY_ASSERT(m_Count > 0);
5214  return data()[0];
5215  }
5216  const T& front() const
5217  {
5218  VMA_HEAVY_ASSERT(m_Count > 0);
5219  return data()[0];
5220  }
5221  T& back()
5222  {
5223  VMA_HEAVY_ASSERT(m_Count > 0);
5224  return data()[m_Count - 1];
5225  }
5226  const T& back() const
5227  {
5228  VMA_HEAVY_ASSERT(m_Count > 0);
5229  return data()[m_Count - 1];
5230  }
5231 
5232  void resize(size_t newCount, bool freeMemory = false)
5233  {
5234  if(newCount > N && m_Count > N)
5235  {
5236  // Any direction, staying in m_DynamicArray
5237  m_DynamicArray.resize(newCount, freeMemory);
5238  }
5239  else if(newCount > N && m_Count <= N)
5240  {
5241  // Growing, moving from m_StaticArray to m_DynamicArray
5242  m_DynamicArray.resize(newCount, freeMemory);
5243  if(m_Count > 0)
5244  {
5245  memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5246  }
5247  }
5248  else if(newCount <= N && m_Count > N)
5249  {
5250  // Shrinking, moving from m_DynamicArray to m_StaticArray
5251  if(newCount > 0)
5252  {
5253  memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5254  }
5255  m_DynamicArray.resize(0, freeMemory);
5256  }
5257  else
5258  {
5259  // Any direction, staying in m_StaticArray - nothing to do here
5260  }
5261  m_Count = newCount;
5262  }
5263 
5264  void clear(bool freeMemory = false)
5265  {
5266  m_DynamicArray.clear(freeMemory);
5267  m_Count = 0;
5268  }
5269 
5270  void insert(size_t index, const T& src)
5271  {
5272  VMA_HEAVY_ASSERT(index <= m_Count);
5273  const size_t oldCount = size();
5274  resize(oldCount + 1);
5275  T* const dataPtr = data();
5276  if(index < oldCount)
5277  {
5278  // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5279  memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5280  }
5281  dataPtr[index] = src;
5282  }
5283 
5284  void remove(size_t index)
5285  {
5286  VMA_HEAVY_ASSERT(index < m_Count);
5287  const size_t oldCount = size();
5288  if(index < oldCount - 1)
5289  {
5290  // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5291  T* const dataPtr = data();
5292  memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5293  }
5294  resize(oldCount - 1);
5295  }
5296 
5297  void push_back(const T& src)
5298  {
5299  const size_t newIndex = size();
5300  resize(newIndex + 1);
5301  data()[newIndex] = src;
5302  }
5303 
5304  void pop_back()
5305  {
5306  VMA_HEAVY_ASSERT(m_Count > 0);
5307  resize(size() - 1);
5308  }
5309 
5310  void push_front(const T& src)
5311  {
5312  insert(0, src);
5313  }
5314 
5315  void pop_front()
5316  {
5317  VMA_HEAVY_ASSERT(m_Count > 0);
5318  remove(0);
5319  }
5320 
5321  typedef T* iterator;
5322 
5323  iterator begin() { return data(); }
5324  iterator end() { return data() + m_Count; }
5325 
5326 private:
5327  size_t m_Count;
5328  T m_StaticArray[N]; // Used when m_Size <= N
5329  VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5330 };
5331 
5333 // class VmaPoolAllocator
5334 
5335 /*
5336 Allocator for objects of type T using a list of arrays (pools) to speed up
5337 allocation. Number of elements that can be allocated is not bounded because
5338 allocator can create multiple blocks.
5339 */
5340 template<typename T>
5341 class VmaPoolAllocator
5342 {
5343  VMA_CLASS_NO_COPY(VmaPoolAllocator)
5344 public:
5345  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5346  ~VmaPoolAllocator();
5347  template<typename... Types> T* Alloc(Types... args);
5348  void Free(T* ptr);
5349 
5350 private:
5351  union Item
5352  {
5353  uint32_t NextFreeIndex;
5354  alignas(T) char Value[sizeof(T)];
5355  };
5356 
5357  struct ItemBlock
5358  {
5359  Item* pItems;
5360  uint32_t Capacity;
5361  uint32_t FirstFreeIndex;
5362  };
5363 
5364  const VkAllocationCallbacks* m_pAllocationCallbacks;
5365  const uint32_t m_FirstBlockCapacity;
5366  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5367 
5368  ItemBlock& CreateNewBlock();
5369 };
5370 
5371 template<typename T>
5372 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5373  m_pAllocationCallbacks(pAllocationCallbacks),
5374  m_FirstBlockCapacity(firstBlockCapacity),
5375  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5376 {
5377  VMA_ASSERT(m_FirstBlockCapacity > 1);
5378 }
5379 
5380 template<typename T>
5381 VmaPoolAllocator<T>::~VmaPoolAllocator()
5382 {
5383  for(size_t i = m_ItemBlocks.size(); i--; )
5384  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5385  m_ItemBlocks.clear();
5386 }
5387 
5388 template<typename T>
5389 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5390 {
5391  for(size_t i = m_ItemBlocks.size(); i--; )
5392  {
5393  ItemBlock& block = m_ItemBlocks[i];
5394  // This block has some free items: Use first one.
5395  if(block.FirstFreeIndex != UINT32_MAX)
5396  {
5397  Item* const pItem = &block.pItems[block.FirstFreeIndex];
5398  block.FirstFreeIndex = pItem->NextFreeIndex;
5399  T* result = (T*)&pItem->Value;
5400  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5401  return result;
5402  }
5403  }
5404 
5405  // No block has free item: Create new one and use it.
5406  ItemBlock& newBlock = CreateNewBlock();
5407  Item* const pItem = &newBlock.pItems[0];
5408  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5409  T* result = (T*)&pItem->Value;
5410  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5411  return result;
5412 }
5413 
5414 template<typename T>
5415 void VmaPoolAllocator<T>::Free(T* ptr)
5416 {
5417  // Search all memory blocks to find ptr.
5418  for(size_t i = m_ItemBlocks.size(); i--; )
5419  {
5420  ItemBlock& block = m_ItemBlocks[i];
5421 
5422  // Casting to union.
5423  Item* pItemPtr;
5424  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5425 
5426  // Check if pItemPtr is in address range of this block.
5427  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5428  {
5429  ptr->~T(); // Explicit destructor call.
5430  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5431  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5432  block.FirstFreeIndex = index;
5433  return;
5434  }
5435  }
5436  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5437 }
5438 
5439 template<typename T>
5440 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5441 {
5442  const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5443  m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5444 
5445  const ItemBlock newBlock = {
5446  vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5447  newBlockCapacity,
5448  0 };
5449 
5450  m_ItemBlocks.push_back(newBlock);
5451 
5452  // Setup singly-linked list of all free items in this block.
5453  for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5454  newBlock.pItems[i].NextFreeIndex = i + 1;
5455  newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5456  return m_ItemBlocks.back();
5457 }
5458 
5460 // class VmaRawList, VmaList
5461 
5462 #if VMA_USE_STL_LIST
5463 
5464 #define VmaList std::list
5465 
5466 #else // #if VMA_USE_STL_LIST
5467 
5468 template<typename T>
5469 struct VmaListItem
5470 {
5471  VmaListItem* pPrev;
5472  VmaListItem* pNext;
5473  T Value;
5474 };
5475 
5476 // Doubly linked list.
5477 template<typename T>
5478 class VmaRawList
5479 {
5480  VMA_CLASS_NO_COPY(VmaRawList)
5481 public:
5482  typedef VmaListItem<T> ItemType;
5483 
5484  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5485  ~VmaRawList();
5486  void Clear();
5487 
5488  size_t GetCount() const { return m_Count; }
5489  bool IsEmpty() const { return m_Count == 0; }
5490 
5491  ItemType* Front() { return m_pFront; }
5492  const ItemType* Front() const { return m_pFront; }
5493  ItemType* Back() { return m_pBack; }
5494  const ItemType* Back() const { return m_pBack; }
5495 
5496  ItemType* PushBack();
5497  ItemType* PushFront();
5498  ItemType* PushBack(const T& value);
5499  ItemType* PushFront(const T& value);
5500  void PopBack();
5501  void PopFront();
5502 
5503  // Item can be null - it means PushBack.
5504  ItemType* InsertBefore(ItemType* pItem);
5505  // Item can be null - it means PushFront.
5506  ItemType* InsertAfter(ItemType* pItem);
5507 
5508  ItemType* InsertBefore(ItemType* pItem, const T& value);
5509  ItemType* InsertAfter(ItemType* pItem, const T& value);
5510 
5511  void Remove(ItemType* pItem);
5512 
5513 private:
5514  const VkAllocationCallbacks* const m_pAllocationCallbacks;
5515  VmaPoolAllocator<ItemType> m_ItemAllocator;
5516  ItemType* m_pFront;
5517  ItemType* m_pBack;
5518  size_t m_Count;
5519 };
5520 
5521 template<typename T>
5522 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5523  m_pAllocationCallbacks(pAllocationCallbacks),
5524  m_ItemAllocator(pAllocationCallbacks, 128),
5525  m_pFront(VMA_NULL),
5526  m_pBack(VMA_NULL),
5527  m_Count(0)
5528 {
5529 }
5530 
5531 template<typename T>
5532 VmaRawList<T>::~VmaRawList()
5533 {
5534  // Intentionally not calling Clear, because that would be unnecessary
5535  // computations to return all items to m_ItemAllocator as free.
5536 }
5537 
5538 template<typename T>
5539 void VmaRawList<T>::Clear()
5540 {
5541  if(IsEmpty() == false)
5542  {
5543  ItemType* pItem = m_pBack;
5544  while(pItem != VMA_NULL)
5545  {
5546  ItemType* const pPrevItem = pItem->pPrev;
5547  m_ItemAllocator.Free(pItem);
5548  pItem = pPrevItem;
5549  }
5550  m_pFront = VMA_NULL;
5551  m_pBack = VMA_NULL;
5552  m_Count = 0;
5553  }
5554 }
5555 
5556 template<typename T>
5557 VmaListItem<T>* VmaRawList<T>::PushBack()
5558 {
5559  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5560  pNewItem->pNext = VMA_NULL;
5561  if(IsEmpty())
5562  {
5563  pNewItem->pPrev = VMA_NULL;
5564  m_pFront = pNewItem;
5565  m_pBack = pNewItem;
5566  m_Count = 1;
5567  }
5568  else
5569  {
5570  pNewItem->pPrev = m_pBack;
5571  m_pBack->pNext = pNewItem;
5572  m_pBack = pNewItem;
5573  ++m_Count;
5574  }
5575  return pNewItem;
5576 }
5577 
5578 template<typename T>
5579 VmaListItem<T>* VmaRawList<T>::PushFront()
5580 {
5581  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5582  pNewItem->pPrev = VMA_NULL;
5583  if(IsEmpty())
5584  {
5585  pNewItem->pNext = VMA_NULL;
5586  m_pFront = pNewItem;
5587  m_pBack = pNewItem;
5588  m_Count = 1;
5589  }
5590  else
5591  {
5592  pNewItem->pNext = m_pFront;
5593  m_pFront->pPrev = pNewItem;
5594  m_pFront = pNewItem;
5595  ++m_Count;
5596  }
5597  return pNewItem;
5598 }
5599 
5600 template<typename T>
5601 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5602 {
5603  ItemType* const pNewItem = PushBack();
5604  pNewItem->Value = value;
5605  return pNewItem;
5606 }
5607 
5608 template<typename T>
5609 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5610 {
5611  ItemType* const pNewItem = PushFront();
5612  pNewItem->Value = value;
5613  return pNewItem;
5614 }
5615 
5616 template<typename T>
5617 void VmaRawList<T>::PopBack()
5618 {
5619  VMA_HEAVY_ASSERT(m_Count > 0);
5620  ItemType* const pBackItem = m_pBack;
5621  ItemType* const pPrevItem = pBackItem->pPrev;
5622  if(pPrevItem != VMA_NULL)
5623  {
5624  pPrevItem->pNext = VMA_NULL;
5625  }
5626  m_pBack = pPrevItem;
5627  m_ItemAllocator.Free(pBackItem);
5628  --m_Count;
5629 }
5630 
5631 template<typename T>
5632 void VmaRawList<T>::PopFront()
5633 {
5634  VMA_HEAVY_ASSERT(m_Count > 0);
5635  ItemType* const pFrontItem = m_pFront;
5636  ItemType* const pNextItem = pFrontItem->pNext;
5637  if(pNextItem != VMA_NULL)
5638  {
5639  pNextItem->pPrev = VMA_NULL;
5640  }
5641  m_pFront = pNextItem;
5642  m_ItemAllocator.Free(pFrontItem);
5643  --m_Count;
5644 }
5645 
5646 template<typename T>
5647 void VmaRawList<T>::Remove(ItemType* pItem)
5648 {
5649  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5650  VMA_HEAVY_ASSERT(m_Count > 0);
5651 
5652  if(pItem->pPrev != VMA_NULL)
5653  {
5654  pItem->pPrev->pNext = pItem->pNext;
5655  }
5656  else
5657  {
5658  VMA_HEAVY_ASSERT(m_pFront == pItem);
5659  m_pFront = pItem->pNext;
5660  }
5661 
5662  if(pItem->pNext != VMA_NULL)
5663  {
5664  pItem->pNext->pPrev = pItem->pPrev;
5665  }
5666  else
5667  {
5668  VMA_HEAVY_ASSERT(m_pBack == pItem);
5669  m_pBack = pItem->pPrev;
5670  }
5671 
5672  m_ItemAllocator.Free(pItem);
5673  --m_Count;
5674 }
5675 
5676 template<typename T>
5677 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5678 {
5679  if(pItem != VMA_NULL)
5680  {
5681  ItemType* const prevItem = pItem->pPrev;
5682  ItemType* const newItem = m_ItemAllocator.Alloc();
5683  newItem->pPrev = prevItem;
5684  newItem->pNext = pItem;
5685  pItem->pPrev = newItem;
5686  if(prevItem != VMA_NULL)
5687  {
5688  prevItem->pNext = newItem;
5689  }
5690  else
5691  {
5692  VMA_HEAVY_ASSERT(m_pFront == pItem);
5693  m_pFront = newItem;
5694  }
5695  ++m_Count;
5696  return newItem;
5697  }
5698  else
5699  return PushBack();
5700 }
5701 
5702 template<typename T>
5703 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5704 {
5705  if(pItem != VMA_NULL)
5706  {
5707  ItemType* const nextItem = pItem->pNext;
5708  ItemType* const newItem = m_ItemAllocator.Alloc();
5709  newItem->pNext = nextItem;
5710  newItem->pPrev = pItem;
5711  pItem->pNext = newItem;
5712  if(nextItem != VMA_NULL)
5713  {
5714  nextItem->pPrev = newItem;
5715  }
5716  else
5717  {
5718  VMA_HEAVY_ASSERT(m_pBack == pItem);
5719  m_pBack = newItem;
5720  }
5721  ++m_Count;
5722  return newItem;
5723  }
5724  else
5725  return PushFront();
5726 }
5727 
5728 template<typename T>
5729 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5730 {
5731  ItemType* const newItem = InsertBefore(pItem);
5732  newItem->Value = value;
5733  return newItem;
5734 }
5735 
5736 template<typename T>
5737 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5738 {
5739  ItemType* const newItem = InsertAfter(pItem);
5740  newItem->Value = value;
5741  return newItem;
5742 }
5743 
5744 template<typename T, typename AllocatorT>
5745 class VmaList
5746 {
5747  VMA_CLASS_NO_COPY(VmaList)
5748 public:
5749  class iterator
5750  {
5751  public:
5752  iterator() :
5753  m_pList(VMA_NULL),
5754  m_pItem(VMA_NULL)
5755  {
5756  }
5757 
5758  T& operator*() const
5759  {
5760  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5761  return m_pItem->Value;
5762  }
5763  T* operator->() const
5764  {
5765  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5766  return &m_pItem->Value;
5767  }
5768 
5769  iterator& operator++()
5770  {
5771  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5772  m_pItem = m_pItem->pNext;
5773  return *this;
5774  }
5775  iterator& operator--()
5776  {
5777  if(m_pItem != VMA_NULL)
5778  {
5779  m_pItem = m_pItem->pPrev;
5780  }
5781  else
5782  {
5783  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5784  m_pItem = m_pList->Back();
5785  }
5786  return *this;
5787  }
5788 
5789  iterator operator++(int)
5790  {
5791  iterator result = *this;
5792  ++*this;
5793  return result;
5794  }
5795  iterator operator--(int)
5796  {
5797  iterator result = *this;
5798  --*this;
5799  return result;
5800  }
5801 
5802  bool operator==(const iterator& rhs) const
5803  {
5804  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5805  return m_pItem == rhs.m_pItem;
5806  }
5807  bool operator!=(const iterator& rhs) const
5808  {
5809  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5810  return m_pItem != rhs.m_pItem;
5811  }
5812 
5813  private:
5814  VmaRawList<T>* m_pList;
5815  VmaListItem<T>* m_pItem;
5816 
5817  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5818  m_pList(pList),
5819  m_pItem(pItem)
5820  {
5821  }
5822 
5823  friend class VmaList<T, AllocatorT>;
5824  };
5825 
5826  class const_iterator
5827  {
5828  public:
5829  const_iterator() :
5830  m_pList(VMA_NULL),
5831  m_pItem(VMA_NULL)
5832  {
5833  }
5834 
5835  const_iterator(const iterator& src) :
5836  m_pList(src.m_pList),
5837  m_pItem(src.m_pItem)
5838  {
5839  }
5840 
5841  const T& operator*() const
5842  {
5843  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5844  return m_pItem->Value;
5845  }
5846  const T* operator->() const
5847  {
5848  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5849  return &m_pItem->Value;
5850  }
5851 
5852  const_iterator& operator++()
5853  {
5854  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5855  m_pItem = m_pItem->pNext;
5856  return *this;
5857  }
5858  const_iterator& operator--()
5859  {
5860  if(m_pItem != VMA_NULL)
5861  {
5862  m_pItem = m_pItem->pPrev;
5863  }
5864  else
5865  {
5866  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5867  m_pItem = m_pList->Back();
5868  }
5869  return *this;
5870  }
5871 
5872  const_iterator operator++(int)
5873  {
5874  const_iterator result = *this;
5875  ++*this;
5876  return result;
5877  }
5878  const_iterator operator--(int)
5879  {
5880  const_iterator result = *this;
5881  --*this;
5882  return result;
5883  }
5884 
5885  bool operator==(const const_iterator& rhs) const
5886  {
5887  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5888  return m_pItem == rhs.m_pItem;
5889  }
5890  bool operator!=(const const_iterator& rhs) const
5891  {
5892  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5893  return m_pItem != rhs.m_pItem;
5894  }
5895 
5896  private:
5897  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5898  m_pList(pList),
5899  m_pItem(pItem)
5900  {
5901  }
5902 
5903  const VmaRawList<T>* m_pList;
5904  const VmaListItem<T>* m_pItem;
5905 
5906  friend class VmaList<T, AllocatorT>;
5907  };
5908 
5909  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5910 
5911  bool empty() const { return m_RawList.IsEmpty(); }
5912  size_t size() const { return m_RawList.GetCount(); }
5913 
5914  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
5915  iterator end() { return iterator(&m_RawList, VMA_NULL); }
5916 
5917  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
5918  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5919 
5920  void clear() { m_RawList.Clear(); }
5921  void push_back(const T& value) { m_RawList.PushBack(value); }
5922  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
5923  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5924 
5925 private:
5926  VmaRawList<T> m_RawList;
5927 };
5928 
5929 #endif // #if VMA_USE_STL_LIST
5930 
5932 // class VmaMap
5933 
5934 // Unused in this version.
5935 #if 0
5936 
5937 #if VMA_USE_STL_UNORDERED_MAP
5938 
5939 #define VmaPair std::pair
5940 
5941 #define VMA_MAP_TYPE(KeyT, ValueT) \
5942  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5943 
5944 #else // #if VMA_USE_STL_UNORDERED_MAP
5945 
5946 template<typename T1, typename T2>
5947 struct VmaPair
5948 {
5949  T1 first;
5950  T2 second;
5951 
5952  VmaPair() : first(), second() { }
5953  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5954 };
5955 
5956 /* Class compatible with subset of interface of std::unordered_map.
5957 KeyT, ValueT must be POD because they will be stored in VmaVector.
5958 */
5959 template<typename KeyT, typename ValueT>
5960 class VmaMap
5961 {
5962 public:
5963  typedef VmaPair<KeyT, ValueT> PairType;
5964  typedef PairType* iterator;
5965 
5966  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5967 
5968  iterator begin() { return m_Vector.begin(); }
5969  iterator end() { return m_Vector.end(); }
5970 
5971  void insert(const PairType& pair);
5972  iterator find(const KeyT& key);
5973  void erase(iterator it);
5974 
5975 private:
5976  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5977 };
5978 
5979 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5980 
5981 template<typename FirstT, typename SecondT>
5982 struct VmaPairFirstLess
5983 {
5984  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5985  {
5986  return lhs.first < rhs.first;
5987  }
5988  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5989  {
5990  return lhs.first < rhsFirst;
5991  }
5992 };
5993 
5994 template<typename KeyT, typename ValueT>
5995 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5996 {
5997  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5998  m_Vector.data(),
5999  m_Vector.data() + m_Vector.size(),
6000  pair,
6001  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
6002  VmaVectorInsert(m_Vector, indexToInsert, pair);
6003 }
6004 
6005 template<typename KeyT, typename ValueT>
6006 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
6007 {
6008  PairType* it = VmaBinaryFindFirstNotLess(
6009  m_Vector.data(),
6010  m_Vector.data() + m_Vector.size(),
6011  key,
6012  VmaPairFirstLess<KeyT, ValueT>());
6013  if((it != m_Vector.end()) && (it->first == key))
6014  {
6015  return it;
6016  }
6017  else
6018  {
6019  return m_Vector.end();
6020  }
6021 }
6022 
6023 template<typename KeyT, typename ValueT>
6024 void VmaMap<KeyT, ValueT>::erase(iterator it)
6025 {
6026  VmaVectorRemove(m_Vector, it - m_Vector.begin());
6027 }
6028 
6029 #endif // #if VMA_USE_STL_UNORDERED_MAP
6030 
6031 #endif // #if 0
6032 
6034 
6035 class VmaDeviceMemoryBlock;
6036 
6037 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
6038 
6039 struct VmaAllocation_T
6040 {
6041 private:
6042  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
6043 
6044  enum FLAGS
6045  {
6046  FLAG_USER_DATA_STRING = 0x01,
6047  };
6048 
6049 public:
6050  enum ALLOCATION_TYPE
6051  {
6052  ALLOCATION_TYPE_NONE,
6053  ALLOCATION_TYPE_BLOCK,
6054  ALLOCATION_TYPE_DEDICATED,
6055  };
6056 
6057  /*
6058  This struct is allocated using VmaPoolAllocator.
6059  */
6060 
6061  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
6062  m_Alignment{1},
6063  m_Size{0},
6064  m_pUserData{VMA_NULL},
6065  m_LastUseFrameIndex{currentFrameIndex},
6066  m_MemoryTypeIndex{0},
6067  m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
6068  m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
6069  m_MapCount{0},
6070  m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
6071  {
6072 #if VMA_STATS_STRING_ENABLED
6073  m_CreationFrameIndex = currentFrameIndex;
6074  m_BufferImageUsage = 0;
6075 #endif
6076  }
6077 
6078  ~VmaAllocation_T()
6079  {
6080  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
6081 
6082  // Check if owned string was freed.
6083  VMA_ASSERT(m_pUserData == VMA_NULL);
6084  }
6085 
6086  void InitBlockAllocation(
6087  VmaDeviceMemoryBlock* block,
6088  VkDeviceSize offset,
6089  VkDeviceSize alignment,
6090  VkDeviceSize size,
6091  uint32_t memoryTypeIndex,
6092  VmaSuballocationType suballocationType,
6093  bool mapped,
6094  bool canBecomeLost)
6095  {
6096  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6097  VMA_ASSERT(block != VMA_NULL);
6098  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6099  m_Alignment = alignment;
6100  m_Size = size;
6101  m_MemoryTypeIndex = memoryTypeIndex;
6102  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6103  m_SuballocationType = (uint8_t)suballocationType;
6104  m_BlockAllocation.m_Block = block;
6105  m_BlockAllocation.m_Offset = offset;
6106  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6107  }
6108 
6109  void InitLost()
6110  {
6111  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6112  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6113  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6114  m_MemoryTypeIndex = 0;
6115  m_BlockAllocation.m_Block = VMA_NULL;
6116  m_BlockAllocation.m_Offset = 0;
6117  m_BlockAllocation.m_CanBecomeLost = true;
6118  }
6119 
6120  void ChangeBlockAllocation(
6121  VmaAllocator hAllocator,
6122  VmaDeviceMemoryBlock* block,
6123  VkDeviceSize offset);
6124 
6125  void ChangeOffset(VkDeviceSize newOffset);
6126 
6127  // pMappedData not null means allocation is created with MAPPED flag.
6128  void InitDedicatedAllocation(
6129  uint32_t memoryTypeIndex,
6130  VkDeviceMemory hMemory,
6131  VmaSuballocationType suballocationType,
6132  void* pMappedData,
6133  VkDeviceSize size)
6134  {
6135  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6136  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6137  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6138  m_Alignment = 0;
6139  m_Size = size;
6140  m_MemoryTypeIndex = memoryTypeIndex;
6141  m_SuballocationType = (uint8_t)suballocationType;
6142  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6143  m_DedicatedAllocation.m_hMemory = hMemory;
6144  m_DedicatedAllocation.m_pMappedData = pMappedData;
6145  }
6146 
6147  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6148  VkDeviceSize GetAlignment() const { return m_Alignment; }
6149  VkDeviceSize GetSize() const { return m_Size; }
6150  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6151  void* GetUserData() const { return m_pUserData; }
6152  void SetUserData(VmaAllocator hAllocator, void* pUserData);
6153  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6154 
6155  VmaDeviceMemoryBlock* GetBlock() const
6156  {
6157  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6158  return m_BlockAllocation.m_Block;
6159  }
6160  VkDeviceSize GetOffset() const;
6161  VkDeviceMemory GetMemory() const;
6162  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6163  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6164  void* GetMappedData() const;
6165  bool CanBecomeLost() const;
6166 
6167  uint32_t GetLastUseFrameIndex() const
6168  {
6169  return m_LastUseFrameIndex.load();
6170  }
6171  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6172  {
6173  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6174  }
6175  /*
6176  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6177  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6178  - Else, returns false.
6179 
6180  If hAllocation is already lost, assert - you should not call it then.
6181  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6182  */
6183  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6184 
6185  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6186  {
6187  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6188  outInfo.blockCount = 1;
6189  outInfo.allocationCount = 1;
6190  outInfo.unusedRangeCount = 0;
6191  outInfo.usedBytes = m_Size;
6192  outInfo.unusedBytes = 0;
6193  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6194  outInfo.unusedRangeSizeMin = UINT64_MAX;
6195  outInfo.unusedRangeSizeMax = 0;
6196  }
6197 
6198  void BlockAllocMap();
6199  void BlockAllocUnmap();
6200  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6201  void DedicatedAllocUnmap(VmaAllocator hAllocator);
6202 
6203 #if VMA_STATS_STRING_ENABLED
6204  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6205  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6206 
6207  void InitBufferImageUsage(uint32_t bufferImageUsage)
6208  {
6209  VMA_ASSERT(m_BufferImageUsage == 0);
6210  m_BufferImageUsage = bufferImageUsage;
6211  }
6212 
6213  void PrintParameters(class VmaJsonWriter& json) const;
6214 #endif
6215 
6216 private:
6217  VkDeviceSize m_Alignment;
6218  VkDeviceSize m_Size;
6219  void* m_pUserData;
6220  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6221  uint32_t m_MemoryTypeIndex;
6222  uint8_t m_Type; // ALLOCATION_TYPE
6223  uint8_t m_SuballocationType; // VmaSuballocationType
6224  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6225  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6226  uint8_t m_MapCount;
6227  uint8_t m_Flags; // enum FLAGS
6228 
6229  // Allocation out of VmaDeviceMemoryBlock.
6230  struct BlockAllocation
6231  {
6232  VmaDeviceMemoryBlock* m_Block;
6233  VkDeviceSize m_Offset;
6234  bool m_CanBecomeLost;
6235  };
6236 
6237  // Allocation for an object that has its own private VkDeviceMemory.
6238  struct DedicatedAllocation
6239  {
6240  VkDeviceMemory m_hMemory;
6241  void* m_pMappedData; // Not null means memory is mapped.
6242  };
6243 
6244  union
6245  {
6246  // Allocation out of VmaDeviceMemoryBlock.
6247  BlockAllocation m_BlockAllocation;
6248  // Allocation for an object that has its own private VkDeviceMemory.
6249  DedicatedAllocation m_DedicatedAllocation;
6250  };
6251 
6252 #if VMA_STATS_STRING_ENABLED
6253  uint32_t m_CreationFrameIndex;
6254  uint32_t m_BufferImageUsage; // 0 if unknown.
6255 #endif
6256 
6257  void FreeUserDataString(VmaAllocator hAllocator);
6258 };
6259 
6260 /*
6261 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6262 allocated memory block or free.
6263 */
6264 struct VmaSuballocation
6265 {
6266  VkDeviceSize offset;
6267  VkDeviceSize size;
6268  VmaAllocation hAllocation;
6269  VmaSuballocationType type;
6270 };
6271 
6272 // Comparator for offsets.
6273 struct VmaSuballocationOffsetLess
6274 {
6275  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6276  {
6277  return lhs.offset < rhs.offset;
6278  }
6279 };
6280 struct VmaSuballocationOffsetGreater
6281 {
6282  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6283  {
6284  return lhs.offset > rhs.offset;
6285  }
6286 };
6287 
6288 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6289 
6290 // Cost of one additional allocation lost, as equivalent in bytes.
6291 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6292 
6293 enum class VmaAllocationRequestType
6294 {
6295  Normal,
6296  // Used by "Linear" algorithm.
6297  UpperAddress,
6298  EndOf1st,
6299  EndOf2nd,
6300 };
6301 
6302 /*
6303 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6304 
6305 If canMakeOtherLost was false:
6306 - item points to a FREE suballocation.
6307 - itemsToMakeLostCount is 0.
6308 
6309 If canMakeOtherLost was true:
6310 - item points to first of sequence of suballocations, which are either FREE,
6311  or point to VmaAllocations that can become lost.
6312 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6313  the requested allocation to succeed.
6314 */
6315 struct VmaAllocationRequest
6316 {
6317  VkDeviceSize offset;
6318  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6319  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6320  VmaSuballocationList::iterator item;
6321  size_t itemsToMakeLostCount;
6322  void* customData;
6323  VmaAllocationRequestType type;
6324 
6325  VkDeviceSize CalcCost() const
6326  {
6327  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6328  }
6329 };
6330 
6331 /*
6332 Data structure used for bookkeeping of allocations and unused ranges of memory
6333 in a single VkDeviceMemory block.
6334 */
6335 class VmaBlockMetadata
6336 {
6337 public:
6338  VmaBlockMetadata(VmaAllocator hAllocator);
6339  virtual ~VmaBlockMetadata() { }
6340  virtual void Init(VkDeviceSize size) { m_Size = size; }
6341 
6342  // Validates all data structures inside this object. If not valid, returns false.
6343  virtual bool Validate() const = 0;
6344  VkDeviceSize GetSize() const { return m_Size; }
6345  virtual size_t GetAllocationCount() const = 0;
6346  virtual VkDeviceSize GetSumFreeSize() const = 0;
6347  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6348  // Returns true if this block is empty - contains only single free suballocation.
6349  virtual bool IsEmpty() const = 0;
6350 
6351  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6352  // Shouldn't modify blockCount.
6353  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6354 
6355 #if VMA_STATS_STRING_ENABLED
6356  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6357 #endif
6358 
6359  // Tries to find a place for suballocation with given parameters inside this block.
6360  // If succeeded, fills pAllocationRequest and returns true.
6361  // If failed, returns false.
6362  virtual bool CreateAllocationRequest(
6363  uint32_t currentFrameIndex,
6364  uint32_t frameInUseCount,
6365  VkDeviceSize bufferImageGranularity,
6366  VkDeviceSize allocSize,
6367  VkDeviceSize allocAlignment,
6368  bool upperAddress,
6369  VmaSuballocationType allocType,
6370  bool canMakeOtherLost,
6371  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6372  uint32_t strategy,
6373  VmaAllocationRequest* pAllocationRequest) = 0;
6374 
6375  virtual bool MakeRequestedAllocationsLost(
6376  uint32_t currentFrameIndex,
6377  uint32_t frameInUseCount,
6378  VmaAllocationRequest* pAllocationRequest) = 0;
6379 
6380  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6381 
6382  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6383 
6384  // Makes actual allocation based on request. Request must already be checked and valid.
6385  virtual void Alloc(
6386  const VmaAllocationRequest& request,
6387  VmaSuballocationType type,
6388  VkDeviceSize allocSize,
6389  VmaAllocation hAllocation) = 0;
6390 
6391  // Frees suballocation assigned to given memory region.
6392  virtual void Free(const VmaAllocation allocation) = 0;
6393  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6394 
6395 protected:
6396  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6397 
6398 #if VMA_STATS_STRING_ENABLED
6399  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6400  VkDeviceSize unusedBytes,
6401  size_t allocationCount,
6402  size_t unusedRangeCount) const;
6403  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6404  VkDeviceSize offset,
6405  VmaAllocation hAllocation) const;
6406  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6407  VkDeviceSize offset,
6408  VkDeviceSize size) const;
6409  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6410 #endif
6411 
6412 private:
6413  VkDeviceSize m_Size;
6414  const VkAllocationCallbacks* m_pAllocationCallbacks;
6415 };
6416 
6417 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6418  VMA_ASSERT(0 && "Validation failed: " #cond); \
6419  return false; \
6420  } } while(false)
6421 
6422 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6423 {
6424  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6425 public:
6426  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6427  virtual ~VmaBlockMetadata_Generic();
6428  virtual void Init(VkDeviceSize size);
6429 
6430  virtual bool Validate() const;
6431  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6432  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6433  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6434  virtual bool IsEmpty() const;
6435 
6436  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6437  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6438 
6439 #if VMA_STATS_STRING_ENABLED
6440  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6441 #endif
6442 
6443  virtual bool CreateAllocationRequest(
6444  uint32_t currentFrameIndex,
6445  uint32_t frameInUseCount,
6446  VkDeviceSize bufferImageGranularity,
6447  VkDeviceSize allocSize,
6448  VkDeviceSize allocAlignment,
6449  bool upperAddress,
6450  VmaSuballocationType allocType,
6451  bool canMakeOtherLost,
6452  uint32_t strategy,
6453  VmaAllocationRequest* pAllocationRequest);
6454 
6455  virtual bool MakeRequestedAllocationsLost(
6456  uint32_t currentFrameIndex,
6457  uint32_t frameInUseCount,
6458  VmaAllocationRequest* pAllocationRequest);
6459 
6460  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6461 
6462  virtual VkResult CheckCorruption(const void* pBlockData);
6463 
6464  virtual void Alloc(
6465  const VmaAllocationRequest& request,
6466  VmaSuballocationType type,
6467  VkDeviceSize allocSize,
6468  VmaAllocation hAllocation);
6469 
6470  virtual void Free(const VmaAllocation allocation);
6471  virtual void FreeAtOffset(VkDeviceSize offset);
6472 
6474  // For defragmentation
6475 
6476  bool IsBufferImageGranularityConflictPossible(
6477  VkDeviceSize bufferImageGranularity,
6478  VmaSuballocationType& inOutPrevSuballocType) const;
6479 
6480 private:
6481  friend class VmaDefragmentationAlgorithm_Generic;
6482  friend class VmaDefragmentationAlgorithm_Fast;
6483 
6484  uint32_t m_FreeCount;
6485  VkDeviceSize m_SumFreeSize;
6486  VmaSuballocationList m_Suballocations;
6487  // Suballocations that are free and have size greater than certain threshold.
6488  // Sorted by size, ascending.
6489  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6490 
6491  bool ValidateFreeSuballocationList() const;
6492 
6493  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6494  // If yes, fills pOffset and returns true. If no, returns false.
6495  bool CheckAllocation(
6496  uint32_t currentFrameIndex,
6497  uint32_t frameInUseCount,
6498  VkDeviceSize bufferImageGranularity,
6499  VkDeviceSize allocSize,
6500  VkDeviceSize allocAlignment,
6501  VmaSuballocationType allocType,
6502  VmaSuballocationList::const_iterator suballocItem,
6503  bool canMakeOtherLost,
6504  VkDeviceSize* pOffset,
6505  size_t* itemsToMakeLostCount,
6506  VkDeviceSize* pSumFreeSize,
6507  VkDeviceSize* pSumItemSize) const;
6508  // Given free suballocation, it merges it with following one, which must also be free.
6509  void MergeFreeWithNext(VmaSuballocationList::iterator item);
6510  // Releases given suballocation, making it free.
6511  // Merges it with adjacent free suballocations if applicable.
6512  // Returns iterator to new free suballocation at this place.
6513  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6514  // Given free suballocation, it inserts it into sorted list of
6515  // m_FreeSuballocationsBySize if it's suitable.
6516  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6517  // Given free suballocation, it removes it from sorted list of
6518  // m_FreeSuballocationsBySize if it's suitable.
6519  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6520 };
6521 
6522 /*
6523 Allocations and their references in internal data structure look like this:
6524 
6525 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6526 
6527  0 +-------+
6528  | |
6529  | |
6530  | |
6531  +-------+
6532  | Alloc | 1st[m_1stNullItemsBeginCount]
6533  +-------+
6534  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6535  +-------+
6536  | ... |
6537  +-------+
6538  | Alloc | 1st[1st.size() - 1]
6539  +-------+
6540  | |
6541  | |
6542  | |
6543 GetSize() +-------+
6544 
6545 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6546 
6547  0 +-------+
6548  | Alloc | 2nd[0]
6549  +-------+
6550  | Alloc | 2nd[1]
6551  +-------+
6552  | ... |
6553  +-------+
6554  | Alloc | 2nd[2nd.size() - 1]
6555  +-------+
6556  | |
6557  | |
6558  | |
6559  +-------+
6560  | Alloc | 1st[m_1stNullItemsBeginCount]
6561  +-------+
6562  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6563  +-------+
6564  | ... |
6565  +-------+
6566  | Alloc | 1st[1st.size() - 1]
6567  +-------+
6568  | |
6569 GetSize() +-------+
6570 
6571 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6572 
6573  0 +-------+
6574  | |
6575  | |
6576  | |
6577  +-------+
6578  | Alloc | 1st[m_1stNullItemsBeginCount]
6579  +-------+
6580  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6581  +-------+
6582  | ... |
6583  +-------+
6584  | Alloc | 1st[1st.size() - 1]
6585  +-------+
6586  | |
6587  | |
6588  | |
6589  +-------+
6590  | Alloc | 2nd[2nd.size() - 1]
6591  +-------+
6592  | ... |
6593  +-------+
6594  | Alloc | 2nd[1]
6595  +-------+
6596  | Alloc | 2nd[0]
6597 GetSize() +-------+
6598 
6599 */
6600 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6601 {
6602  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6603 public:
6604  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6605  virtual ~VmaBlockMetadata_Linear();
6606  virtual void Init(VkDeviceSize size);
6607 
6608  virtual bool Validate() const;
6609  virtual size_t GetAllocationCount() const;
6610  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6611  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6612  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6613 
6614  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6615  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6616 
6617 #if VMA_STATS_STRING_ENABLED
6618  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6619 #endif
6620 
6621  virtual bool CreateAllocationRequest(
6622  uint32_t currentFrameIndex,
6623  uint32_t frameInUseCount,
6624  VkDeviceSize bufferImageGranularity,
6625  VkDeviceSize allocSize,
6626  VkDeviceSize allocAlignment,
6627  bool upperAddress,
6628  VmaSuballocationType allocType,
6629  bool canMakeOtherLost,
6630  uint32_t strategy,
6631  VmaAllocationRequest* pAllocationRequest);
6632 
6633  virtual bool MakeRequestedAllocationsLost(
6634  uint32_t currentFrameIndex,
6635  uint32_t frameInUseCount,
6636  VmaAllocationRequest* pAllocationRequest);
6637 
6638  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6639 
6640  virtual VkResult CheckCorruption(const void* pBlockData);
6641 
6642  virtual void Alloc(
6643  const VmaAllocationRequest& request,
6644  VmaSuballocationType type,
6645  VkDeviceSize allocSize,
6646  VmaAllocation hAllocation);
6647 
6648  virtual void Free(const VmaAllocation allocation);
6649  virtual void FreeAtOffset(VkDeviceSize offset);
6650 
6651 private:
6652  /*
6653  There are two suballocation vectors, used in ping-pong way.
6654  The one with index m_1stVectorIndex is called 1st.
6655  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6656  2nd can be non-empty only when 1st is not empty.
6657  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6658  */
6659  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6660 
6661  enum SECOND_VECTOR_MODE
6662  {
6663  SECOND_VECTOR_EMPTY,
6664  /*
6665  Suballocations in 2nd vector are created later than the ones in 1st, but they
6666  all have smaller offset.
6667  */
6668  SECOND_VECTOR_RING_BUFFER,
6669  /*
6670  Suballocations in 2nd vector are upper side of double stack.
6671  They all have offsets higher than those in 1st vector.
6672  Top of this stack means smaller offsets, but higher indices in this vector.
6673  */
6674  SECOND_VECTOR_DOUBLE_STACK,
6675  };
6676 
6677  VkDeviceSize m_SumFreeSize;
6678  SuballocationVectorType m_Suballocations0, m_Suballocations1;
6679  uint32_t m_1stVectorIndex;
6680  SECOND_VECTOR_MODE m_2ndVectorMode;
6681 
6682  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6683  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6684  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6685  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6686 
6687  // Number of items in 1st vector with hAllocation = null at the beginning.
6688  size_t m_1stNullItemsBeginCount;
6689  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6690  size_t m_1stNullItemsMiddleCount;
6691  // Number of items in 2nd vector with hAllocation = null.
6692  size_t m_2ndNullItemsCount;
6693 
6694  bool ShouldCompact1st() const;
6695  void CleanupAfterFree();
6696 
6697  bool CreateAllocationRequest_LowerAddress(
6698  uint32_t currentFrameIndex,
6699  uint32_t frameInUseCount,
6700  VkDeviceSize bufferImageGranularity,
6701  VkDeviceSize allocSize,
6702  VkDeviceSize allocAlignment,
6703  VmaSuballocationType allocType,
6704  bool canMakeOtherLost,
6705  uint32_t strategy,
6706  VmaAllocationRequest* pAllocationRequest);
6707  bool CreateAllocationRequest_UpperAddress(
6708  uint32_t currentFrameIndex,
6709  uint32_t frameInUseCount,
6710  VkDeviceSize bufferImageGranularity,
6711  VkDeviceSize allocSize,
6712  VkDeviceSize allocAlignment,
6713  VmaSuballocationType allocType,
6714  bool canMakeOtherLost,
6715  uint32_t strategy,
6716  VmaAllocationRequest* pAllocationRequest);
6717 };
6718 
6719 /*
6720 - GetSize() is the original size of allocated memory block.
6721 - m_UsableSize is this size aligned down to a power of two.
6722  All allocations and calculations happen relative to m_UsableSize.
6723 - GetUnusableSize() is the difference between them.
6724  It is repoted as separate, unused range, not available for allocations.
6725 
6726 Node at level 0 has size = m_UsableSize.
6727 Each next level contains nodes with size 2 times smaller than current level.
6728 m_LevelCount is the maximum number of levels to use in the current object.
6729 */
6730 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6731 {
6732  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6733 public:
6734  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6735  virtual ~VmaBlockMetadata_Buddy();
6736  virtual void Init(VkDeviceSize size);
6737 
6738  virtual bool Validate() const;
6739  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
6740  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6741  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6742  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6743 
6744  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6745  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6746 
6747 #if VMA_STATS_STRING_ENABLED
6748  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6749 #endif
6750 
6751  virtual bool CreateAllocationRequest(
6752  uint32_t currentFrameIndex,
6753  uint32_t frameInUseCount,
6754  VkDeviceSize bufferImageGranularity,
6755  VkDeviceSize allocSize,
6756  VkDeviceSize allocAlignment,
6757  bool upperAddress,
6758  VmaSuballocationType allocType,
6759  bool canMakeOtherLost,
6760  uint32_t strategy,
6761  VmaAllocationRequest* pAllocationRequest);
6762 
6763  virtual bool MakeRequestedAllocationsLost(
6764  uint32_t currentFrameIndex,
6765  uint32_t frameInUseCount,
6766  VmaAllocationRequest* pAllocationRequest);
6767 
6768  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6769 
6770  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6771 
6772  virtual void Alloc(
6773  const VmaAllocationRequest& request,
6774  VmaSuballocationType type,
6775  VkDeviceSize allocSize,
6776  VmaAllocation hAllocation);
6777 
6778  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
6779  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6780 
6781 private:
6782  static const VkDeviceSize MIN_NODE_SIZE = 32;
6783  static const size_t MAX_LEVELS = 30;
6784 
6785  struct ValidationContext
6786  {
6787  size_t calculatedAllocationCount;
6788  size_t calculatedFreeCount;
6789  VkDeviceSize calculatedSumFreeSize;
6790 
6791  ValidationContext() :
6792  calculatedAllocationCount(0),
6793  calculatedFreeCount(0),
6794  calculatedSumFreeSize(0) { }
6795  };
6796 
6797  struct Node
6798  {
6799  VkDeviceSize offset;
6800  enum TYPE
6801  {
6802  TYPE_FREE,
6803  TYPE_ALLOCATION,
6804  TYPE_SPLIT,
6805  TYPE_COUNT
6806  } type;
6807  Node* parent;
6808  Node* buddy;
6809 
6810  union
6811  {
6812  struct
6813  {
6814  Node* prev;
6815  Node* next;
6816  } free;
6817  struct
6818  {
6819  VmaAllocation alloc;
6820  } allocation;
6821  struct
6822  {
6823  Node* leftChild;
6824  } split;
6825  };
6826  };
6827 
6828  // Size of the memory block aligned down to a power of two.
6829  VkDeviceSize m_UsableSize;
6830  uint32_t m_LevelCount;
6831 
6832  Node* m_Root;
6833  struct {
6834  Node* front;
6835  Node* back;
6836  } m_FreeList[MAX_LEVELS];
6837  // Number of nodes in the tree with type == TYPE_ALLOCATION.
6838  size_t m_AllocationCount;
6839  // Number of nodes in the tree with type == TYPE_FREE.
6840  size_t m_FreeCount;
6841  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6842  VkDeviceSize m_SumFreeSize;
6843 
6844  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6845  void DeleteNode(Node* node);
6846  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6847  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
6848  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6849  // Alloc passed just for validation. Can be null.
6850  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6851  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6852  // Adds node to the front of FreeList at given level.
6853  // node->type must be FREE.
6854  // node->free.prev, next can be undefined.
6855  void AddToFreeListFront(uint32_t level, Node* node);
6856  // Removes node from FreeList at given level.
6857  // node->type must be FREE.
6858  // node->free.prev, next stay untouched.
6859  void RemoveFromFreeList(uint32_t level, Node* node);
6860 
6861 #if VMA_STATS_STRING_ENABLED
6862  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6863 #endif
6864 };
6865 
6866 /*
6867 Represents a single block of device memory (`VkDeviceMemory`) with all the
6868 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6869 
6870 Thread-safety: This class must be externally synchronized.
6871 */
6872 class VmaDeviceMemoryBlock
6873 {
6874  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6875 public:
6876  VmaBlockMetadata* m_pMetadata;
6877 
6878  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6879 
6880  ~VmaDeviceMemoryBlock()
6881  {
6882  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6883  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6884  }
6885 
6886  // Always call after construction.
6887  void Init(
6888  VmaAllocator hAllocator,
6889  VmaPool hParentPool,
6890  uint32_t newMemoryTypeIndex,
6891  VkDeviceMemory newMemory,
6892  VkDeviceSize newSize,
6893  uint32_t id,
6894  uint32_t algorithm);
6895  // Always call before destruction.
6896  void Destroy(VmaAllocator allocator);
6897 
6898  VmaPool GetParentPool() const { return m_hParentPool; }
6899  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
6900  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6901  uint32_t GetId() const { return m_Id; }
6902  void* GetMappedData() const { return m_pMappedData; }
6903 
6904  // Validates all data structures inside this object. If not valid, returns false.
6905  bool Validate() const;
6906 
6907  VkResult CheckCorruption(VmaAllocator hAllocator);
6908 
6909  // ppData can be null.
6910  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6911  void Unmap(VmaAllocator hAllocator, uint32_t count);
6912 
6913  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6914  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6915 
6916  VkResult BindBufferMemory(
6917  const VmaAllocator hAllocator,
6918  const VmaAllocation hAllocation,
6919  VkDeviceSize allocationLocalOffset,
6920  VkBuffer hBuffer,
6921  const void* pNext);
6922  VkResult BindImageMemory(
6923  const VmaAllocator hAllocator,
6924  const VmaAllocation hAllocation,
6925  VkDeviceSize allocationLocalOffset,
6926  VkImage hImage,
6927  const void* pNext);
6928 
6929 private:
6930  VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6931  uint32_t m_MemoryTypeIndex;
6932  uint32_t m_Id;
6933  VkDeviceMemory m_hMemory;
6934 
6935  /*
6936  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6937  Also protects m_MapCount, m_pMappedData.
6938  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6939  */
6940  VMA_MUTEX m_Mutex;
6941  uint32_t m_MapCount;
6942  void* m_pMappedData;
6943 };
6944 
6945 struct VmaPointerLess
6946 {
6947  bool operator()(const void* lhs, const void* rhs) const
6948  {
6949  return lhs < rhs;
6950  }
6951 };
6952 
6953 struct VmaDefragmentationMove
6954 {
6955  size_t srcBlockIndex;
6956  size_t dstBlockIndex;
6957  VkDeviceSize srcOffset;
6958  VkDeviceSize dstOffset;
6959  VkDeviceSize size;
6960  VmaAllocation hAllocation;
6961  VmaDeviceMemoryBlock* pSrcBlock;
6962  VmaDeviceMemoryBlock* pDstBlock;
6963 };
6964 
6965 class VmaDefragmentationAlgorithm;
6966 
6967 /*
6968 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6969 Vulkan memory type.
6970 
6971 Synchronized internally with a mutex.
6972 */
6973 struct VmaBlockVector
6974 {
6975  VMA_CLASS_NO_COPY(VmaBlockVector)
6976 public:
6977  VmaBlockVector(
6978  VmaAllocator hAllocator,
6979  VmaPool hParentPool,
6980  uint32_t memoryTypeIndex,
6981  VkDeviceSize preferredBlockSize,
6982  size_t minBlockCount,
6983  size_t maxBlockCount,
6984  VkDeviceSize bufferImageGranularity,
6985  uint32_t frameInUseCount,
6986  bool explicitBlockSize,
6987  uint32_t algorithm);
6988  ~VmaBlockVector();
6989 
6990  VkResult CreateMinBlocks();
6991 
6992  VmaAllocator GetAllocator() const { return m_hAllocator; }
6993  VmaPool GetParentPool() const { return m_hParentPool; }
6994  bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
6995  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6996  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
6997  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
6998  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
6999  uint32_t GetAlgorithm() const { return m_Algorithm; }
7000 
7001  void GetPoolStats(VmaPoolStats* pStats);
7002 
7003  bool IsEmpty();
7004  bool IsCorruptionDetectionEnabled() const;
7005 
7006  VkResult Allocate(
7007  uint32_t currentFrameIndex,
7008  VkDeviceSize size,
7009  VkDeviceSize alignment,
7010  const VmaAllocationCreateInfo& createInfo,
7011  VmaSuballocationType suballocType,
7012  size_t allocationCount,
7013  VmaAllocation* pAllocations);
7014 
7015  void Free(const VmaAllocation hAllocation);
7016 
7017  // Adds statistics of this BlockVector to pStats.
7018  void AddStats(VmaStats* pStats);
7019 
7020 #if VMA_STATS_STRING_ENABLED
7021  void PrintDetailedMap(class VmaJsonWriter& json);
7022 #endif
7023 
7024  void MakePoolAllocationsLost(
7025  uint32_t currentFrameIndex,
7026  size_t* pLostAllocationCount);
7027  VkResult CheckCorruption();
7028 
7029  // Saves results in pCtx->res.
7030  void Defragment(
7031  class VmaBlockVectorDefragmentationContext* pCtx,
7033  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
7034  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
7035  VkCommandBuffer commandBuffer);
7036  void DefragmentationEnd(
7037  class VmaBlockVectorDefragmentationContext* pCtx,
7038  uint32_t flags,
7039  VmaDefragmentationStats* pStats);
7040 
7041  uint32_t ProcessDefragmentations(
7042  class VmaBlockVectorDefragmentationContext *pCtx,
7043  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
7044 
7045  void CommitDefragmentations(
7046  class VmaBlockVectorDefragmentationContext *pCtx,
7047  VmaDefragmentationStats* pStats);
7048 
7050  // To be used only while the m_Mutex is locked. Used during defragmentation.
7051 
7052  size_t GetBlockCount() const { return m_Blocks.size(); }
7053  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
7054  size_t CalcAllocationCount() const;
7055  bool IsBufferImageGranularityConflictPossible() const;
7056 
7057 private:
7058  friend class VmaDefragmentationAlgorithm_Generic;
7059 
7060  const VmaAllocator m_hAllocator;
7061  const VmaPool m_hParentPool;
7062  const uint32_t m_MemoryTypeIndex;
7063  const VkDeviceSize m_PreferredBlockSize;
7064  const size_t m_MinBlockCount;
7065  const size_t m_MaxBlockCount;
7066  const VkDeviceSize m_BufferImageGranularity;
7067  const uint32_t m_FrameInUseCount;
7068  const bool m_ExplicitBlockSize;
7069  const uint32_t m_Algorithm;
7070  VMA_RW_MUTEX m_Mutex;
7071 
7072  /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7073  a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7074  bool m_HasEmptyBlock;
7075  // Incrementally sorted by sumFreeSize, ascending.
7076  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7077  uint32_t m_NextBlockId;
7078 
7079  VkDeviceSize CalcMaxBlockSize() const;
7080 
7081  // Finds and removes given block from vector.
7082  void Remove(VmaDeviceMemoryBlock* pBlock);
7083 
7084  // Performs single step in sorting m_Blocks. They may not be fully sorted
7085  // after this call.
7086  void IncrementallySortBlocks();
7087 
7088  VkResult AllocatePage(
7089  uint32_t currentFrameIndex,
7090  VkDeviceSize size,
7091  VkDeviceSize alignment,
7092  const VmaAllocationCreateInfo& createInfo,
7093  VmaSuballocationType suballocType,
7094  VmaAllocation* pAllocation);
7095 
7096  // To be used only without CAN_MAKE_OTHER_LOST flag.
7097  VkResult AllocateFromBlock(
7098  VmaDeviceMemoryBlock* pBlock,
7099  uint32_t currentFrameIndex,
7100  VkDeviceSize size,
7101  VkDeviceSize alignment,
7102  VmaAllocationCreateFlags allocFlags,
7103  void* pUserData,
7104  VmaSuballocationType suballocType,
7105  uint32_t strategy,
7106  VmaAllocation* pAllocation);
7107 
7108  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7109 
7110  // Saves result to pCtx->res.
7111  void ApplyDefragmentationMovesCpu(
7112  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7113  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7114  // Saves result to pCtx->res.
7115  void ApplyDefragmentationMovesGpu(
7116  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7117  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7118  VkCommandBuffer commandBuffer);
7119 
7120  /*
7121  Used during defragmentation. pDefragmentationStats is optional. It's in/out
7122  - updated with new data.
7123  */
7124  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7125 
7126  void UpdateHasEmptyBlock();
7127 };
7128 
7129 struct VmaPool_T
7130 {
7131  VMA_CLASS_NO_COPY(VmaPool_T)
7132 public:
7133  VmaBlockVector m_BlockVector;
7134 
7135  VmaPool_T(
7136  VmaAllocator hAllocator,
7137  const VmaPoolCreateInfo& createInfo,
7138  VkDeviceSize preferredBlockSize);
7139  ~VmaPool_T();
7140 
7141  uint32_t GetId() const { return m_Id; }
7142  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7143 
7144  const char* GetName() const { return m_Name; }
7145  void SetName(const char* pName);
7146 
7147 #if VMA_STATS_STRING_ENABLED
7148  //void PrintDetailedMap(class VmaStringBuilder& sb);
7149 #endif
7150 
7151 private:
7152  uint32_t m_Id;
7153  char* m_Name;
7154 };
7155 
7156 /*
7157 Performs defragmentation:
7158 
7159 - Updates `pBlockVector->m_pMetadata`.
7160 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7161 - Does not move actual data, only returns requested moves as `moves`.
7162 */
7163 class VmaDefragmentationAlgorithm
7164 {
7165  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7166 public:
7167  VmaDefragmentationAlgorithm(
7168  VmaAllocator hAllocator,
7169  VmaBlockVector* pBlockVector,
7170  uint32_t currentFrameIndex) :
7171  m_hAllocator(hAllocator),
7172  m_pBlockVector(pBlockVector),
7173  m_CurrentFrameIndex(currentFrameIndex)
7174  {
7175  }
7176  virtual ~VmaDefragmentationAlgorithm()
7177  {
7178  }
7179 
7180  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7181  virtual void AddAll() = 0;
7182 
7183  virtual VkResult Defragment(
7184  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7185  VkDeviceSize maxBytesToMove,
7186  uint32_t maxAllocationsToMove,
7187  VmaDefragmentationFlags flags) = 0;
7188 
7189  virtual VkDeviceSize GetBytesMoved() const = 0;
7190  virtual uint32_t GetAllocationsMoved() const = 0;
7191 
7192 protected:
7193  VmaAllocator const m_hAllocator;
7194  VmaBlockVector* const m_pBlockVector;
7195  const uint32_t m_CurrentFrameIndex;
7196 
7197  struct AllocationInfo
7198  {
7199  VmaAllocation m_hAllocation;
7200  VkBool32* m_pChanged;
7201 
7202  AllocationInfo() :
7203  m_hAllocation(VK_NULL_HANDLE),
7204  m_pChanged(VMA_NULL)
7205  {
7206  }
7207  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7208  m_hAllocation(hAlloc),
7209  m_pChanged(pChanged)
7210  {
7211  }
7212  };
7213 };
7214 
7215 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7216 {
7217  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7218 public:
7219  VmaDefragmentationAlgorithm_Generic(
7220  VmaAllocator hAllocator,
7221  VmaBlockVector* pBlockVector,
7222  uint32_t currentFrameIndex,
7223  bool overlappingMoveSupported);
7224  virtual ~VmaDefragmentationAlgorithm_Generic();
7225 
7226  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7227  virtual void AddAll() { m_AllAllocations = true; }
7228 
7229  virtual VkResult Defragment(
7230  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7231  VkDeviceSize maxBytesToMove,
7232  uint32_t maxAllocationsToMove,
7233  VmaDefragmentationFlags flags);
7234 
7235  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7236  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7237 
7238 private:
7239  uint32_t m_AllocationCount;
7240  bool m_AllAllocations;
7241 
7242  VkDeviceSize m_BytesMoved;
7243  uint32_t m_AllocationsMoved;
7244 
7245  struct AllocationInfoSizeGreater
7246  {
7247  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7248  {
7249  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7250  }
7251  };
7252 
7253  struct AllocationInfoOffsetGreater
7254  {
7255  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7256  {
7257  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7258  }
7259  };
7260 
7261  struct BlockInfo
7262  {
7263  size_t m_OriginalBlockIndex;
7264  VmaDeviceMemoryBlock* m_pBlock;
7265  bool m_HasNonMovableAllocations;
7266  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7267 
7268  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7269  m_OriginalBlockIndex(SIZE_MAX),
7270  m_pBlock(VMA_NULL),
7271  m_HasNonMovableAllocations(true),
7272  m_Allocations(pAllocationCallbacks)
7273  {
7274  }
7275 
7276  void CalcHasNonMovableAllocations()
7277  {
7278  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7279  const size_t defragmentAllocCount = m_Allocations.size();
7280  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7281  }
7282 
7283  void SortAllocationsBySizeDescending()
7284  {
7285  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7286  }
7287 
7288  void SortAllocationsByOffsetDescending()
7289  {
7290  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7291  }
7292  };
7293 
7294  struct BlockPointerLess
7295  {
7296  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7297  {
7298  return pLhsBlockInfo->m_pBlock < pRhsBlock;
7299  }
7300  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7301  {
7302  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7303  }
7304  };
7305 
7306  // 1. Blocks with some non-movable allocations go first.
7307  // 2. Blocks with smaller sumFreeSize go first.
7308  struct BlockInfoCompareMoveDestination
7309  {
7310  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7311  {
7312  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7313  {
7314  return true;
7315  }
7316  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7317  {
7318  return false;
7319  }
7320  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7321  {
7322  return true;
7323  }
7324  return false;
7325  }
7326  };
7327 
7328  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7329  BlockInfoVector m_Blocks;
7330 
7331  VkResult DefragmentRound(
7332  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7333  VkDeviceSize maxBytesToMove,
7334  uint32_t maxAllocationsToMove,
7335  bool freeOldAllocations);
7336 
7337  size_t CalcBlocksWithNonMovableCount() const;
7338 
7339  static bool MoveMakesSense(
7340  size_t dstBlockIndex, VkDeviceSize dstOffset,
7341  size_t srcBlockIndex, VkDeviceSize srcOffset);
7342 };
7343 
7344 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7345 {
7346  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7347 public:
7348  VmaDefragmentationAlgorithm_Fast(
7349  VmaAllocator hAllocator,
7350  VmaBlockVector* pBlockVector,
7351  uint32_t currentFrameIndex,
7352  bool overlappingMoveSupported);
7353  virtual ~VmaDefragmentationAlgorithm_Fast();
7354 
7355  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7356  virtual void AddAll() { m_AllAllocations = true; }
7357 
7358  virtual VkResult Defragment(
7359  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7360  VkDeviceSize maxBytesToMove,
7361  uint32_t maxAllocationsToMove,
7362  VmaDefragmentationFlags flags);
7363 
7364  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7365  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7366 
7367 private:
7368  struct BlockInfo
7369  {
7370  size_t origBlockIndex;
7371  };
7372 
7373  class FreeSpaceDatabase
7374  {
7375  public:
7376  FreeSpaceDatabase()
7377  {
7378  FreeSpace s = {};
7379  s.blockInfoIndex = SIZE_MAX;
7380  for(size_t i = 0; i < MAX_COUNT; ++i)
7381  {
7382  m_FreeSpaces[i] = s;
7383  }
7384  }
7385 
7386  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7387  {
7388  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7389  {
7390  return;
7391  }
7392 
7393  // Find first invalid or the smallest structure.
7394  size_t bestIndex = SIZE_MAX;
7395  for(size_t i = 0; i < MAX_COUNT; ++i)
7396  {
7397  // Empty structure.
7398  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7399  {
7400  bestIndex = i;
7401  break;
7402  }
7403  if(m_FreeSpaces[i].size < size &&
7404  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7405  {
7406  bestIndex = i;
7407  }
7408  }
7409 
7410  if(bestIndex != SIZE_MAX)
7411  {
7412  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7413  m_FreeSpaces[bestIndex].offset = offset;
7414  m_FreeSpaces[bestIndex].size = size;
7415  }
7416  }
7417 
7418  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7419  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7420  {
7421  size_t bestIndex = SIZE_MAX;
7422  VkDeviceSize bestFreeSpaceAfter = 0;
7423  for(size_t i = 0; i < MAX_COUNT; ++i)
7424  {
7425  // Structure is valid.
7426  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7427  {
7428  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7429  // Allocation fits into this structure.
7430  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7431  {
7432  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7433  (dstOffset + size);
7434  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7435  {
7436  bestIndex = i;
7437  bestFreeSpaceAfter = freeSpaceAfter;
7438  }
7439  }
7440  }
7441  }
7442 
7443  if(bestIndex != SIZE_MAX)
7444  {
7445  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7446  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7447 
7448  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7449  {
7450  // Leave this structure for remaining empty space.
7451  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7452  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7453  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7454  }
7455  else
7456  {
7457  // This structure becomes invalid.
7458  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7459  }
7460 
7461  return true;
7462  }
7463 
7464  return false;
7465  }
7466 
7467  private:
7468  static const size_t MAX_COUNT = 4;
7469 
7470  struct FreeSpace
7471  {
7472  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7473  VkDeviceSize offset;
7474  VkDeviceSize size;
7475  } m_FreeSpaces[MAX_COUNT];
7476  };
7477 
7478  const bool m_OverlappingMoveSupported;
7479 
7480  uint32_t m_AllocationCount;
7481  bool m_AllAllocations;
7482 
7483  VkDeviceSize m_BytesMoved;
7484  uint32_t m_AllocationsMoved;
7485 
7486  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7487 
7488  void PreprocessMetadata();
7489  void PostprocessMetadata();
7490  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7491 };
7492 
7493 struct VmaBlockDefragmentationContext
7494 {
7495  enum BLOCK_FLAG
7496  {
7497  BLOCK_FLAG_USED = 0x00000001,
7498  };
7499  uint32_t flags;
7500  VkBuffer hBuffer;
7501 };
7502 
7503 class VmaBlockVectorDefragmentationContext
7504 {
7505  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7506 public:
7507  VkResult res;
7508  bool mutexLocked;
7509  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7510  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7511  uint32_t defragmentationMovesProcessed;
7512  uint32_t defragmentationMovesCommitted;
7513  bool hasDefragmentationPlan;
7514 
7515  VmaBlockVectorDefragmentationContext(
7516  VmaAllocator hAllocator,
7517  VmaPool hCustomPool, // Optional.
7518  VmaBlockVector* pBlockVector,
7519  uint32_t currFrameIndex);
7520  ~VmaBlockVectorDefragmentationContext();
7521 
7522  VmaPool GetCustomPool() const { return m_hCustomPool; }
7523  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7524  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7525 
7526  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7527  void AddAll() { m_AllAllocations = true; }
7528 
7529  void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7530 
7531 private:
7532  const VmaAllocator m_hAllocator;
7533  // Null if not from custom pool.
7534  const VmaPool m_hCustomPool;
7535  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7536  VmaBlockVector* const m_pBlockVector;
7537  const uint32_t m_CurrFrameIndex;
7538  // Owner of this object.
7539  VmaDefragmentationAlgorithm* m_pAlgorithm;
7540 
7541  struct AllocInfo
7542  {
7543  VmaAllocation hAlloc;
7544  VkBool32* pChanged;
7545  };
7546  // Used between constructor and Begin.
7547  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7548  bool m_AllAllocations;
7549 };
7550 
7551 struct VmaDefragmentationContext_T
7552 {
7553 private:
7554  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7555 public:
7556  VmaDefragmentationContext_T(
7557  VmaAllocator hAllocator,
7558  uint32_t currFrameIndex,
7559  uint32_t flags,
7560  VmaDefragmentationStats* pStats);
7561  ~VmaDefragmentationContext_T();
7562 
7563  void AddPools(uint32_t poolCount, const VmaPool* pPools);
7564  void AddAllocations(
7565  uint32_t allocationCount,
7566  const VmaAllocation* pAllocations,
7567  VkBool32* pAllocationsChanged);
7568 
7569  /*
7570  Returns:
7571  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7572  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7573  - Negative value if error occured and object can be destroyed immediately.
7574  */
7575  VkResult Defragment(
7576  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7577  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7578  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7579 
7580  VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7581  VkResult DefragmentPassEnd();
7582 
7583 private:
7584  const VmaAllocator m_hAllocator;
7585  const uint32_t m_CurrFrameIndex;
7586  const uint32_t m_Flags;
7587  VmaDefragmentationStats* const m_pStats;
7588 
7589  VkDeviceSize m_MaxCpuBytesToMove;
7590  uint32_t m_MaxCpuAllocationsToMove;
7591  VkDeviceSize m_MaxGpuBytesToMove;
7592  uint32_t m_MaxGpuAllocationsToMove;
7593 
7594  // Owner of these objects.
7595  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7596  // Owner of these objects.
7597  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7598 };
7599 
7600 #if VMA_RECORDING_ENABLED
7601 
7602 class VmaRecorder
7603 {
7604 public:
7605  VmaRecorder();
7606  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7607  void WriteConfiguration(
7608  const VkPhysicalDeviceProperties& devProps,
7609  const VkPhysicalDeviceMemoryProperties& memProps,
7610  uint32_t vulkanApiVersion,
7611  bool dedicatedAllocationExtensionEnabled,
7612  bool bindMemory2ExtensionEnabled,
7613  bool memoryBudgetExtensionEnabled,
7614  bool deviceCoherentMemoryExtensionEnabled);
7615  ~VmaRecorder();
7616 
7617  void RecordCreateAllocator(uint32_t frameIndex);
7618  void RecordDestroyAllocator(uint32_t frameIndex);
7619  void RecordCreatePool(uint32_t frameIndex,
7620  const VmaPoolCreateInfo& createInfo,
7621  VmaPool pool);
7622  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7623  void RecordAllocateMemory(uint32_t frameIndex,
7624  const VkMemoryRequirements& vkMemReq,
7625  const VmaAllocationCreateInfo& createInfo,
7626  VmaAllocation allocation);
7627  void RecordAllocateMemoryPages(uint32_t frameIndex,
7628  const VkMemoryRequirements& vkMemReq,
7629  const VmaAllocationCreateInfo& createInfo,
7630  uint64_t allocationCount,
7631  const VmaAllocation* pAllocations);
7632  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7633  const VkMemoryRequirements& vkMemReq,
7634  bool requiresDedicatedAllocation,
7635  bool prefersDedicatedAllocation,
7636  const VmaAllocationCreateInfo& createInfo,
7637  VmaAllocation allocation);
7638  void RecordAllocateMemoryForImage(uint32_t frameIndex,
7639  const VkMemoryRequirements& vkMemReq,
7640  bool requiresDedicatedAllocation,
7641  bool prefersDedicatedAllocation,
7642  const VmaAllocationCreateInfo& createInfo,
7643  VmaAllocation allocation);
7644  void RecordFreeMemory(uint32_t frameIndex,
7645  VmaAllocation allocation);
7646  void RecordFreeMemoryPages(uint32_t frameIndex,
7647  uint64_t allocationCount,
7648  const VmaAllocation* pAllocations);
7649  void RecordSetAllocationUserData(uint32_t frameIndex,
7650  VmaAllocation allocation,
7651  const void* pUserData);
7652  void RecordCreateLostAllocation(uint32_t frameIndex,
7653  VmaAllocation allocation);
7654  void RecordMapMemory(uint32_t frameIndex,
7655  VmaAllocation allocation);
7656  void RecordUnmapMemory(uint32_t frameIndex,
7657  VmaAllocation allocation);
7658  void RecordFlushAllocation(uint32_t frameIndex,
7659  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7660  void RecordInvalidateAllocation(uint32_t frameIndex,
7661  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7662  void RecordCreateBuffer(uint32_t frameIndex,
7663  const VkBufferCreateInfo& bufCreateInfo,
7664  const VmaAllocationCreateInfo& allocCreateInfo,
7665  VmaAllocation allocation);
7666  void RecordCreateImage(uint32_t frameIndex,
7667  const VkImageCreateInfo& imageCreateInfo,
7668  const VmaAllocationCreateInfo& allocCreateInfo,
7669  VmaAllocation allocation);
7670  void RecordDestroyBuffer(uint32_t frameIndex,
7671  VmaAllocation allocation);
7672  void RecordDestroyImage(uint32_t frameIndex,
7673  VmaAllocation allocation);
7674  void RecordTouchAllocation(uint32_t frameIndex,
7675  VmaAllocation allocation);
7676  void RecordGetAllocationInfo(uint32_t frameIndex,
7677  VmaAllocation allocation);
7678  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7679  VmaPool pool);
7680  void RecordDefragmentationBegin(uint32_t frameIndex,
7681  const VmaDefragmentationInfo2& info,
7683  void RecordDefragmentationEnd(uint32_t frameIndex,
7685  void RecordSetPoolName(uint32_t frameIndex,
7686  VmaPool pool,
7687  const char* name);
7688 
7689 private:
7690  struct CallParams
7691  {
7692  uint32_t threadId;
7693  double time;
7694  };
7695 
7696  class UserDataString
7697  {
7698  public:
7699  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
7700  const char* GetString() const { return m_Str; }
7701 
7702  private:
7703  char m_PtrStr[17];
7704  const char* m_Str;
7705  };
7706 
7707  bool m_UseMutex;
7708  VmaRecordFlags m_Flags;
7709  FILE* m_File;
7710  VMA_MUTEX m_FileMutex;
7711  std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7712 
7713  void GetBasicParams(CallParams& outParams);
7714 
7715  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7716  template<typename T>
7717  void PrintPointerList(uint64_t count, const T* pItems)
7718  {
7719  if(count)
7720  {
7721  fprintf(m_File, "%p", pItems[0]);
7722  for(uint64_t i = 1; i < count; ++i)
7723  {
7724  fprintf(m_File, " %p", pItems[i]);
7725  }
7726  }
7727  }
7728 
7729  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7730  void Flush();
7731 };
7732 
7733 #endif // #if VMA_RECORDING_ENABLED
7734 
7735 /*
7736 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7737 */
7738 class VmaAllocationObjectAllocator
7739 {
7740  VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7741 public:
7742  VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7743 
7744  template<typename... Types> VmaAllocation Allocate(Types... args);
7745  void Free(VmaAllocation hAlloc);
7746 
7747 private:
7748  VMA_MUTEX m_Mutex;
7749  VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7750 };
7751 
7752 struct VmaCurrentBudgetData
7753 {
7754  VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7755  VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7756 
7757 #if VMA_MEMORY_BUDGET
7758  VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7759  VMA_RW_MUTEX m_BudgetMutex;
7760  uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7761  uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7762  uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7763 #endif // #if VMA_MEMORY_BUDGET
7764 
7765  VmaCurrentBudgetData()
7766  {
7767  for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7768  {
7769  m_BlockBytes[heapIndex] = 0;
7770  m_AllocationBytes[heapIndex] = 0;
7771 #if VMA_MEMORY_BUDGET
7772  m_VulkanUsage[heapIndex] = 0;
7773  m_VulkanBudget[heapIndex] = 0;
7774  m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7775 #endif
7776  }
7777 
7778 #if VMA_MEMORY_BUDGET
7779  m_OperationsSinceBudgetFetch = 0;
7780 #endif
7781  }
7782 
7783  void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7784  {
7785  m_AllocationBytes[heapIndex] += allocationSize;
7786 #if VMA_MEMORY_BUDGET
7787  ++m_OperationsSinceBudgetFetch;
7788 #endif
7789  }
7790 
7791  void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7792  {
7793  VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7794  m_AllocationBytes[heapIndex] -= allocationSize;
7795 #if VMA_MEMORY_BUDGET
7796  ++m_OperationsSinceBudgetFetch;
7797 #endif
7798  }
7799 };
7800 
7801 // Main allocator object.
7802 struct VmaAllocator_T
7803 {
7804  VMA_CLASS_NO_COPY(VmaAllocator_T)
7805 public:
7806  bool m_UseMutex;
7807  uint32_t m_VulkanApiVersion;
7808  bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7809  bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7810  bool m_UseExtMemoryBudget;
7811  bool m_UseAmdDeviceCoherentMemory;
7812  bool m_UseKhrBufferDeviceAddress;
7813  VkDevice m_hDevice;
7814  VkInstance m_hInstance;
7815  bool m_AllocationCallbacksSpecified;
7816  VkAllocationCallbacks m_AllocationCallbacks;
7817  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7818  VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7819 
7820  // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7821  uint32_t m_HeapSizeLimitMask;
7822 
7823  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7824  VkPhysicalDeviceMemoryProperties m_MemProps;
7825 
7826  // Default pools.
7827  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7828 
7829  // Each vector is sorted by memory (handle value).
7830  typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7831  AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7832  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7833 
7834  VmaCurrentBudgetData m_Budget;
7835 
7836  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7837  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7838  ~VmaAllocator_T();
7839 
7840  const VkAllocationCallbacks* GetAllocationCallbacks() const
7841  {
7842  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7843  }
7844  const VmaVulkanFunctions& GetVulkanFunctions() const
7845  {
7846  return m_VulkanFunctions;
7847  }
7848 
7849  VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7850 
7851  VkDeviceSize GetBufferImageGranularity() const
7852  {
7853  return VMA_MAX(
7854  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7855  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7856  }
7857 
7858  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
7859  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7860 
7861  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7862  {
7863  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7864  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7865  }
7866  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
7867  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7868  {
7869  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7870  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7871  }
7872  // Minimum alignment for all allocations in specific memory type.
7873  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7874  {
7875  return IsMemoryTypeNonCoherent(memTypeIndex) ?
7876  VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7877  (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7878  }
7879 
7880  bool IsIntegratedGpu() const
7881  {
7882  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7883  }
7884 
7885  uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7886 
7887 #if VMA_RECORDING_ENABLED
7888  VmaRecorder* GetRecorder() const { return m_pRecorder; }
7889 #endif
7890 
7891  void GetBufferMemoryRequirements(
7892  VkBuffer hBuffer,
7893  VkMemoryRequirements& memReq,
7894  bool& requiresDedicatedAllocation,
7895  bool& prefersDedicatedAllocation) const;
7896  void GetImageMemoryRequirements(
7897  VkImage hImage,
7898  VkMemoryRequirements& memReq,
7899  bool& requiresDedicatedAllocation,
7900  bool& prefersDedicatedAllocation) const;
7901 
7902  // Main allocation function.
7903  VkResult AllocateMemory(
7904  const VkMemoryRequirements& vkMemReq,
7905  bool requiresDedicatedAllocation,
7906  bool prefersDedicatedAllocation,
7907  VkBuffer dedicatedBuffer,
7908  VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7909  VkImage dedicatedImage,
7910  const VmaAllocationCreateInfo& createInfo,
7911  VmaSuballocationType suballocType,
7912  size_t allocationCount,
7913  VmaAllocation* pAllocations);
7914 
7915  // Main deallocation function.
7916  void FreeMemory(
7917  size_t allocationCount,
7918  const VmaAllocation* pAllocations);
7919 
7920  VkResult ResizeAllocation(
7921  const VmaAllocation alloc,
7922  VkDeviceSize newSize);
7923 
7924  void CalculateStats(VmaStats* pStats);
7925 
7926  void GetBudget(
7927  VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7928 
7929 #if VMA_STATS_STRING_ENABLED
7930  void PrintDetailedMap(class VmaJsonWriter& json);
7931 #endif
7932 
7933  VkResult DefragmentationBegin(
7934  const VmaDefragmentationInfo2& info,
7935  VmaDefragmentationStats* pStats,
7936  VmaDefragmentationContext* pContext);
7937  VkResult DefragmentationEnd(
7938  VmaDefragmentationContext context);
7939 
7940  VkResult DefragmentationPassBegin(
7942  VmaDefragmentationContext context);
7943  VkResult DefragmentationPassEnd(
7944  VmaDefragmentationContext context);
7945 
7946  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7947  bool TouchAllocation(VmaAllocation hAllocation);
7948 
7949  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7950  void DestroyPool(VmaPool pool);
7951  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7952 
7953  void SetCurrentFrameIndex(uint32_t frameIndex);
7954  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7955 
7956  void MakePoolAllocationsLost(
7957  VmaPool hPool,
7958  size_t* pLostAllocationCount);
7959  VkResult CheckPoolCorruption(VmaPool hPool);
7960  VkResult CheckCorruption(uint32_t memoryTypeBits);
7961 
7962  void CreateLostAllocation(VmaAllocation* pAllocation);
7963 
7964  // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7965  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7966  // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7967  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7968  // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7969  VkResult BindVulkanBuffer(
7970  VkDeviceMemory memory,
7971  VkDeviceSize memoryOffset,
7972  VkBuffer buffer,
7973  const void* pNext);
7974  // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7975  VkResult BindVulkanImage(
7976  VkDeviceMemory memory,
7977  VkDeviceSize memoryOffset,
7978  VkImage image,
7979  const void* pNext);
7980 
7981  VkResult Map(VmaAllocation hAllocation, void** ppData);
7982  void Unmap(VmaAllocation hAllocation);
7983 
7984  VkResult BindBufferMemory(
7985  VmaAllocation hAllocation,
7986  VkDeviceSize allocationLocalOffset,
7987  VkBuffer hBuffer,
7988  const void* pNext);
7989  VkResult BindImageMemory(
7990  VmaAllocation hAllocation,
7991  VkDeviceSize allocationLocalOffset,
7992  VkImage hImage,
7993  const void* pNext);
7994 
7995  VkResult FlushOrInvalidateAllocation(
7996  VmaAllocation hAllocation,
7997  VkDeviceSize offset, VkDeviceSize size,
7998  VMA_CACHE_OPERATION op);
7999  VkResult FlushOrInvalidateAllocations(
8000  uint32_t allocationCount,
8001  const VmaAllocation* allocations,
8002  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
8003  VMA_CACHE_OPERATION op);
8004 
8005  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
8006 
8007  /*
8008  Returns bit mask of memory types that can support defragmentation on GPU as
8009  they support creation of required buffer for copy operations.
8010  */
8011  uint32_t GetGpuDefragmentationMemoryTypeBits();
8012 
8013 private:
8014  VkDeviceSize m_PreferredLargeHeapBlockSize;
8015 
8016  VkPhysicalDevice m_PhysicalDevice;
8017  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
8018  VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
8019 
8020  VMA_RW_MUTEX m_PoolsMutex;
8021  // Protected by m_PoolsMutex. Sorted by pointer value.
8022  VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
8023  uint32_t m_NextPoolId;
8024 
8025  VmaVulkanFunctions m_VulkanFunctions;
8026 
8027  // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
8028  uint32_t m_GlobalMemoryTypeBits;
8029 
8030 #if VMA_RECORDING_ENABLED
8031  VmaRecorder* m_pRecorder;
8032 #endif
8033 
8034  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
8035 
8036 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8037  void ImportVulkanFunctions_Static();
8038 #endif
8039 
8040  void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8041 
8042 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8043  void ImportVulkanFunctions_Dynamic();
8044 #endif
8045 
8046  void ValidateVulkanFunctions();
8047 
8048  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8049 
8050  VkResult AllocateMemoryOfType(
8051  VkDeviceSize size,
8052  VkDeviceSize alignment,
8053  bool dedicatedAllocation,
8054  VkBuffer dedicatedBuffer,
8055  VkBufferUsageFlags dedicatedBufferUsage,
8056  VkImage dedicatedImage,
8057  const VmaAllocationCreateInfo& createInfo,
8058  uint32_t memTypeIndex,
8059  VmaSuballocationType suballocType,
8060  size_t allocationCount,
8061  VmaAllocation* pAllocations);
8062 
8063  // Helper function only to be used inside AllocateDedicatedMemory.
8064  VkResult AllocateDedicatedMemoryPage(
8065  VkDeviceSize size,
8066  VmaSuballocationType suballocType,
8067  uint32_t memTypeIndex,
8068  const VkMemoryAllocateInfo& allocInfo,
8069  bool map,
8070  bool isUserDataString,
8071  void* pUserData,
8072  VmaAllocation* pAllocation);
8073 
8074  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8075  VkResult AllocateDedicatedMemory(
8076  VkDeviceSize size,
8077  VmaSuballocationType suballocType,
8078  uint32_t memTypeIndex,
8079  bool withinBudget,
8080  bool map,
8081  bool isUserDataString,
8082  void* pUserData,
8083  VkBuffer dedicatedBuffer,
8084  VkBufferUsageFlags dedicatedBufferUsage,
8085  VkImage dedicatedImage,
8086  size_t allocationCount,
8087  VmaAllocation* pAllocations);
8088 
8089  void FreeDedicatedMemory(const VmaAllocation allocation);
8090 
8091  /*
8092  Calculates and returns bit mask of memory types that can support defragmentation
8093  on GPU as they support creation of required buffer for copy operations.
8094  */
8095  uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8096 
8097  uint32_t CalculateGlobalMemoryTypeBits() const;
8098 
8099  bool GetFlushOrInvalidateRange(
8100  VmaAllocation allocation,
8101  VkDeviceSize offset, VkDeviceSize size,
8102  VkMappedMemoryRange& outRange) const;
8103 
8104 #if VMA_MEMORY_BUDGET
8105  void UpdateVulkanBudget();
8106 #endif // #if VMA_MEMORY_BUDGET
8107 };
8108 
8110 // Memory allocation #2 after VmaAllocator_T definition
8111 
8112 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8113 {
8114  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8115 }
8116 
8117 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8118 {
8119  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8120 }
8121 
8122 template<typename T>
8123 static T* VmaAllocate(VmaAllocator hAllocator)
8124 {
8125  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8126 }
8127 
8128 template<typename T>
8129 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8130 {
8131  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8132 }
8133 
8134 template<typename T>
8135 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8136 {
8137  if(ptr != VMA_NULL)
8138  {
8139  ptr->~T();
8140  VmaFree(hAllocator, ptr);
8141  }
8142 }
8143 
8144 template<typename T>
8145 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8146 {
8147  if(ptr != VMA_NULL)
8148  {
8149  for(size_t i = count; i--; )
8150  ptr[i].~T();
8151  VmaFree(hAllocator, ptr);
8152  }
8153 }
8154 
8156 // VmaStringBuilder
8157 
8158 #if VMA_STATS_STRING_ENABLED
8159 
8160 class VmaStringBuilder
8161 {
8162 public:
8163  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8164  size_t GetLength() const { return m_Data.size(); }
8165  const char* GetData() const { return m_Data.data(); }
8166 
8167  void Add(char ch) { m_Data.push_back(ch); }
8168  void Add(const char* pStr);
8169  void AddNewLine() { Add('\n'); }
8170  void AddNumber(uint32_t num);
8171  void AddNumber(uint64_t num);
8172  void AddPointer(const void* ptr);
8173 
8174 private:
8175  VmaVector< char, VmaStlAllocator<char> > m_Data;
8176 };
8177 
8178 void VmaStringBuilder::Add(const char* pStr)
8179 {
8180  const size_t strLen = strlen(pStr);
8181  if(strLen > 0)
8182  {
8183  const size_t oldCount = m_Data.size();
8184  m_Data.resize(oldCount + strLen);
8185  memcpy(m_Data.data() + oldCount, pStr, strLen);
8186  }
8187 }
8188 
8189 void VmaStringBuilder::AddNumber(uint32_t num)
8190 {
8191  char buf[11];
8192  buf[10] = '\0';
8193  char *p = &buf[10];
8194  do
8195  {
8196  *--p = '0' + (num % 10);
8197  num /= 10;
8198  }
8199  while(num);
8200  Add(p);
8201 }
8202 
8203 void VmaStringBuilder::AddNumber(uint64_t num)
8204 {
8205  char buf[21];
8206  buf[20] = '\0';
8207  char *p = &buf[20];
8208  do
8209  {
8210  *--p = '0' + (num % 10);
8211  num /= 10;
8212  }
8213  while(num);
8214  Add(p);
8215 }
8216 
8217 void VmaStringBuilder::AddPointer(const void* ptr)
8218 {
8219  char buf[21];
8220  VmaPtrToStr(buf, sizeof(buf), ptr);
8221  Add(buf);
8222 }
8223 
8224 #endif // #if VMA_STATS_STRING_ENABLED
8225 
8227 // VmaJsonWriter
8228 
8229 #if VMA_STATS_STRING_ENABLED
8230 
8231 class VmaJsonWriter
8232 {
8233  VMA_CLASS_NO_COPY(VmaJsonWriter)
8234 public:
8235  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8236  ~VmaJsonWriter();
8237 
8238  void BeginObject(bool singleLine = false);
8239  void EndObject();
8240 
8241  void BeginArray(bool singleLine = false);
8242  void EndArray();
8243 
8244  void WriteString(const char* pStr);
8245  void BeginString(const char* pStr = VMA_NULL);
8246  void ContinueString(const char* pStr);
8247  void ContinueString(uint32_t n);
8248  void ContinueString(uint64_t n);
8249  void ContinueString_Pointer(const void* ptr);
8250  void EndString(const char* pStr = VMA_NULL);
8251 
8252  void WriteNumber(uint32_t n);
8253  void WriteNumber(uint64_t n);
8254  void WriteBool(bool b);
8255  void WriteNull();
8256 
8257 private:
8258  static const char* const INDENT;
8259 
8260  enum COLLECTION_TYPE
8261  {
8262  COLLECTION_TYPE_OBJECT,
8263  COLLECTION_TYPE_ARRAY,
8264  };
8265  struct StackItem
8266  {
8267  COLLECTION_TYPE type;
8268  uint32_t valueCount;
8269  bool singleLineMode;
8270  };
8271 
8272  VmaStringBuilder& m_SB;
8273  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8274  bool m_InsideString;
8275 
8276  void BeginValue(bool isString);
8277  void WriteIndent(bool oneLess = false);
8278 };
8279 
8280 const char* const VmaJsonWriter::INDENT = " ";
8281 
8282 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8283  m_SB(sb),
8284  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8285  m_InsideString(false)
8286 {
8287 }
8288 
8289 VmaJsonWriter::~VmaJsonWriter()
8290 {
8291  VMA_ASSERT(!m_InsideString);
8292  VMA_ASSERT(m_Stack.empty());
8293 }
8294 
8295 void VmaJsonWriter::BeginObject(bool singleLine)
8296 {
8297  VMA_ASSERT(!m_InsideString);
8298 
8299  BeginValue(false);
8300  m_SB.Add('{');
8301 
8302  StackItem item;
8303  item.type = COLLECTION_TYPE_OBJECT;
8304  item.valueCount = 0;
8305  item.singleLineMode = singleLine;
8306  m_Stack.push_back(item);
8307 }
8308 
8309 void VmaJsonWriter::EndObject()
8310 {
8311  VMA_ASSERT(!m_InsideString);
8312 
8313  WriteIndent(true);
8314  m_SB.Add('}');
8315 
8316  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8317  m_Stack.pop_back();
8318 }
8319 
8320 void VmaJsonWriter::BeginArray(bool singleLine)
8321 {
8322  VMA_ASSERT(!m_InsideString);
8323 
8324  BeginValue(false);
8325  m_SB.Add('[');
8326 
8327  StackItem item;
8328  item.type = COLLECTION_TYPE_ARRAY;
8329  item.valueCount = 0;
8330  item.singleLineMode = singleLine;
8331  m_Stack.push_back(item);
8332 }
8333 
8334 void VmaJsonWriter::EndArray()
8335 {
8336  VMA_ASSERT(!m_InsideString);
8337 
8338  WriteIndent(true);
8339  m_SB.Add(']');
8340 
8341  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8342  m_Stack.pop_back();
8343 }
8344 
8345 void VmaJsonWriter::WriteString(const char* pStr)
8346 {
8347  BeginString(pStr);
8348  EndString();
8349 }
8350 
8351 void VmaJsonWriter::BeginString(const char* pStr)
8352 {
8353  VMA_ASSERT(!m_InsideString);
8354 
8355  BeginValue(true);
8356  m_SB.Add('"');
8357  m_InsideString = true;
8358  if(pStr != VMA_NULL && pStr[0] != '\0')
8359  {
8360  ContinueString(pStr);
8361  }
8362 }
8363 
8364 void VmaJsonWriter::ContinueString(const char* pStr)
8365 {
8366  VMA_ASSERT(m_InsideString);
8367 
8368  const size_t strLen = strlen(pStr);
8369  for(size_t i = 0; i < strLen; ++i)
8370  {
8371  char ch = pStr[i];
8372  if(ch == '\\')
8373  {
8374  m_SB.Add("\\\\");
8375  }
8376  else if(ch == '"')
8377  {
8378  m_SB.Add("\\\"");
8379  }
8380  else if(ch >= 32)
8381  {
8382  m_SB.Add(ch);
8383  }
8384  else switch(ch)
8385  {
8386  case '\b':
8387  m_SB.Add("\\b");
8388  break;
8389  case '\f':
8390  m_SB.Add("\\f");
8391  break;
8392  case '\n':
8393  m_SB.Add("\\n");
8394  break;
8395  case '\r':
8396  m_SB.Add("\\r");
8397  break;
8398  case '\t':
8399  m_SB.Add("\\t");
8400  break;
8401  default:
8402  VMA_ASSERT(0 && "Character not currently supported.");
8403  break;
8404  }
8405  }
8406 }
8407 
8408 void VmaJsonWriter::ContinueString(uint32_t n)
8409 {
8410  VMA_ASSERT(m_InsideString);
8411  m_SB.AddNumber(n);
8412 }
8413 
8414 void VmaJsonWriter::ContinueString(uint64_t n)
8415 {
8416  VMA_ASSERT(m_InsideString);
8417  m_SB.AddNumber(n);
8418 }
8419 
8420 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8421 {
8422  VMA_ASSERT(m_InsideString);
8423  m_SB.AddPointer(ptr);
8424 }
8425 
8426 void VmaJsonWriter::EndString(const char* pStr)
8427 {
8428  VMA_ASSERT(m_InsideString);
8429  if(pStr != VMA_NULL && pStr[0] != '\0')
8430  {
8431  ContinueString(pStr);
8432  }
8433  m_SB.Add('"');
8434  m_InsideString = false;
8435 }
8436 
8437 void VmaJsonWriter::WriteNumber(uint32_t n)
8438 {
8439  VMA_ASSERT(!m_InsideString);
8440  BeginValue(false);
8441  m_SB.AddNumber(n);
8442 }
8443 
8444 void VmaJsonWriter::WriteNumber(uint64_t n)
8445 {
8446  VMA_ASSERT(!m_InsideString);
8447  BeginValue(false);
8448  m_SB.AddNumber(n);
8449 }
8450 
8451 void VmaJsonWriter::WriteBool(bool b)
8452 {
8453  VMA_ASSERT(!m_InsideString);
8454  BeginValue(false);
8455  m_SB.Add(b ? "true" : "false");
8456 }
8457 
8458 void VmaJsonWriter::WriteNull()
8459 {
8460  VMA_ASSERT(!m_InsideString);
8461  BeginValue(false);
8462  m_SB.Add("null");
8463 }
8464 
8465 void VmaJsonWriter::BeginValue(bool isString)
8466 {
8467  if(!m_Stack.empty())
8468  {
8469  StackItem& currItem = m_Stack.back();
8470  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8471  currItem.valueCount % 2 == 0)
8472  {
8473  VMA_ASSERT(isString);
8474  }
8475 
8476  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8477  currItem.valueCount % 2 != 0)
8478  {
8479  m_SB.Add(": ");
8480  }
8481  else if(currItem.valueCount > 0)
8482  {
8483  m_SB.Add(", ");
8484  WriteIndent();
8485  }
8486  else
8487  {
8488  WriteIndent();
8489  }
8490  ++currItem.valueCount;
8491  }
8492 }
8493 
8494 void VmaJsonWriter::WriteIndent(bool oneLess)
8495 {
8496  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8497  {
8498  m_SB.AddNewLine();
8499 
8500  size_t count = m_Stack.size();
8501  if(count > 0 && oneLess)
8502  {
8503  --count;
8504  }
8505  for(size_t i = 0; i < count; ++i)
8506  {
8507  m_SB.Add(INDENT);
8508  }
8509  }
8510 }
8511 
8512 #endif // #if VMA_STATS_STRING_ENABLED
8513 
8515 
8516 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8517 {
8518  if(IsUserDataString())
8519  {
8520  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8521 
8522  FreeUserDataString(hAllocator);
8523 
8524  if(pUserData != VMA_NULL)
8525  {
8526  m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8527  }
8528  }
8529  else
8530  {
8531  m_pUserData = pUserData;
8532  }
8533 }
8534 
8535 void VmaAllocation_T::ChangeBlockAllocation(
8536  VmaAllocator hAllocator,
8537  VmaDeviceMemoryBlock* block,
8538  VkDeviceSize offset)
8539 {
8540  VMA_ASSERT(block != VMA_NULL);
8541  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8542 
8543  // Move mapping reference counter from old block to new block.
8544  if(block != m_BlockAllocation.m_Block)
8545  {
8546  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8547  if(IsPersistentMap())
8548  ++mapRefCount;
8549  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8550  block->Map(hAllocator, mapRefCount, VMA_NULL);
8551  }
8552 
8553  m_BlockAllocation.m_Block = block;
8554  m_BlockAllocation.m_Offset = offset;
8555 }
8556 
8557 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8558 {
8559  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8560  m_BlockAllocation.m_Offset = newOffset;
8561 }
8562 
8563 VkDeviceSize VmaAllocation_T::GetOffset() const
8564 {
8565  switch(m_Type)
8566  {
8567  case ALLOCATION_TYPE_BLOCK:
8568  return m_BlockAllocation.m_Offset;
8569  case ALLOCATION_TYPE_DEDICATED:
8570  return 0;
8571  default:
8572  VMA_ASSERT(0);
8573  return 0;
8574  }
8575 }
8576 
8577 VkDeviceMemory VmaAllocation_T::GetMemory() const
8578 {
8579  switch(m_Type)
8580  {
8581  case ALLOCATION_TYPE_BLOCK:
8582  return m_BlockAllocation.m_Block->GetDeviceMemory();
8583  case ALLOCATION_TYPE_DEDICATED:
8584  return m_DedicatedAllocation.m_hMemory;
8585  default:
8586  VMA_ASSERT(0);
8587  return VK_NULL_HANDLE;
8588  }
8589 }
8590 
8591 void* VmaAllocation_T::GetMappedData() const
8592 {
8593  switch(m_Type)
8594  {
8595  case ALLOCATION_TYPE_BLOCK:
8596  if(m_MapCount != 0)
8597  {
8598  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8599  VMA_ASSERT(pBlockData != VMA_NULL);
8600  return (char*)pBlockData + m_BlockAllocation.m_Offset;
8601  }
8602  else
8603  {
8604  return VMA_NULL;
8605  }
8606  break;
8607  case ALLOCATION_TYPE_DEDICATED:
8608  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8609  return m_DedicatedAllocation.m_pMappedData;
8610  default:
8611  VMA_ASSERT(0);
8612  return VMA_NULL;
8613  }
8614 }
8615 
8616 bool VmaAllocation_T::CanBecomeLost() const
8617 {
8618  switch(m_Type)
8619  {
8620  case ALLOCATION_TYPE_BLOCK:
8621  return m_BlockAllocation.m_CanBecomeLost;
8622  case ALLOCATION_TYPE_DEDICATED:
8623  return false;
8624  default:
8625  VMA_ASSERT(0);
8626  return false;
8627  }
8628 }
8629 
8630 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8631 {
8632  VMA_ASSERT(CanBecomeLost());
8633 
8634  /*
8635  Warning: This is a carefully designed algorithm.
8636  Do not modify unless you really know what you're doing :)
8637  */
8638  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8639  for(;;)
8640  {
8641  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8642  {
8643  VMA_ASSERT(0);
8644  return false;
8645  }
8646  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8647  {
8648  return false;
8649  }
8650  else // Last use time earlier than current time.
8651  {
8652  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8653  {
8654  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8655  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8656  return true;
8657  }
8658  }
8659  }
8660 }
8661 
8662 #if VMA_STATS_STRING_ENABLED
8663 
8664 // Correspond to values of enum VmaSuballocationType.
8665 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8666  "FREE",
8667  "UNKNOWN",
8668  "BUFFER",
8669  "IMAGE_UNKNOWN",
8670  "IMAGE_LINEAR",
8671  "IMAGE_OPTIMAL",
8672 };
8673 
8674 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8675 {
8676  json.WriteString("Type");
8677  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8678 
8679  json.WriteString("Size");
8680  json.WriteNumber(m_Size);
8681 
8682  if(m_pUserData != VMA_NULL)
8683  {
8684  json.WriteString("UserData");
8685  if(IsUserDataString())
8686  {
8687  json.WriteString((const char*)m_pUserData);
8688  }
8689  else
8690  {
8691  json.BeginString();
8692  json.ContinueString_Pointer(m_pUserData);
8693  json.EndString();
8694  }
8695  }
8696 
8697  json.WriteString("CreationFrameIndex");
8698  json.WriteNumber(m_CreationFrameIndex);
8699 
8700  json.WriteString("LastUseFrameIndex");
8701  json.WriteNumber(GetLastUseFrameIndex());
8702 
8703  if(m_BufferImageUsage != 0)
8704  {
8705  json.WriteString("Usage");
8706  json.WriteNumber(m_BufferImageUsage);
8707  }
8708 }
8709 
8710 #endif
8711 
8712 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8713 {
8714  VMA_ASSERT(IsUserDataString());
8715  VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8716  m_pUserData = VMA_NULL;
8717 }
8718 
8719 void VmaAllocation_T::BlockAllocMap()
8720 {
8721  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8722 
8723  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8724  {
8725  ++m_MapCount;
8726  }
8727  else
8728  {
8729  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8730  }
8731 }
8732 
8733 void VmaAllocation_T::BlockAllocUnmap()
8734 {
8735  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8736 
8737  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8738  {
8739  --m_MapCount;
8740  }
8741  else
8742  {
8743  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8744  }
8745 }
8746 
8747 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8748 {
8749  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8750 
8751  if(m_MapCount != 0)
8752  {
8753  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8754  {
8755  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8756  *ppData = m_DedicatedAllocation.m_pMappedData;
8757  ++m_MapCount;
8758  return VK_SUCCESS;
8759  }
8760  else
8761  {
8762  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8763  return VK_ERROR_MEMORY_MAP_FAILED;
8764  }
8765  }
8766  else
8767  {
8768  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8769  hAllocator->m_hDevice,
8770  m_DedicatedAllocation.m_hMemory,
8771  0, // offset
8772  VK_WHOLE_SIZE,
8773  0, // flags
8774  ppData);
8775  if(result == VK_SUCCESS)
8776  {
8777  m_DedicatedAllocation.m_pMappedData = *ppData;
8778  m_MapCount = 1;
8779  }
8780  return result;
8781  }
8782 }
8783 
8784 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8785 {
8786  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8787 
8788  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8789  {
8790  --m_MapCount;
8791  if(m_MapCount == 0)
8792  {
8793  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8794  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8795  hAllocator->m_hDevice,
8796  m_DedicatedAllocation.m_hMemory);
8797  }
8798  }
8799  else
8800  {
8801  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8802  }
8803 }
8804 
8805 #if VMA_STATS_STRING_ENABLED
8806 
8807 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8808 {
8809  json.BeginObject();
8810 
8811  json.WriteString("Blocks");
8812  json.WriteNumber(stat.blockCount);
8813 
8814  json.WriteString("Allocations");
8815  json.WriteNumber(stat.allocationCount);
8816 
8817  json.WriteString("UnusedRanges");
8818  json.WriteNumber(stat.unusedRangeCount);
8819 
8820  json.WriteString("UsedBytes");
8821  json.WriteNumber(stat.usedBytes);
8822 
8823  json.WriteString("UnusedBytes");
8824  json.WriteNumber(stat.unusedBytes);
8825 
8826  if(stat.allocationCount > 1)
8827  {
8828  json.WriteString("AllocationSize");
8829  json.BeginObject(true);
8830  json.WriteString("Min");
8831  json.WriteNumber(stat.allocationSizeMin);
8832  json.WriteString("Avg");
8833  json.WriteNumber(stat.allocationSizeAvg);
8834  json.WriteString("Max");
8835  json.WriteNumber(stat.allocationSizeMax);
8836  json.EndObject();
8837  }
8838 
8839  if(stat.unusedRangeCount > 1)
8840  {
8841  json.WriteString("UnusedRangeSize");
8842  json.BeginObject(true);
8843  json.WriteString("Min");
8844  json.WriteNumber(stat.unusedRangeSizeMin);
8845  json.WriteString("Avg");
8846  json.WriteNumber(stat.unusedRangeSizeAvg);
8847  json.WriteString("Max");
8848  json.WriteNumber(stat.unusedRangeSizeMax);
8849  json.EndObject();
8850  }
8851 
8852  json.EndObject();
8853 }
8854 
8855 #endif // #if VMA_STATS_STRING_ENABLED
8856 
8857 struct VmaSuballocationItemSizeLess
8858 {
8859  bool operator()(
8860  const VmaSuballocationList::iterator lhs,
8861  const VmaSuballocationList::iterator rhs) const
8862  {
8863  return lhs->size < rhs->size;
8864  }
8865  bool operator()(
8866  const VmaSuballocationList::iterator lhs,
8867  VkDeviceSize rhsSize) const
8868  {
8869  return lhs->size < rhsSize;
8870  }
8871 };
8872 
8873 
8875 // class VmaBlockMetadata
8876 
8877 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8878  m_Size(0),
8879  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8880 {
8881 }
8882 
8883 #if VMA_STATS_STRING_ENABLED
8884 
8885 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8886  VkDeviceSize unusedBytes,
8887  size_t allocationCount,
8888  size_t unusedRangeCount) const
8889 {
8890  json.BeginObject();
8891 
8892  json.WriteString("TotalBytes");
8893  json.WriteNumber(GetSize());
8894 
8895  json.WriteString("UnusedBytes");
8896  json.WriteNumber(unusedBytes);
8897 
8898  json.WriteString("Allocations");
8899  json.WriteNumber((uint64_t)allocationCount);
8900 
8901  json.WriteString("UnusedRanges");
8902  json.WriteNumber((uint64_t)unusedRangeCount);
8903 
8904  json.WriteString("Suballocations");
8905  json.BeginArray();
8906 }
8907 
8908 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8909  VkDeviceSize offset,
8910  VmaAllocation hAllocation) const
8911 {
8912  json.BeginObject(true);
8913 
8914  json.WriteString("Offset");
8915  json.WriteNumber(offset);
8916 
8917  hAllocation->PrintParameters(json);
8918 
8919  json.EndObject();
8920 }
8921 
8922 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8923  VkDeviceSize offset,
8924  VkDeviceSize size) const
8925 {
8926  json.BeginObject(true);
8927 
8928  json.WriteString("Offset");
8929  json.WriteNumber(offset);
8930 
8931  json.WriteString("Type");
8932  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8933 
8934  json.WriteString("Size");
8935  json.WriteNumber(size);
8936 
8937  json.EndObject();
8938 }
8939 
8940 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8941 {
8942  json.EndArray();
8943  json.EndObject();
8944 }
8945 
8946 #endif // #if VMA_STATS_STRING_ENABLED
8947 
8949 // class VmaBlockMetadata_Generic
8950 
8951 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8952  VmaBlockMetadata(hAllocator),
8953  m_FreeCount(0),
8954  m_SumFreeSize(0),
8955  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8956  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8957 {
8958 }
8959 
8960 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8961 {
8962 }
8963 
8964 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8965 {
8966  VmaBlockMetadata::Init(size);
8967 
8968  m_FreeCount = 1;
8969  m_SumFreeSize = size;
8970 
8971  VmaSuballocation suballoc = {};
8972  suballoc.offset = 0;
8973  suballoc.size = size;
8974  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8975  suballoc.hAllocation = VK_NULL_HANDLE;
8976 
8977  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8978  m_Suballocations.push_back(suballoc);
8979  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8980  --suballocItem;
8981  m_FreeSuballocationsBySize.push_back(suballocItem);
8982 }
8983 
8984 bool VmaBlockMetadata_Generic::Validate() const
8985 {
8986  VMA_VALIDATE(!m_Suballocations.empty());
8987 
8988  // Expected offset of new suballocation as calculated from previous ones.
8989  VkDeviceSize calculatedOffset = 0;
8990  // Expected number of free suballocations as calculated from traversing their list.
8991  uint32_t calculatedFreeCount = 0;
8992  // Expected sum size of free suballocations as calculated from traversing their list.
8993  VkDeviceSize calculatedSumFreeSize = 0;
8994  // Expected number of free suballocations that should be registered in
8995  // m_FreeSuballocationsBySize calculated from traversing their list.
8996  size_t freeSuballocationsToRegister = 0;
8997  // True if previous visited suballocation was free.
8998  bool prevFree = false;
8999 
9000  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9001  suballocItem != m_Suballocations.cend();
9002  ++suballocItem)
9003  {
9004  const VmaSuballocation& subAlloc = *suballocItem;
9005 
9006  // Actual offset of this suballocation doesn't match expected one.
9007  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
9008 
9009  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
9010  // Two adjacent free suballocations are invalid. They should be merged.
9011  VMA_VALIDATE(!prevFree || !currFree);
9012 
9013  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
9014 
9015  if(currFree)
9016  {
9017  calculatedSumFreeSize += subAlloc.size;
9018  ++calculatedFreeCount;
9019  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9020  {
9021  ++freeSuballocationsToRegister;
9022  }
9023 
9024  // Margin required between allocations - every free space must be at least that large.
9025  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
9026  }
9027  else
9028  {
9029  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
9030  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
9031 
9032  // Margin required between allocations - previous allocation must be free.
9033  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
9034  }
9035 
9036  calculatedOffset += subAlloc.size;
9037  prevFree = currFree;
9038  }
9039 
9040  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9041  // match expected one.
9042  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9043 
9044  VkDeviceSize lastSize = 0;
9045  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9046  {
9047  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9048 
9049  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9050  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9051  // They must be sorted by size ascending.
9052  VMA_VALIDATE(suballocItem->size >= lastSize);
9053 
9054  lastSize = suballocItem->size;
9055  }
9056 
9057  // Check if totals match calculacted values.
9058  VMA_VALIDATE(ValidateFreeSuballocationList());
9059  VMA_VALIDATE(calculatedOffset == GetSize());
9060  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9061  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9062 
9063  return true;
9064 }
9065 
9066 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9067 {
9068  if(!m_FreeSuballocationsBySize.empty())
9069  {
9070  return m_FreeSuballocationsBySize.back()->size;
9071  }
9072  else
9073  {
9074  return 0;
9075  }
9076 }
9077 
9078 bool VmaBlockMetadata_Generic::IsEmpty() const
9079 {
9080  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9081 }
9082 
9083 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9084 {
9085  outInfo.blockCount = 1;
9086 
9087  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9088  outInfo.allocationCount = rangeCount - m_FreeCount;
9089  outInfo.unusedRangeCount = m_FreeCount;
9090 
9091  outInfo.unusedBytes = m_SumFreeSize;
9092  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9093 
9094  outInfo.allocationSizeMin = UINT64_MAX;
9095  outInfo.allocationSizeMax = 0;
9096  outInfo.unusedRangeSizeMin = UINT64_MAX;
9097  outInfo.unusedRangeSizeMax = 0;
9098 
9099  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9100  suballocItem != m_Suballocations.cend();
9101  ++suballocItem)
9102  {
9103  const VmaSuballocation& suballoc = *suballocItem;
9104  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9105  {
9106  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9107  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9108  }
9109  else
9110  {
9111  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9112  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9113  }
9114  }
9115 }
9116 
9117 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9118 {
9119  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9120 
9121  inoutStats.size += GetSize();
9122  inoutStats.unusedSize += m_SumFreeSize;
9123  inoutStats.allocationCount += rangeCount - m_FreeCount;
9124  inoutStats.unusedRangeCount += m_FreeCount;
9125  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9126 }
9127 
9128 #if VMA_STATS_STRING_ENABLED
9129 
9130 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9131 {
9132  PrintDetailedMap_Begin(json,
9133  m_SumFreeSize, // unusedBytes
9134  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9135  m_FreeCount); // unusedRangeCount
9136 
9137  size_t i = 0;
9138  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9139  suballocItem != m_Suballocations.cend();
9140  ++suballocItem, ++i)
9141  {
9142  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9143  {
9144  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9145  }
9146  else
9147  {
9148  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9149  }
9150  }
9151 
9152  PrintDetailedMap_End(json);
9153 }
9154 
9155 #endif // #if VMA_STATS_STRING_ENABLED
9156 
9157 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9158  uint32_t currentFrameIndex,
9159  uint32_t frameInUseCount,
9160  VkDeviceSize bufferImageGranularity,
9161  VkDeviceSize allocSize,
9162  VkDeviceSize allocAlignment,
9163  bool upperAddress,
9164  VmaSuballocationType allocType,
9165  bool canMakeOtherLost,
9166  uint32_t strategy,
9167  VmaAllocationRequest* pAllocationRequest)
9168 {
9169  VMA_ASSERT(allocSize > 0);
9170  VMA_ASSERT(!upperAddress);
9171  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9172  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9173  VMA_HEAVY_ASSERT(Validate());
9174 
9175  pAllocationRequest->type = VmaAllocationRequestType::Normal;
9176 
9177  // There is not enough total free space in this block to fullfill the request: Early return.
9178  if(canMakeOtherLost == false &&
9179  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9180  {
9181  return false;
9182  }
9183 
9184  // New algorithm, efficiently searching freeSuballocationsBySize.
9185  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9186  if(freeSuballocCount > 0)
9187  {
9189  {
9190  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9191  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9192  m_FreeSuballocationsBySize.data(),
9193  m_FreeSuballocationsBySize.data() + freeSuballocCount,
9194  allocSize + 2 * VMA_DEBUG_MARGIN,
9195  VmaSuballocationItemSizeLess());
9196  size_t index = it - m_FreeSuballocationsBySize.data();
9197  for(; index < freeSuballocCount; ++index)
9198  {
9199  if(CheckAllocation(
9200  currentFrameIndex,
9201  frameInUseCount,
9202  bufferImageGranularity,
9203  allocSize,
9204  allocAlignment,
9205  allocType,
9206  m_FreeSuballocationsBySize[index],
9207  false, // canMakeOtherLost
9208  &pAllocationRequest->offset,
9209  &pAllocationRequest->itemsToMakeLostCount,
9210  &pAllocationRequest->sumFreeSize,
9211  &pAllocationRequest->sumItemSize))
9212  {
9213  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9214  return true;
9215  }
9216  }
9217  }
9218  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9219  {
9220  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9221  it != m_Suballocations.end();
9222  ++it)
9223  {
9224  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9225  currentFrameIndex,
9226  frameInUseCount,
9227  bufferImageGranularity,
9228  allocSize,
9229  allocAlignment,
9230  allocType,
9231  it,
9232  false, // canMakeOtherLost
9233  &pAllocationRequest->offset,
9234  &pAllocationRequest->itemsToMakeLostCount,
9235  &pAllocationRequest->sumFreeSize,
9236  &pAllocationRequest->sumItemSize))
9237  {
9238  pAllocationRequest->item = it;
9239  return true;
9240  }
9241  }
9242  }
9243  else // WORST_FIT, FIRST_FIT
9244  {
9245  // Search staring from biggest suballocations.
9246  for(size_t index = freeSuballocCount; index--; )
9247  {
9248  if(CheckAllocation(
9249  currentFrameIndex,
9250  frameInUseCount,
9251  bufferImageGranularity,
9252  allocSize,
9253  allocAlignment,
9254  allocType,
9255  m_FreeSuballocationsBySize[index],
9256  false, // canMakeOtherLost
9257  &pAllocationRequest->offset,
9258  &pAllocationRequest->itemsToMakeLostCount,
9259  &pAllocationRequest->sumFreeSize,
9260  &pAllocationRequest->sumItemSize))
9261  {
9262  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9263  return true;
9264  }
9265  }
9266  }
9267  }
9268 
9269  if(canMakeOtherLost)
9270  {
9271  // Brute-force algorithm. TODO: Come up with something better.
9272 
9273  bool found = false;
9274  VmaAllocationRequest tmpAllocRequest = {};
9275  tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9276  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9277  suballocIt != m_Suballocations.end();
9278  ++suballocIt)
9279  {
9280  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9281  suballocIt->hAllocation->CanBecomeLost())
9282  {
9283  if(CheckAllocation(
9284  currentFrameIndex,
9285  frameInUseCount,
9286  bufferImageGranularity,
9287  allocSize,
9288  allocAlignment,
9289  allocType,
9290  suballocIt,
9291  canMakeOtherLost,
9292  &tmpAllocRequest.offset,
9293  &tmpAllocRequest.itemsToMakeLostCount,
9294  &tmpAllocRequest.sumFreeSize,
9295  &tmpAllocRequest.sumItemSize))
9296  {
9298  {
9299  *pAllocationRequest = tmpAllocRequest;
9300  pAllocationRequest->item = suballocIt;
9301  break;
9302  }
9303  if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9304  {
9305  *pAllocationRequest = tmpAllocRequest;
9306  pAllocationRequest->item = suballocIt;
9307  found = true;
9308  }
9309  }
9310  }
9311  }
9312 
9313  return found;
9314  }
9315 
9316  return false;
9317 }
9318 
9319 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9320  uint32_t currentFrameIndex,
9321  uint32_t frameInUseCount,
9322  VmaAllocationRequest* pAllocationRequest)
9323 {
9324  VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9325 
9326  while(pAllocationRequest->itemsToMakeLostCount > 0)
9327  {
9328  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9329  {
9330  ++pAllocationRequest->item;
9331  }
9332  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9333  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9334  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9335  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9336  {
9337  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9338  --pAllocationRequest->itemsToMakeLostCount;
9339  }
9340  else
9341  {
9342  return false;
9343  }
9344  }
9345 
9346  VMA_HEAVY_ASSERT(Validate());
9347  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9348  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9349 
9350  return true;
9351 }
9352 
9353 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9354 {
9355  uint32_t lostAllocationCount = 0;
9356  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9357  it != m_Suballocations.end();
9358  ++it)
9359  {
9360  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9361  it->hAllocation->CanBecomeLost() &&
9362  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9363  {
9364  it = FreeSuballocation(it);
9365  ++lostAllocationCount;
9366  }
9367  }
9368  return lostAllocationCount;
9369 }
9370 
9371 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9372 {
9373  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9374  it != m_Suballocations.end();
9375  ++it)
9376  {
9377  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9378  {
9379  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9380  {
9381  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9382  return VK_ERROR_VALIDATION_FAILED_EXT;
9383  }
9384  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9385  {
9386  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9387  return VK_ERROR_VALIDATION_FAILED_EXT;
9388  }
9389  }
9390  }
9391 
9392  return VK_SUCCESS;
9393 }
9394 
9395 void VmaBlockMetadata_Generic::Alloc(
9396  const VmaAllocationRequest& request,
9397  VmaSuballocationType type,
9398  VkDeviceSize allocSize,
9399  VmaAllocation hAllocation)
9400 {
9401  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9402  VMA_ASSERT(request.item != m_Suballocations.end());
9403  VmaSuballocation& suballoc = *request.item;
9404  // Given suballocation is a free block.
9405  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9406  // Given offset is inside this suballocation.
9407  VMA_ASSERT(request.offset >= suballoc.offset);
9408  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9409  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9410  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9411 
9412  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9413  // it to become used.
9414  UnregisterFreeSuballocation(request.item);
9415 
9416  suballoc.offset = request.offset;
9417  suballoc.size = allocSize;
9418  suballoc.type = type;
9419  suballoc.hAllocation = hAllocation;
9420 
9421  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9422  if(paddingEnd)
9423  {
9424  VmaSuballocation paddingSuballoc = {};
9425  paddingSuballoc.offset = request.offset + allocSize;
9426  paddingSuballoc.size = paddingEnd;
9427  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9428  VmaSuballocationList::iterator next = request.item;
9429  ++next;
9430  const VmaSuballocationList::iterator paddingEndItem =
9431  m_Suballocations.insert(next, paddingSuballoc);
9432  RegisterFreeSuballocation(paddingEndItem);
9433  }
9434 
9435  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9436  if(paddingBegin)
9437  {
9438  VmaSuballocation paddingSuballoc = {};
9439  paddingSuballoc.offset = request.offset - paddingBegin;
9440  paddingSuballoc.size = paddingBegin;
9441  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9442  const VmaSuballocationList::iterator paddingBeginItem =
9443  m_Suballocations.insert(request.item, paddingSuballoc);
9444  RegisterFreeSuballocation(paddingBeginItem);
9445  }
9446 
9447  // Update totals.
9448  m_FreeCount = m_FreeCount - 1;
9449  if(paddingBegin > 0)
9450  {
9451  ++m_FreeCount;
9452  }
9453  if(paddingEnd > 0)
9454  {
9455  ++m_FreeCount;
9456  }
9457  m_SumFreeSize -= allocSize;
9458 }
9459 
9460 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9461 {
9462  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9463  suballocItem != m_Suballocations.end();
9464  ++suballocItem)
9465  {
9466  VmaSuballocation& suballoc = *suballocItem;
9467  if(suballoc.hAllocation == allocation)
9468  {
9469  FreeSuballocation(suballocItem);
9470  VMA_HEAVY_ASSERT(Validate());
9471  return;
9472  }
9473  }
9474  VMA_ASSERT(0 && "Not found!");
9475 }
9476 
9477 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9478 {
9479  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9480  suballocItem != m_Suballocations.end();
9481  ++suballocItem)
9482  {
9483  VmaSuballocation& suballoc = *suballocItem;
9484  if(suballoc.offset == offset)
9485  {
9486  FreeSuballocation(suballocItem);
9487  return;
9488  }
9489  }
9490  VMA_ASSERT(0 && "Not found!");
9491 }
9492 
9493 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9494 {
9495  VkDeviceSize lastSize = 0;
9496  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9497  {
9498  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9499 
9500  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9501  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9502  VMA_VALIDATE(it->size >= lastSize);
9503  lastSize = it->size;
9504  }
9505  return true;
9506 }
9507 
9508 bool VmaBlockMetadata_Generic::CheckAllocation(
9509  uint32_t currentFrameIndex,
9510  uint32_t frameInUseCount,
9511  VkDeviceSize bufferImageGranularity,
9512  VkDeviceSize allocSize,
9513  VkDeviceSize allocAlignment,
9514  VmaSuballocationType allocType,
9515  VmaSuballocationList::const_iterator suballocItem,
9516  bool canMakeOtherLost,
9517  VkDeviceSize* pOffset,
9518  size_t* itemsToMakeLostCount,
9519  VkDeviceSize* pSumFreeSize,
9520  VkDeviceSize* pSumItemSize) const
9521 {
9522  VMA_ASSERT(allocSize > 0);
9523  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9524  VMA_ASSERT(suballocItem != m_Suballocations.cend());
9525  VMA_ASSERT(pOffset != VMA_NULL);
9526 
9527  *itemsToMakeLostCount = 0;
9528  *pSumFreeSize = 0;
9529  *pSumItemSize = 0;
9530 
9531  if(canMakeOtherLost)
9532  {
9533  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9534  {
9535  *pSumFreeSize = suballocItem->size;
9536  }
9537  else
9538  {
9539  if(suballocItem->hAllocation->CanBecomeLost() &&
9540  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9541  {
9542  ++*itemsToMakeLostCount;
9543  *pSumItemSize = suballocItem->size;
9544  }
9545  else
9546  {
9547  return false;
9548  }
9549  }
9550 
9551  // Remaining size is too small for this request: Early return.
9552  if(GetSize() - suballocItem->offset < allocSize)
9553  {
9554  return false;
9555  }
9556 
9557  // Start from offset equal to beginning of this suballocation.
9558  *pOffset = suballocItem->offset;
9559 
9560  // Apply VMA_DEBUG_MARGIN at the beginning.
9561  if(VMA_DEBUG_MARGIN > 0)
9562  {
9563  *pOffset += VMA_DEBUG_MARGIN;
9564  }
9565 
9566  // Apply alignment.
9567  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9568 
9569  // Check previous suballocations for BufferImageGranularity conflicts.
9570  // Make bigger alignment if necessary.
9571  if(bufferImageGranularity > 1)
9572  {
9573  bool bufferImageGranularityConflict = false;
9574  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9575  while(prevSuballocItem != m_Suballocations.cbegin())
9576  {
9577  --prevSuballocItem;
9578  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9579  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9580  {
9581  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9582  {
9583  bufferImageGranularityConflict = true;
9584  break;
9585  }
9586  }
9587  else
9588  // Already on previous page.
9589  break;
9590  }
9591  if(bufferImageGranularityConflict)
9592  {
9593  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9594  }
9595  }
9596 
9597  // Now that we have final *pOffset, check if we are past suballocItem.
9598  // If yes, return false - this function should be called for another suballocItem as starting point.
9599  if(*pOffset >= suballocItem->offset + suballocItem->size)
9600  {
9601  return false;
9602  }
9603 
9604  // Calculate padding at the beginning based on current offset.
9605  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9606 
9607  // Calculate required margin at the end.
9608  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9609 
9610  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9611  // Another early return check.
9612  if(suballocItem->offset + totalSize > GetSize())
9613  {
9614  return false;
9615  }
9616 
9617  // Advance lastSuballocItem until desired size is reached.
9618  // Update itemsToMakeLostCount.
9619  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9620  if(totalSize > suballocItem->size)
9621  {
9622  VkDeviceSize remainingSize = totalSize - suballocItem->size;
9623  while(remainingSize > 0)
9624  {
9625  ++lastSuballocItem;
9626  if(lastSuballocItem == m_Suballocations.cend())
9627  {
9628  return false;
9629  }
9630  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9631  {
9632  *pSumFreeSize += lastSuballocItem->size;
9633  }
9634  else
9635  {
9636  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9637  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9638  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9639  {
9640  ++*itemsToMakeLostCount;
9641  *pSumItemSize += lastSuballocItem->size;
9642  }
9643  else
9644  {
9645  return false;
9646  }
9647  }
9648  remainingSize = (lastSuballocItem->size < remainingSize) ?
9649  remainingSize - lastSuballocItem->size : 0;
9650  }
9651  }
9652 
9653  // Check next suballocations for BufferImageGranularity conflicts.
9654  // If conflict exists, we must mark more allocations lost or fail.
9655  if(bufferImageGranularity > 1)
9656  {
9657  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9658  ++nextSuballocItem;
9659  while(nextSuballocItem != m_Suballocations.cend())
9660  {
9661  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9662  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9663  {
9664  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9665  {
9666  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9667  if(nextSuballoc.hAllocation->CanBecomeLost() &&
9668  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9669  {
9670  ++*itemsToMakeLostCount;
9671  }
9672  else
9673  {
9674  return false;
9675  }
9676  }
9677  }
9678  else
9679  {
9680  // Already on next page.
9681  break;
9682  }
9683  ++nextSuballocItem;
9684  }
9685  }
9686  }
9687  else
9688  {
9689  const VmaSuballocation& suballoc = *suballocItem;
9690  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9691 
9692  *pSumFreeSize = suballoc.size;
9693 
9694  // Size of this suballocation is too small for this request: Early return.
9695  if(suballoc.size < allocSize)
9696  {
9697  return false;
9698  }
9699 
9700  // Start from offset equal to beginning of this suballocation.
9701  *pOffset = suballoc.offset;
9702 
9703  // Apply VMA_DEBUG_MARGIN at the beginning.
9704  if(VMA_DEBUG_MARGIN > 0)
9705  {
9706  *pOffset += VMA_DEBUG_MARGIN;
9707  }
9708 
9709  // Apply alignment.
9710  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9711 
9712  // Check previous suballocations for BufferImageGranularity conflicts.
9713  // Make bigger alignment if necessary.
9714  if(bufferImageGranularity > 1)
9715  {
9716  bool bufferImageGranularityConflict = false;
9717  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9718  while(prevSuballocItem != m_Suballocations.cbegin())
9719  {
9720  --prevSuballocItem;
9721  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9722  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9723  {
9724  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9725  {
9726  bufferImageGranularityConflict = true;
9727  break;
9728  }
9729  }
9730  else
9731  // Already on previous page.
9732  break;
9733  }
9734  if(bufferImageGranularityConflict)
9735  {
9736  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9737  }
9738  }
9739 
9740  // Calculate padding at the beginning based on current offset.
9741  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9742 
9743  // Calculate required margin at the end.
9744  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9745 
9746  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9747  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9748  {
9749  return false;
9750  }
9751 
9752  // Check next suballocations for BufferImageGranularity conflicts.
9753  // If conflict exists, allocation cannot be made here.
9754  if(bufferImageGranularity > 1)
9755  {
9756  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9757  ++nextSuballocItem;
9758  while(nextSuballocItem != m_Suballocations.cend())
9759  {
9760  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9761  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9762  {
9763  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9764  {
9765  return false;
9766  }
9767  }
9768  else
9769  {
9770  // Already on next page.
9771  break;
9772  }
9773  ++nextSuballocItem;
9774  }
9775  }
9776  }
9777 
9778  // All tests passed: Success. pOffset is already filled.
9779  return true;
9780 }
9781 
9782 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9783 {
9784  VMA_ASSERT(item != m_Suballocations.end());
9785  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9786 
9787  VmaSuballocationList::iterator nextItem = item;
9788  ++nextItem;
9789  VMA_ASSERT(nextItem != m_Suballocations.end());
9790  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9791 
9792  item->size += nextItem->size;
9793  --m_FreeCount;
9794  m_Suballocations.erase(nextItem);
9795 }
9796 
9797 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9798 {
9799  // Change this suballocation to be marked as free.
9800  VmaSuballocation& suballoc = *suballocItem;
9801  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9802  suballoc.hAllocation = VK_NULL_HANDLE;
9803 
9804  // Update totals.
9805  ++m_FreeCount;
9806  m_SumFreeSize += suballoc.size;
9807 
9808  // Merge with previous and/or next suballocation if it's also free.
9809  bool mergeWithNext = false;
9810  bool mergeWithPrev = false;
9811 
9812  VmaSuballocationList::iterator nextItem = suballocItem;
9813  ++nextItem;
9814  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9815  {
9816  mergeWithNext = true;
9817  }
9818 
9819  VmaSuballocationList::iterator prevItem = suballocItem;
9820  if(suballocItem != m_Suballocations.begin())
9821  {
9822  --prevItem;
9823  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9824  {
9825  mergeWithPrev = true;
9826  }
9827  }
9828 
9829  if(mergeWithNext)
9830  {
9831  UnregisterFreeSuballocation(nextItem);
9832  MergeFreeWithNext(suballocItem);
9833  }
9834 
9835  if(mergeWithPrev)
9836  {
9837  UnregisterFreeSuballocation(prevItem);
9838  MergeFreeWithNext(prevItem);
9839  RegisterFreeSuballocation(prevItem);
9840  return prevItem;
9841  }
9842  else
9843  {
9844  RegisterFreeSuballocation(suballocItem);
9845  return suballocItem;
9846  }
9847 }
9848 
9849 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9850 {
9851  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9852  VMA_ASSERT(item->size > 0);
9853 
9854  // You may want to enable this validation at the beginning or at the end of
9855  // this function, depending on what do you want to check.
9856  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9857 
9858  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9859  {
9860  if(m_FreeSuballocationsBySize.empty())
9861  {
9862  m_FreeSuballocationsBySize.push_back(item);
9863  }
9864  else
9865  {
9866  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9867  }
9868  }
9869 
9870  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9871 }
9872 
9873 
9874 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9875 {
9876  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9877  VMA_ASSERT(item->size > 0);
9878 
9879  // You may want to enable this validation at the beginning or at the end of
9880  // this function, depending on what do you want to check.
9881  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9882 
9883  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9884  {
9885  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9886  m_FreeSuballocationsBySize.data(),
9887  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9888  item,
9889  VmaSuballocationItemSizeLess());
9890  for(size_t index = it - m_FreeSuballocationsBySize.data();
9891  index < m_FreeSuballocationsBySize.size();
9892  ++index)
9893  {
9894  if(m_FreeSuballocationsBySize[index] == item)
9895  {
9896  VmaVectorRemove(m_FreeSuballocationsBySize, index);
9897  return;
9898  }
9899  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9900  }
9901  VMA_ASSERT(0 && "Not found.");
9902  }
9903 
9904  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9905 }
9906 
9907 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9908  VkDeviceSize bufferImageGranularity,
9909  VmaSuballocationType& inOutPrevSuballocType) const
9910 {
9911  if(bufferImageGranularity == 1 || IsEmpty())
9912  {
9913  return false;
9914  }
9915 
9916  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9917  bool typeConflictFound = false;
9918  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9919  it != m_Suballocations.cend();
9920  ++it)
9921  {
9922  const VmaSuballocationType suballocType = it->type;
9923  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9924  {
9925  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9926  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9927  {
9928  typeConflictFound = true;
9929  }
9930  inOutPrevSuballocType = suballocType;
9931  }
9932  }
9933 
9934  return typeConflictFound || minAlignment >= bufferImageGranularity;
9935 }
9936 
9938 // class VmaBlockMetadata_Linear
9939 
9940 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9941  VmaBlockMetadata(hAllocator),
9942  m_SumFreeSize(0),
9943  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9944  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9945  m_1stVectorIndex(0),
9946  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9947  m_1stNullItemsBeginCount(0),
9948  m_1stNullItemsMiddleCount(0),
9949  m_2ndNullItemsCount(0)
9950 {
9951 }
9952 
9953 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9954 {
9955 }
9956 
9957 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9958 {
9959  VmaBlockMetadata::Init(size);
9960  m_SumFreeSize = size;
9961 }
9962 
9963 bool VmaBlockMetadata_Linear::Validate() const
9964 {
9965  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9966  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9967 
9968  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9969  VMA_VALIDATE(!suballocations1st.empty() ||
9970  suballocations2nd.empty() ||
9971  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9972 
9973  if(!suballocations1st.empty())
9974  {
9975  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9976  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9977  // Null item at the end should be just pop_back().
9978  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9979  }
9980  if(!suballocations2nd.empty())
9981  {
9982  // Null item at the end should be just pop_back().
9983  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9984  }
9985 
9986  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9987  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9988 
9989  VkDeviceSize sumUsedSize = 0;
9990  const size_t suballoc1stCount = suballocations1st.size();
9991  VkDeviceSize offset = VMA_DEBUG_MARGIN;
9992 
9993  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9994  {
9995  const size_t suballoc2ndCount = suballocations2nd.size();
9996  size_t nullItem2ndCount = 0;
9997  for(size_t i = 0; i < suballoc2ndCount; ++i)
9998  {
9999  const VmaSuballocation& suballoc = suballocations2nd[i];
10000  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10001 
10002  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10003  VMA_VALIDATE(suballoc.offset >= offset);
10004 
10005  if(!currFree)
10006  {
10007  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10008  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10009  sumUsedSize += suballoc.size;
10010  }
10011  else
10012  {
10013  ++nullItem2ndCount;
10014  }
10015 
10016  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10017  }
10018 
10019  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10020  }
10021 
10022  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
10023  {
10024  const VmaSuballocation& suballoc = suballocations1st[i];
10025  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
10026  suballoc.hAllocation == VK_NULL_HANDLE);
10027  }
10028 
10029  size_t nullItem1stCount = m_1stNullItemsBeginCount;
10030 
10031  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
10032  {
10033  const VmaSuballocation& suballoc = suballocations1st[i];
10034  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10035 
10036  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10037  VMA_VALIDATE(suballoc.offset >= offset);
10038  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10039 
10040  if(!currFree)
10041  {
10042  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10043  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10044  sumUsedSize += suballoc.size;
10045  }
10046  else
10047  {
10048  ++nullItem1stCount;
10049  }
10050 
10051  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10052  }
10053  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10054 
10055  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10056  {
10057  const size_t suballoc2ndCount = suballocations2nd.size();
10058  size_t nullItem2ndCount = 0;
10059  for(size_t i = suballoc2ndCount; i--; )
10060  {
10061  const VmaSuballocation& suballoc = suballocations2nd[i];
10062  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10063 
10064  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10065  VMA_VALIDATE(suballoc.offset >= offset);
10066 
10067  if(!currFree)
10068  {
10069  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10070  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10071  sumUsedSize += suballoc.size;
10072  }
10073  else
10074  {
10075  ++nullItem2ndCount;
10076  }
10077 
10078  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10079  }
10080 
10081  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10082  }
10083 
10084  VMA_VALIDATE(offset <= GetSize());
10085  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10086 
10087  return true;
10088 }
10089 
10090 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10091 {
10092  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10093  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10094 }
10095 
10096 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10097 {
10098  const VkDeviceSize size = GetSize();
10099 
10100  /*
10101  We don't consider gaps inside allocation vectors with freed allocations because
10102  they are not suitable for reuse in linear allocator. We consider only space that
10103  is available for new allocations.
10104  */
10105  if(IsEmpty())
10106  {
10107  return size;
10108  }
10109 
10110  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10111 
10112  switch(m_2ndVectorMode)
10113  {
10114  case SECOND_VECTOR_EMPTY:
10115  /*
10116  Available space is after end of 1st, as well as before beginning of 1st (which
10117  whould make it a ring buffer).
10118  */
10119  {
10120  const size_t suballocations1stCount = suballocations1st.size();
10121  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10122  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10123  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10124  return VMA_MAX(
10125  firstSuballoc.offset,
10126  size - (lastSuballoc.offset + lastSuballoc.size));
10127  }
10128  break;
10129 
10130  case SECOND_VECTOR_RING_BUFFER:
10131  /*
10132  Available space is only between end of 2nd and beginning of 1st.
10133  */
10134  {
10135  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10136  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10137  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10138  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10139  }
10140  break;
10141 
10142  case SECOND_VECTOR_DOUBLE_STACK:
10143  /*
10144  Available space is only between end of 1st and top of 2nd.
10145  */
10146  {
10147  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10148  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10149  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10150  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10151  }
10152  break;
10153 
10154  default:
10155  VMA_ASSERT(0);
10156  return 0;
10157  }
10158 }
10159 
10160 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10161 {
10162  const VkDeviceSize size = GetSize();
10163  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10164  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10165  const size_t suballoc1stCount = suballocations1st.size();
10166  const size_t suballoc2ndCount = suballocations2nd.size();
10167 
10168  outInfo.blockCount = 1;
10169  outInfo.allocationCount = (uint32_t)GetAllocationCount();
10170  outInfo.unusedRangeCount = 0;
10171  outInfo.usedBytes = 0;
10172  outInfo.allocationSizeMin = UINT64_MAX;
10173  outInfo.allocationSizeMax = 0;
10174  outInfo.unusedRangeSizeMin = UINT64_MAX;
10175  outInfo.unusedRangeSizeMax = 0;
10176 
10177  VkDeviceSize lastOffset = 0;
10178 
10179  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10180  {
10181  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10182  size_t nextAlloc2ndIndex = 0;
10183  while(lastOffset < freeSpace2ndTo1stEnd)
10184  {
10185  // Find next non-null allocation or move nextAllocIndex to the end.
10186  while(nextAlloc2ndIndex < suballoc2ndCount &&
10187  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10188  {
10189  ++nextAlloc2ndIndex;
10190  }
10191 
10192  // Found non-null allocation.
10193  if(nextAlloc2ndIndex < suballoc2ndCount)
10194  {
10195  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10196 
10197  // 1. Process free space before this allocation.
10198  if(lastOffset < suballoc.offset)
10199  {
10200  // There is free space from lastOffset to suballoc.offset.
10201  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10202  ++outInfo.unusedRangeCount;
10203  outInfo.unusedBytes += unusedRangeSize;
10204  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10205  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10206  }
10207 
10208  // 2. Process this allocation.
10209  // There is allocation with suballoc.offset, suballoc.size.
10210  outInfo.usedBytes += suballoc.size;
10211  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10212  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10213 
10214  // 3. Prepare for next iteration.
10215  lastOffset = suballoc.offset + suballoc.size;
10216  ++nextAlloc2ndIndex;
10217  }
10218  // We are at the end.
10219  else
10220  {
10221  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10222  if(lastOffset < freeSpace2ndTo1stEnd)
10223  {
10224  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10225  ++outInfo.unusedRangeCount;
10226  outInfo.unusedBytes += unusedRangeSize;
10227  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10228  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10229  }
10230 
10231  // End of loop.
10232  lastOffset = freeSpace2ndTo1stEnd;
10233  }
10234  }
10235  }
10236 
10237  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10238  const VkDeviceSize freeSpace1stTo2ndEnd =
10239  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10240  while(lastOffset < freeSpace1stTo2ndEnd)
10241  {
10242  // Find next non-null allocation or move nextAllocIndex to the end.
10243  while(nextAlloc1stIndex < suballoc1stCount &&
10244  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10245  {
10246  ++nextAlloc1stIndex;
10247  }
10248 
10249  // Found non-null allocation.
10250  if(nextAlloc1stIndex < suballoc1stCount)
10251  {
10252  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10253 
10254  // 1. Process free space before this allocation.
10255  if(lastOffset < suballoc.offset)
10256  {
10257  // There is free space from lastOffset to suballoc.offset.
10258  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10259  ++outInfo.unusedRangeCount;
10260  outInfo.unusedBytes += unusedRangeSize;
10261  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10262  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10263  }
10264 
10265  // 2. Process this allocation.
10266  // There is allocation with suballoc.offset, suballoc.size.
10267  outInfo.usedBytes += suballoc.size;
10268  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10269  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10270 
10271  // 3. Prepare for next iteration.
10272  lastOffset = suballoc.offset + suballoc.size;
10273  ++nextAlloc1stIndex;
10274  }
10275  // We are at the end.
10276  else
10277  {
10278  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10279  if(lastOffset < freeSpace1stTo2ndEnd)
10280  {
10281  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10282  ++outInfo.unusedRangeCount;
10283  outInfo.unusedBytes += unusedRangeSize;
10284  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10285  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10286  }
10287 
10288  // End of loop.
10289  lastOffset = freeSpace1stTo2ndEnd;
10290  }
10291  }
10292 
10293  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10294  {
10295  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10296  while(lastOffset < size)
10297  {
10298  // Find next non-null allocation or move nextAllocIndex to the end.
10299  while(nextAlloc2ndIndex != SIZE_MAX &&
10300  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10301  {
10302  --nextAlloc2ndIndex;
10303  }
10304 
10305  // Found non-null allocation.
10306  if(nextAlloc2ndIndex != SIZE_MAX)
10307  {
10308  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10309 
10310  // 1. Process free space before this allocation.
10311  if(lastOffset < suballoc.offset)
10312  {
10313  // There is free space from lastOffset to suballoc.offset.
10314  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10315  ++outInfo.unusedRangeCount;
10316  outInfo.unusedBytes += unusedRangeSize;
10317  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10318  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10319  }
10320 
10321  // 2. Process this allocation.
10322  // There is allocation with suballoc.offset, suballoc.size.
10323  outInfo.usedBytes += suballoc.size;
10324  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10325  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10326 
10327  // 3. Prepare for next iteration.
10328  lastOffset = suballoc.offset + suballoc.size;
10329  --nextAlloc2ndIndex;
10330  }
10331  // We are at the end.
10332  else
10333  {
10334  // There is free space from lastOffset to size.
10335  if(lastOffset < size)
10336  {
10337  const VkDeviceSize unusedRangeSize = size - lastOffset;
10338  ++outInfo.unusedRangeCount;
10339  outInfo.unusedBytes += unusedRangeSize;
10340  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10341  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10342  }
10343 
10344  // End of loop.
10345  lastOffset = size;
10346  }
10347  }
10348  }
10349 
10350  outInfo.unusedBytes = size - outInfo.usedBytes;
10351 }
10352 
10353 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10354 {
10355  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10356  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10357  const VkDeviceSize size = GetSize();
10358  const size_t suballoc1stCount = suballocations1st.size();
10359  const size_t suballoc2ndCount = suballocations2nd.size();
10360 
10361  inoutStats.size += size;
10362 
10363  VkDeviceSize lastOffset = 0;
10364 
10365  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10366  {
10367  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10368  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10369  while(lastOffset < freeSpace2ndTo1stEnd)
10370  {
10371  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10372  while(nextAlloc2ndIndex < suballoc2ndCount &&
10373  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10374  {
10375  ++nextAlloc2ndIndex;
10376  }
10377 
10378  // Found non-null allocation.
10379  if(nextAlloc2ndIndex < suballoc2ndCount)
10380  {
10381  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10382 
10383  // 1. Process free space before this allocation.
10384  if(lastOffset < suballoc.offset)
10385  {
10386  // There is free space from lastOffset to suballoc.offset.
10387  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10388  inoutStats.unusedSize += unusedRangeSize;
10389  ++inoutStats.unusedRangeCount;
10390  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10391  }
10392 
10393  // 2. Process this allocation.
10394  // There is allocation with suballoc.offset, suballoc.size.
10395  ++inoutStats.allocationCount;
10396 
10397  // 3. Prepare for next iteration.
10398  lastOffset = suballoc.offset + suballoc.size;
10399  ++nextAlloc2ndIndex;
10400  }
10401  // We are at the end.
10402  else
10403  {
10404  if(lastOffset < freeSpace2ndTo1stEnd)
10405  {
10406  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10407  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10408  inoutStats.unusedSize += unusedRangeSize;
10409  ++inoutStats.unusedRangeCount;
10410  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10411  }
10412 
10413  // End of loop.
10414  lastOffset = freeSpace2ndTo1stEnd;
10415  }
10416  }
10417  }
10418 
10419  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10420  const VkDeviceSize freeSpace1stTo2ndEnd =
10421  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10422  while(lastOffset < freeSpace1stTo2ndEnd)
10423  {
10424  // Find next non-null allocation or move nextAllocIndex to the end.
10425  while(nextAlloc1stIndex < suballoc1stCount &&
10426  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10427  {
10428  ++nextAlloc1stIndex;
10429  }
10430 
10431  // Found non-null allocation.
10432  if(nextAlloc1stIndex < suballoc1stCount)
10433  {
10434  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10435 
10436  // 1. Process free space before this allocation.
10437  if(lastOffset < suballoc.offset)
10438  {
10439  // There is free space from lastOffset to suballoc.offset.
10440  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10441  inoutStats.unusedSize += unusedRangeSize;
10442  ++inoutStats.unusedRangeCount;
10443  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10444  }
10445 
10446  // 2. Process this allocation.
10447  // There is allocation with suballoc.offset, suballoc.size.
10448  ++inoutStats.allocationCount;
10449 
10450  // 3. Prepare for next iteration.
10451  lastOffset = suballoc.offset + suballoc.size;
10452  ++nextAlloc1stIndex;
10453  }
10454  // We are at the end.
10455  else
10456  {
10457  if(lastOffset < freeSpace1stTo2ndEnd)
10458  {
10459  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10460  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10461  inoutStats.unusedSize += unusedRangeSize;
10462  ++inoutStats.unusedRangeCount;
10463  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10464  }
10465 
10466  // End of loop.
10467  lastOffset = freeSpace1stTo2ndEnd;
10468  }
10469  }
10470 
10471  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10472  {
10473  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10474  while(lastOffset < size)
10475  {
10476  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10477  while(nextAlloc2ndIndex != SIZE_MAX &&
10478  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10479  {
10480  --nextAlloc2ndIndex;
10481  }
10482 
10483  // Found non-null allocation.
10484  if(nextAlloc2ndIndex != SIZE_MAX)
10485  {
10486  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10487 
10488  // 1. Process free space before this allocation.
10489  if(lastOffset < suballoc.offset)
10490  {
10491  // There is free space from lastOffset to suballoc.offset.
10492  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10493  inoutStats.unusedSize += unusedRangeSize;
10494  ++inoutStats.unusedRangeCount;
10495  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10496  }
10497 
10498  // 2. Process this allocation.
10499  // There is allocation with suballoc.offset, suballoc.size.
10500  ++inoutStats.allocationCount;
10501 
10502  // 3. Prepare for next iteration.
10503  lastOffset = suballoc.offset + suballoc.size;
10504  --nextAlloc2ndIndex;
10505  }
10506  // We are at the end.
10507  else
10508  {
10509  if(lastOffset < size)
10510  {
10511  // There is free space from lastOffset to size.
10512  const VkDeviceSize unusedRangeSize = size - lastOffset;
10513  inoutStats.unusedSize += unusedRangeSize;
10514  ++inoutStats.unusedRangeCount;
10515  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10516  }
10517 
10518  // End of loop.
10519  lastOffset = size;
10520  }
10521  }
10522  }
10523 }
10524 
10525 #if VMA_STATS_STRING_ENABLED
10526 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10527 {
10528  const VkDeviceSize size = GetSize();
10529  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10530  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10531  const size_t suballoc1stCount = suballocations1st.size();
10532  const size_t suballoc2ndCount = suballocations2nd.size();
10533 
10534  // FIRST PASS
10535 
10536  size_t unusedRangeCount = 0;
10537  VkDeviceSize usedBytes = 0;
10538 
10539  VkDeviceSize lastOffset = 0;
10540 
10541  size_t alloc2ndCount = 0;
10542  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10543  {
10544  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10545  size_t nextAlloc2ndIndex = 0;
10546  while(lastOffset < freeSpace2ndTo1stEnd)
10547  {
10548  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10549  while(nextAlloc2ndIndex < suballoc2ndCount &&
10550  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10551  {
10552  ++nextAlloc2ndIndex;
10553  }
10554 
10555  // Found non-null allocation.
10556  if(nextAlloc2ndIndex < suballoc2ndCount)
10557  {
10558  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10559 
10560  // 1. Process free space before this allocation.
10561  if(lastOffset < suballoc.offset)
10562  {
10563  // There is free space from lastOffset to suballoc.offset.
10564  ++unusedRangeCount;
10565  }
10566 
10567  // 2. Process this allocation.
10568  // There is allocation with suballoc.offset, suballoc.size.
10569  ++alloc2ndCount;
10570  usedBytes += suballoc.size;
10571 
10572  // 3. Prepare for next iteration.
10573  lastOffset = suballoc.offset + suballoc.size;
10574  ++nextAlloc2ndIndex;
10575  }
10576  // We are at the end.
10577  else
10578  {
10579  if(lastOffset < freeSpace2ndTo1stEnd)
10580  {
10581  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10582  ++unusedRangeCount;
10583  }
10584 
10585  // End of loop.
10586  lastOffset = freeSpace2ndTo1stEnd;
10587  }
10588  }
10589  }
10590 
10591  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10592  size_t alloc1stCount = 0;
10593  const VkDeviceSize freeSpace1stTo2ndEnd =
10594  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10595  while(lastOffset < freeSpace1stTo2ndEnd)
10596  {
10597  // Find next non-null allocation or move nextAllocIndex to the end.
10598  while(nextAlloc1stIndex < suballoc1stCount &&
10599  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10600  {
10601  ++nextAlloc1stIndex;
10602  }
10603 
10604  // Found non-null allocation.
10605  if(nextAlloc1stIndex < suballoc1stCount)
10606  {
10607  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10608 
10609  // 1. Process free space before this allocation.
10610  if(lastOffset < suballoc.offset)
10611  {
10612  // There is free space from lastOffset to suballoc.offset.
10613  ++unusedRangeCount;
10614  }
10615 
10616  // 2. Process this allocation.
10617  // There is allocation with suballoc.offset, suballoc.size.
10618  ++alloc1stCount;
10619  usedBytes += suballoc.size;
10620 
10621  // 3. Prepare for next iteration.
10622  lastOffset = suballoc.offset + suballoc.size;
10623  ++nextAlloc1stIndex;
10624  }
10625  // We are at the end.
10626  else
10627  {
10628  if(lastOffset < size)
10629  {
10630  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10631  ++unusedRangeCount;
10632  }
10633 
10634  // End of loop.
10635  lastOffset = freeSpace1stTo2ndEnd;
10636  }
10637  }
10638 
10639  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10640  {
10641  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10642  while(lastOffset < size)
10643  {
10644  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10645  while(nextAlloc2ndIndex != SIZE_MAX &&
10646  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10647  {
10648  --nextAlloc2ndIndex;
10649  }
10650 
10651  // Found non-null allocation.
10652  if(nextAlloc2ndIndex != SIZE_MAX)
10653  {
10654  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10655 
10656  // 1. Process free space before this allocation.
10657  if(lastOffset < suballoc.offset)
10658  {
10659  // There is free space from lastOffset to suballoc.offset.
10660  ++unusedRangeCount;
10661  }
10662 
10663  // 2. Process this allocation.
10664  // There is allocation with suballoc.offset, suballoc.size.
10665  ++alloc2ndCount;
10666  usedBytes += suballoc.size;
10667 
10668  // 3. Prepare for next iteration.
10669  lastOffset = suballoc.offset + suballoc.size;
10670  --nextAlloc2ndIndex;
10671  }
10672  // We are at the end.
10673  else
10674  {
10675  if(lastOffset < size)
10676  {
10677  // There is free space from lastOffset to size.
10678  ++unusedRangeCount;
10679  }
10680 
10681  // End of loop.
10682  lastOffset = size;
10683  }
10684  }
10685  }
10686 
10687  const VkDeviceSize unusedBytes = size - usedBytes;
10688  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10689 
10690  // SECOND PASS
10691  lastOffset = 0;
10692 
10693  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10694  {
10695  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10696  size_t nextAlloc2ndIndex = 0;
10697  while(lastOffset < freeSpace2ndTo1stEnd)
10698  {
10699  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10700  while(nextAlloc2ndIndex < suballoc2ndCount &&
10701  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10702  {
10703  ++nextAlloc2ndIndex;
10704  }
10705 
10706  // Found non-null allocation.
10707  if(nextAlloc2ndIndex < suballoc2ndCount)
10708  {
10709  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10710 
10711  // 1. Process free space before this allocation.
10712  if(lastOffset < suballoc.offset)
10713  {
10714  // There is free space from lastOffset to suballoc.offset.
10715  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10716  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10717  }
10718 
10719  // 2. Process this allocation.
10720  // There is allocation with suballoc.offset, suballoc.size.
10721  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10722 
10723  // 3. Prepare for next iteration.
10724  lastOffset = suballoc.offset + suballoc.size;
10725  ++nextAlloc2ndIndex;
10726  }
10727  // We are at the end.
10728  else
10729  {
10730  if(lastOffset < freeSpace2ndTo1stEnd)
10731  {
10732  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10733  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10734  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10735  }
10736 
10737  // End of loop.
10738  lastOffset = freeSpace2ndTo1stEnd;
10739  }
10740  }
10741  }
10742 
10743  nextAlloc1stIndex = m_1stNullItemsBeginCount;
10744  while(lastOffset < freeSpace1stTo2ndEnd)
10745  {
10746  // Find next non-null allocation or move nextAllocIndex to the end.
10747  while(nextAlloc1stIndex < suballoc1stCount &&
10748  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10749  {
10750  ++nextAlloc1stIndex;
10751  }
10752 
10753  // Found non-null allocation.
10754  if(nextAlloc1stIndex < suballoc1stCount)
10755  {
10756  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10757 
10758  // 1. Process free space before this allocation.
10759  if(lastOffset < suballoc.offset)
10760  {
10761  // There is free space from lastOffset to suballoc.offset.
10762  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10763  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10764  }
10765 
10766  // 2. Process this allocation.
10767  // There is allocation with suballoc.offset, suballoc.size.
10768  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10769 
10770  // 3. Prepare for next iteration.
10771  lastOffset = suballoc.offset + suballoc.size;
10772  ++nextAlloc1stIndex;
10773  }
10774  // We are at the end.
10775  else
10776  {
10777  if(lastOffset < freeSpace1stTo2ndEnd)
10778  {
10779  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10780  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10781  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10782  }
10783 
10784  // End of loop.
10785  lastOffset = freeSpace1stTo2ndEnd;
10786  }
10787  }
10788 
10789  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10790  {
10791  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10792  while(lastOffset < size)
10793  {
10794  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10795  while(nextAlloc2ndIndex != SIZE_MAX &&
10796  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10797  {
10798  --nextAlloc2ndIndex;
10799  }
10800 
10801  // Found non-null allocation.
10802  if(nextAlloc2ndIndex != SIZE_MAX)
10803  {
10804  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10805 
10806  // 1. Process free space before this allocation.
10807  if(lastOffset < suballoc.offset)
10808  {
10809  // There is free space from lastOffset to suballoc.offset.
10810  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10811  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10812  }
10813 
10814  // 2. Process this allocation.
10815  // There is allocation with suballoc.offset, suballoc.size.
10816  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10817 
10818  // 3. Prepare for next iteration.
10819  lastOffset = suballoc.offset + suballoc.size;
10820  --nextAlloc2ndIndex;
10821  }
10822  // We are at the end.
10823  else
10824  {
10825  if(lastOffset < size)
10826  {
10827  // There is free space from lastOffset to size.
10828  const VkDeviceSize unusedRangeSize = size - lastOffset;
10829  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10830  }
10831 
10832  // End of loop.
10833  lastOffset = size;
10834  }
10835  }
10836  }
10837 
10838  PrintDetailedMap_End(json);
10839 }
10840 #endif // #if VMA_STATS_STRING_ENABLED
10841 
10842 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10843  uint32_t currentFrameIndex,
10844  uint32_t frameInUseCount,
10845  VkDeviceSize bufferImageGranularity,
10846  VkDeviceSize allocSize,
10847  VkDeviceSize allocAlignment,
10848  bool upperAddress,
10849  VmaSuballocationType allocType,
10850  bool canMakeOtherLost,
10851  uint32_t strategy,
10852  VmaAllocationRequest* pAllocationRequest)
10853 {
10854  VMA_ASSERT(allocSize > 0);
10855  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10856  VMA_ASSERT(pAllocationRequest != VMA_NULL);
10857  VMA_HEAVY_ASSERT(Validate());
10858  return upperAddress ?
10859  CreateAllocationRequest_UpperAddress(
10860  currentFrameIndex, frameInUseCount, bufferImageGranularity,
10861  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10862  CreateAllocationRequest_LowerAddress(
10863  currentFrameIndex, frameInUseCount, bufferImageGranularity,
10864  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10865 }
10866 
10867 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10868  uint32_t currentFrameIndex,
10869  uint32_t frameInUseCount,
10870  VkDeviceSize bufferImageGranularity,
10871  VkDeviceSize allocSize,
10872  VkDeviceSize allocAlignment,
10873  VmaSuballocationType allocType,
10874  bool canMakeOtherLost,
10875  uint32_t strategy,
10876  VmaAllocationRequest* pAllocationRequest)
10877 {
10878  const VkDeviceSize size = GetSize();
10879  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10880  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10881 
10882  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10883  {
10884  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10885  return false;
10886  }
10887 
10888  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10889  if(allocSize > size)
10890  {
10891  return false;
10892  }
10893  VkDeviceSize resultBaseOffset = size - allocSize;
10894  if(!suballocations2nd.empty())
10895  {
10896  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10897  resultBaseOffset = lastSuballoc.offset - allocSize;
10898  if(allocSize > lastSuballoc.offset)
10899  {
10900  return false;
10901  }
10902  }
10903 
10904  // Start from offset equal to end of free space.
10905  VkDeviceSize resultOffset = resultBaseOffset;
10906 
10907  // Apply VMA_DEBUG_MARGIN at the end.
10908  if(VMA_DEBUG_MARGIN > 0)
10909  {
10910  if(resultOffset < VMA_DEBUG_MARGIN)
10911  {
10912  return false;
10913  }
10914  resultOffset -= VMA_DEBUG_MARGIN;
10915  }
10916 
10917  // Apply alignment.
10918  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10919 
10920  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10921  // Make bigger alignment if necessary.
10922  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10923  {
10924  bool bufferImageGranularityConflict = false;
10925  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10926  {
10927  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10928  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10929  {
10930  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10931  {
10932  bufferImageGranularityConflict = true;
10933  break;
10934  }
10935  }
10936  else
10937  // Already on previous page.
10938  break;
10939  }
10940  if(bufferImageGranularityConflict)
10941  {
10942  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10943  }
10944  }
10945 
10946  // There is enough free space.
10947  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10948  suballocations1st.back().offset + suballocations1st.back().size :
10949  0;
10950  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10951  {
10952  // Check previous suballocations for BufferImageGranularity conflicts.
10953  // If conflict exists, allocation cannot be made here.
10954  if(bufferImageGranularity > 1)
10955  {
10956  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10957  {
10958  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10959  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10960  {
10961  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10962  {
10963  return false;
10964  }
10965  }
10966  else
10967  {
10968  // Already on next page.
10969  break;
10970  }
10971  }
10972  }
10973 
10974  // All tests passed: Success.
10975  pAllocationRequest->offset = resultOffset;
10976  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10977  pAllocationRequest->sumItemSize = 0;
10978  // pAllocationRequest->item unused.
10979  pAllocationRequest->itemsToMakeLostCount = 0;
10980  pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10981  return true;
10982  }
10983 
10984  return false;
10985 }
10986 
10987 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10988  uint32_t currentFrameIndex,
10989  uint32_t frameInUseCount,
10990  VkDeviceSize bufferImageGranularity,
10991  VkDeviceSize allocSize,
10992  VkDeviceSize allocAlignment,
10993  VmaSuballocationType allocType,
10994  bool canMakeOtherLost,
10995  uint32_t strategy,
10996  VmaAllocationRequest* pAllocationRequest)
10997 {
10998  const VkDeviceSize size = GetSize();
10999  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11000  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11001 
11002  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11003  {
11004  // Try to allocate at the end of 1st vector.
11005 
11006  VkDeviceSize resultBaseOffset = 0;
11007  if(!suballocations1st.empty())
11008  {
11009  const VmaSuballocation& lastSuballoc = suballocations1st.back();
11010  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11011  }
11012 
11013  // Start from offset equal to beginning of free space.
11014  VkDeviceSize resultOffset = resultBaseOffset;
11015 
11016  // Apply VMA_DEBUG_MARGIN at the beginning.
11017  if(VMA_DEBUG_MARGIN > 0)
11018  {
11019  resultOffset += VMA_DEBUG_MARGIN;
11020  }
11021 
11022  // Apply alignment.
11023  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11024 
11025  // Check previous suballocations for BufferImageGranularity conflicts.
11026  // Make bigger alignment if necessary.
11027  if(bufferImageGranularity > 1 && !suballocations1st.empty())
11028  {
11029  bool bufferImageGranularityConflict = false;
11030  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11031  {
11032  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11033  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11034  {
11035  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11036  {
11037  bufferImageGranularityConflict = true;
11038  break;
11039  }
11040  }
11041  else
11042  // Already on previous page.
11043  break;
11044  }
11045  if(bufferImageGranularityConflict)
11046  {
11047  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11048  }
11049  }
11050 
11051  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11052  suballocations2nd.back().offset : size;
11053 
11054  // There is enough free space at the end after alignment.
11055  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11056  {
11057  // Check next suballocations for BufferImageGranularity conflicts.
11058  // If conflict exists, allocation cannot be made here.
11059  if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11060  {
11061  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11062  {
11063  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11064  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11065  {
11066  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11067  {
11068  return false;
11069  }
11070  }
11071  else
11072  {
11073  // Already on previous page.
11074  break;
11075  }
11076  }
11077  }
11078 
11079  // All tests passed: Success.
11080  pAllocationRequest->offset = resultOffset;
11081  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11082  pAllocationRequest->sumItemSize = 0;
11083  // pAllocationRequest->item, customData unused.
11084  pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11085  pAllocationRequest->itemsToMakeLostCount = 0;
11086  return true;
11087  }
11088  }
11089 
11090  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11091  // beginning of 1st vector as the end of free space.
11092  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11093  {
11094  VMA_ASSERT(!suballocations1st.empty());
11095 
11096  VkDeviceSize resultBaseOffset = 0;
11097  if(!suballocations2nd.empty())
11098  {
11099  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11100  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11101  }
11102 
11103  // Start from offset equal to beginning of free space.
11104  VkDeviceSize resultOffset = resultBaseOffset;
11105 
11106  // Apply VMA_DEBUG_MARGIN at the beginning.
11107  if(VMA_DEBUG_MARGIN > 0)
11108  {
11109  resultOffset += VMA_DEBUG_MARGIN;
11110  }
11111 
11112  // Apply alignment.
11113  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11114 
11115  // Check previous suballocations for BufferImageGranularity conflicts.
11116  // Make bigger alignment if necessary.
11117  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
11118  {
11119  bool bufferImageGranularityConflict = false;
11120  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11121  {
11122  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11123  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11124  {
11125  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11126  {
11127  bufferImageGranularityConflict = true;
11128  break;
11129  }
11130  }
11131  else
11132  // Already on previous page.
11133  break;
11134  }
11135  if(bufferImageGranularityConflict)
11136  {
11137  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11138  }
11139  }
11140 
11141  pAllocationRequest->itemsToMakeLostCount = 0;
11142  pAllocationRequest->sumItemSize = 0;
11143  size_t index1st = m_1stNullItemsBeginCount;
11144 
11145  if(canMakeOtherLost)
11146  {
11147  while(index1st < suballocations1st.size() &&
11148  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11149  {
11150  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11151  const VmaSuballocation& suballoc = suballocations1st[index1st];
11152  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11153  {
11154  // No problem.
11155  }
11156  else
11157  {
11158  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11159  if(suballoc.hAllocation->CanBecomeLost() &&
11160  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11161  {
11162  ++pAllocationRequest->itemsToMakeLostCount;
11163  pAllocationRequest->sumItemSize += suballoc.size;
11164  }
11165  else
11166  {
11167  return false;
11168  }
11169  }
11170  ++index1st;
11171  }
11172 
11173  // Check next suballocations for BufferImageGranularity conflicts.
11174  // If conflict exists, we must mark more allocations lost or fail.
11175  if(bufferImageGranularity > 1)
11176  {
11177  while(index1st < suballocations1st.size())
11178  {
11179  const VmaSuballocation& suballoc = suballocations1st[index1st];
11180  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11181  {
11182  if(suballoc.hAllocation != VK_NULL_HANDLE)
11183  {
11184  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11185  if(suballoc.hAllocation->CanBecomeLost() &&
11186  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11187  {
11188  ++pAllocationRequest->itemsToMakeLostCount;
11189  pAllocationRequest->sumItemSize += suballoc.size;
11190  }
11191  else
11192  {
11193  return false;
11194  }
11195  }
11196  }
11197  else
11198  {
11199  // Already on next page.
11200  break;
11201  }
11202  ++index1st;
11203  }
11204  }
11205 
11206  // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11207  if(index1st == suballocations1st.size() &&
11208  resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11209  {
11210  // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11211  VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11212  }
11213  }
11214 
11215  // There is enough free space at the end after alignment.
11216  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11217  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11218  {
11219  // Check next suballocations for BufferImageGranularity conflicts.
11220  // If conflict exists, allocation cannot be made here.
11221  if(bufferImageGranularity > 1)
11222  {
11223  for(size_t nextSuballocIndex = index1st;
11224  nextSuballocIndex < suballocations1st.size();
11225  nextSuballocIndex++)
11226  {
11227  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11228  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11229  {
11230  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11231  {
11232  return false;
11233  }
11234  }
11235  else
11236  {
11237  // Already on next page.
11238  break;
11239  }
11240  }
11241  }
11242 
11243  // All tests passed: Success.
11244  pAllocationRequest->offset = resultOffset;
11245  pAllocationRequest->sumFreeSize =
11246  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11247  - resultBaseOffset
11248  - pAllocationRequest->sumItemSize;
11249  pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11250  // pAllocationRequest->item, customData unused.
11251  return true;
11252  }
11253  }
11254 
11255  return false;
11256 }
11257 
11258 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11259  uint32_t currentFrameIndex,
11260  uint32_t frameInUseCount,
11261  VmaAllocationRequest* pAllocationRequest)
11262 {
11263  if(pAllocationRequest->itemsToMakeLostCount == 0)
11264  {
11265  return true;
11266  }
11267 
11268  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11269 
11270  // We always start from 1st.
11271  SuballocationVectorType* suballocations = &AccessSuballocations1st();
11272  size_t index = m_1stNullItemsBeginCount;
11273  size_t madeLostCount = 0;
11274  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11275  {
11276  if(index == suballocations->size())
11277  {
11278  index = 0;
11279  // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11280  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11281  {
11282  suballocations = &AccessSuballocations2nd();
11283  }
11284  // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11285  // suballocations continues pointing at AccessSuballocations1st().
11286  VMA_ASSERT(!suballocations->empty());
11287  }
11288  VmaSuballocation& suballoc = (*suballocations)[index];
11289  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11290  {
11291  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11292  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11293  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11294  {
11295  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11296  suballoc.hAllocation = VK_NULL_HANDLE;
11297  m_SumFreeSize += suballoc.size;
11298  if(suballocations == &AccessSuballocations1st())
11299  {
11300  ++m_1stNullItemsMiddleCount;
11301  }
11302  else
11303  {
11304  ++m_2ndNullItemsCount;
11305  }
11306  ++madeLostCount;
11307  }
11308  else
11309  {
11310  return false;
11311  }
11312  }
11313  ++index;
11314  }
11315 
11316  CleanupAfterFree();
11317  //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11318 
11319  return true;
11320 }
11321 
11322 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11323 {
11324  uint32_t lostAllocationCount = 0;
11325 
11326  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11327  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11328  {
11329  VmaSuballocation& suballoc = suballocations1st[i];
11330  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11331  suballoc.hAllocation->CanBecomeLost() &&
11332  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11333  {
11334  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11335  suballoc.hAllocation = VK_NULL_HANDLE;
11336  ++m_1stNullItemsMiddleCount;
11337  m_SumFreeSize += suballoc.size;
11338  ++lostAllocationCount;
11339  }
11340  }
11341 
11342  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11343  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11344  {
11345  VmaSuballocation& suballoc = suballocations2nd[i];
11346  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11347  suballoc.hAllocation->CanBecomeLost() &&
11348  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11349  {
11350  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11351  suballoc.hAllocation = VK_NULL_HANDLE;
11352  ++m_2ndNullItemsCount;
11353  m_SumFreeSize += suballoc.size;
11354  ++lostAllocationCount;
11355  }
11356  }
11357 
11358  if(lostAllocationCount)
11359  {
11360  CleanupAfterFree();
11361  }
11362 
11363  return lostAllocationCount;
11364 }
11365 
11366 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11367 {
11368  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11369  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11370  {
11371  const VmaSuballocation& suballoc = suballocations1st[i];
11372  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11373  {
11374  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11375  {
11376  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11377  return VK_ERROR_VALIDATION_FAILED_EXT;
11378  }
11379  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11380  {
11381  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11382  return VK_ERROR_VALIDATION_FAILED_EXT;
11383  }
11384  }
11385  }
11386 
11387  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11388  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11389  {
11390  const VmaSuballocation& suballoc = suballocations2nd[i];
11391  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11392  {
11393  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11394  {
11395  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11396  return VK_ERROR_VALIDATION_FAILED_EXT;
11397  }
11398  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11399  {
11400  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11401  return VK_ERROR_VALIDATION_FAILED_EXT;
11402  }
11403  }
11404  }
11405 
11406  return VK_SUCCESS;
11407 }
11408 
11409 void VmaBlockMetadata_Linear::Alloc(
11410  const VmaAllocationRequest& request,
11411  VmaSuballocationType type,
11412  VkDeviceSize allocSize,
11413  VmaAllocation hAllocation)
11414 {
11415  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11416 
11417  switch(request.type)
11418  {
11419  case VmaAllocationRequestType::UpperAddress:
11420  {
11421  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11422  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11423  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11424  suballocations2nd.push_back(newSuballoc);
11425  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11426  }
11427  break;
11428  case VmaAllocationRequestType::EndOf1st:
11429  {
11430  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11431 
11432  VMA_ASSERT(suballocations1st.empty() ||
11433  request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11434  // Check if it fits before the end of the block.
11435  VMA_ASSERT(request.offset + allocSize <= GetSize());
11436 
11437  suballocations1st.push_back(newSuballoc);
11438  }
11439  break;
11440  case VmaAllocationRequestType::EndOf2nd:
11441  {
11442  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11443  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11444  VMA_ASSERT(!suballocations1st.empty() &&
11445  request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11446  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11447 
11448  switch(m_2ndVectorMode)
11449  {
11450  case SECOND_VECTOR_EMPTY:
11451  // First allocation from second part ring buffer.
11452  VMA_ASSERT(suballocations2nd.empty());
11453  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11454  break;
11455  case SECOND_VECTOR_RING_BUFFER:
11456  // 2-part ring buffer is already started.
11457  VMA_ASSERT(!suballocations2nd.empty());
11458  break;
11459  case SECOND_VECTOR_DOUBLE_STACK:
11460  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11461  break;
11462  default:
11463  VMA_ASSERT(0);
11464  }
11465 
11466  suballocations2nd.push_back(newSuballoc);
11467  }
11468  break;
11469  default:
11470  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11471  }
11472 
11473  m_SumFreeSize -= newSuballoc.size;
11474 }
11475 
11476 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11477 {
11478  FreeAtOffset(allocation->GetOffset());
11479 }
11480 
11481 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11482 {
11483  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11484  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11485 
11486  if(!suballocations1st.empty())
11487  {
11488  // First allocation: Mark it as next empty at the beginning.
11489  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11490  if(firstSuballoc.offset == offset)
11491  {
11492  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11493  firstSuballoc.hAllocation = VK_NULL_HANDLE;
11494  m_SumFreeSize += firstSuballoc.size;
11495  ++m_1stNullItemsBeginCount;
11496  CleanupAfterFree();
11497  return;
11498  }
11499  }
11500 
11501  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11502  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11503  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11504  {
11505  VmaSuballocation& lastSuballoc = suballocations2nd.back();
11506  if(lastSuballoc.offset == offset)
11507  {
11508  m_SumFreeSize += lastSuballoc.size;
11509  suballocations2nd.pop_back();
11510  CleanupAfterFree();
11511  return;
11512  }
11513  }
11514  // Last allocation in 1st vector.
11515  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11516  {
11517  VmaSuballocation& lastSuballoc = suballocations1st.back();
11518  if(lastSuballoc.offset == offset)
11519  {
11520  m_SumFreeSize += lastSuballoc.size;
11521  suballocations1st.pop_back();
11522  CleanupAfterFree();
11523  return;
11524  }
11525  }
11526 
11527  // Item from the middle of 1st vector.
11528  {
11529  VmaSuballocation refSuballoc;
11530  refSuballoc.offset = offset;
11531  // Rest of members stays uninitialized intentionally for better performance.
11532  SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11533  suballocations1st.begin() + m_1stNullItemsBeginCount,
11534  suballocations1st.end(),
11535  refSuballoc,
11536  VmaSuballocationOffsetLess());
11537  if(it != suballocations1st.end())
11538  {
11539  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11540  it->hAllocation = VK_NULL_HANDLE;
11541  ++m_1stNullItemsMiddleCount;
11542  m_SumFreeSize += it->size;
11543  CleanupAfterFree();
11544  return;
11545  }
11546  }
11547 
11548  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11549  {
11550  // Item from the middle of 2nd vector.
11551  VmaSuballocation refSuballoc;
11552  refSuballoc.offset = offset;
11553  // Rest of members stays uninitialized intentionally for better performance.
11554  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11555  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11556  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11557  if(it != suballocations2nd.end())
11558  {
11559  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11560  it->hAllocation = VK_NULL_HANDLE;
11561  ++m_2ndNullItemsCount;
11562  m_SumFreeSize += it->size;
11563  CleanupAfterFree();
11564  return;
11565  }
11566  }
11567 
11568  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11569 }
11570 
11571 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11572 {
11573  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11574  const size_t suballocCount = AccessSuballocations1st().size();
11575  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11576 }
11577 
11578 void VmaBlockMetadata_Linear::CleanupAfterFree()
11579 {
11580  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11581  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11582 
11583  if(IsEmpty())
11584  {
11585  suballocations1st.clear();
11586  suballocations2nd.clear();
11587  m_1stNullItemsBeginCount = 0;
11588  m_1stNullItemsMiddleCount = 0;
11589  m_2ndNullItemsCount = 0;
11590  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11591  }
11592  else
11593  {
11594  const size_t suballoc1stCount = suballocations1st.size();
11595  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11596  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11597 
11598  // Find more null items at the beginning of 1st vector.
11599  while(m_1stNullItemsBeginCount < suballoc1stCount &&
11600  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11601  {
11602  ++m_1stNullItemsBeginCount;
11603  --m_1stNullItemsMiddleCount;
11604  }
11605 
11606  // Find more null items at the end of 1st vector.
11607  while(m_1stNullItemsMiddleCount > 0 &&
11608  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11609  {
11610  --m_1stNullItemsMiddleCount;
11611  suballocations1st.pop_back();
11612  }
11613 
11614  // Find more null items at the end of 2nd vector.
11615  while(m_2ndNullItemsCount > 0 &&
11616  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11617  {
11618  --m_2ndNullItemsCount;
11619  suballocations2nd.pop_back();
11620  }
11621 
11622  // Find more null items at the beginning of 2nd vector.
11623  while(m_2ndNullItemsCount > 0 &&
11624  suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11625  {
11626  --m_2ndNullItemsCount;
11627  VmaVectorRemove(suballocations2nd, 0);
11628  }
11629 
11630  if(ShouldCompact1st())
11631  {
11632  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11633  size_t srcIndex = m_1stNullItemsBeginCount;
11634  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11635  {
11636  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11637  {
11638  ++srcIndex;
11639  }
11640  if(dstIndex != srcIndex)
11641  {
11642  suballocations1st[dstIndex] = suballocations1st[srcIndex];
11643  }
11644  ++srcIndex;
11645  }
11646  suballocations1st.resize(nonNullItemCount);
11647  m_1stNullItemsBeginCount = 0;
11648  m_1stNullItemsMiddleCount = 0;
11649  }
11650 
11651  // 2nd vector became empty.
11652  if(suballocations2nd.empty())
11653  {
11654  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11655  }
11656 
11657  // 1st vector became empty.
11658  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11659  {
11660  suballocations1st.clear();
11661  m_1stNullItemsBeginCount = 0;
11662 
11663  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11664  {
11665  // Swap 1st with 2nd. Now 2nd is empty.
11666  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11667  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11668  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11669  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11670  {
11671  ++m_1stNullItemsBeginCount;
11672  --m_1stNullItemsMiddleCount;
11673  }
11674  m_2ndNullItemsCount = 0;
11675  m_1stVectorIndex ^= 1;
11676  }
11677  }
11678  }
11679 
11680  VMA_HEAVY_ASSERT(Validate());
11681 }
11682 
11683 
11685 // class VmaBlockMetadata_Buddy
11686 
11687 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11688  VmaBlockMetadata(hAllocator),
11689  m_Root(VMA_NULL),
11690  m_AllocationCount(0),
11691  m_FreeCount(1),
11692  m_SumFreeSize(0)
11693 {
11694  memset(m_FreeList, 0, sizeof(m_FreeList));
11695 }
11696 
11697 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11698 {
11699  DeleteNode(m_Root);
11700 }
11701 
11702 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11703 {
11704  VmaBlockMetadata::Init(size);
11705 
11706  m_UsableSize = VmaPrevPow2(size);
11707  m_SumFreeSize = m_UsableSize;
11708 
11709  // Calculate m_LevelCount.
11710  m_LevelCount = 1;
11711  while(m_LevelCount < MAX_LEVELS &&
11712  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11713  {
11714  ++m_LevelCount;
11715  }
11716 
11717  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11718  rootNode->offset = 0;
11719  rootNode->type = Node::TYPE_FREE;
11720  rootNode->parent = VMA_NULL;
11721  rootNode->buddy = VMA_NULL;
11722 
11723  m_Root = rootNode;
11724  AddToFreeListFront(0, rootNode);
11725 }
11726 
11727 bool VmaBlockMetadata_Buddy::Validate() const
11728 {
11729  // Validate tree.
11730  ValidationContext ctx;
11731  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11732  {
11733  VMA_VALIDATE(false && "ValidateNode failed.");
11734  }
11735  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11736  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11737 
11738  // Validate free node lists.
11739  for(uint32_t level = 0; level < m_LevelCount; ++level)
11740  {
11741  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11742  m_FreeList[level].front->free.prev == VMA_NULL);
11743 
11744  for(Node* node = m_FreeList[level].front;
11745  node != VMA_NULL;
11746  node = node->free.next)
11747  {
11748  VMA_VALIDATE(node->type == Node::TYPE_FREE);
11749 
11750  if(node->free.next == VMA_NULL)
11751  {
11752  VMA_VALIDATE(m_FreeList[level].back == node);
11753  }
11754  else
11755  {
11756  VMA_VALIDATE(node->free.next->free.prev == node);
11757  }
11758  }
11759  }
11760 
11761  // Validate that free lists ar higher levels are empty.
11762  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11763  {
11764  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11765  }
11766 
11767  return true;
11768 }
11769 
11770 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11771 {
11772  for(uint32_t level = 0; level < m_LevelCount; ++level)
11773  {
11774  if(m_FreeList[level].front != VMA_NULL)
11775  {
11776  return LevelToNodeSize(level);
11777  }
11778  }
11779  return 0;
11780 }
11781 
11782 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11783 {
11784  const VkDeviceSize unusableSize = GetUnusableSize();
11785 
11786  outInfo.blockCount = 1;
11787 
11788  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11789  outInfo.usedBytes = outInfo.unusedBytes = 0;
11790 
11791  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11792  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11793  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11794 
11795  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11796 
11797  if(unusableSize > 0)
11798  {
11799  ++outInfo.unusedRangeCount;
11800  outInfo.unusedBytes += unusableSize;
11801  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11802  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11803  }
11804 }
11805 
11806 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11807 {
11808  const VkDeviceSize unusableSize = GetUnusableSize();
11809 
11810  inoutStats.size += GetSize();
11811  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11812  inoutStats.allocationCount += m_AllocationCount;
11813  inoutStats.unusedRangeCount += m_FreeCount;
11814  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11815 
11816  if(unusableSize > 0)
11817  {
11818  ++inoutStats.unusedRangeCount;
11819  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11820  }
11821 }
11822 
11823 #if VMA_STATS_STRING_ENABLED
11824 
11825 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11826 {
11827  // TODO optimize
11828  VmaStatInfo stat;
11829  CalcAllocationStatInfo(stat);
11830 
11831  PrintDetailedMap_Begin(
11832  json,
11833  stat.unusedBytes,
11834  stat.allocationCount,
11835  stat.unusedRangeCount);
11836 
11837  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11838 
11839  const VkDeviceSize unusableSize = GetUnusableSize();
11840  if(unusableSize > 0)
11841  {
11842  PrintDetailedMap_UnusedRange(json,
11843  m_UsableSize, // offset
11844  unusableSize); // size
11845  }
11846 
11847  PrintDetailedMap_End(json);
11848 }
11849 
11850 #endif // #if VMA_STATS_STRING_ENABLED
11851 
11852 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11853  uint32_t currentFrameIndex,
11854  uint32_t frameInUseCount,
11855  VkDeviceSize bufferImageGranularity,
11856  VkDeviceSize allocSize,
11857  VkDeviceSize allocAlignment,
11858  bool upperAddress,
11859  VmaSuballocationType allocType,
11860  bool canMakeOtherLost,
11861  uint32_t strategy,
11862  VmaAllocationRequest* pAllocationRequest)
11863 {
11864  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11865 
11866  // Simple way to respect bufferImageGranularity. May be optimized some day.
11867  // Whenever it might be an OPTIMAL image...
11868  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11869  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11870  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11871  {
11872  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11873  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11874  }
11875 
11876  if(allocSize > m_UsableSize)
11877  {
11878  return false;
11879  }
11880 
11881  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11882  for(uint32_t level = targetLevel + 1; level--; )
11883  {
11884  for(Node* freeNode = m_FreeList[level].front;
11885  freeNode != VMA_NULL;
11886  freeNode = freeNode->free.next)
11887  {
11888  if(freeNode->offset % allocAlignment == 0)
11889  {
11890  pAllocationRequest->type = VmaAllocationRequestType::Normal;
11891  pAllocationRequest->offset = freeNode->offset;
11892  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11893  pAllocationRequest->sumItemSize = 0;
11894  pAllocationRequest->itemsToMakeLostCount = 0;
11895  pAllocationRequest->customData = (void*)(uintptr_t)level;
11896  return true;
11897  }
11898  }
11899  }
11900 
11901  return false;
11902 }
11903 
11904 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11905  uint32_t currentFrameIndex,
11906  uint32_t frameInUseCount,
11907  VmaAllocationRequest* pAllocationRequest)
11908 {
11909  /*
11910  Lost allocations are not supported in buddy allocator at the moment.
11911  Support might be added in the future.
11912  */
11913  return pAllocationRequest->itemsToMakeLostCount == 0;
11914 }
11915 
11916 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11917 {
11918  /*
11919  Lost allocations are not supported in buddy allocator at the moment.
11920  Support might be added in the future.
11921  */
11922  return 0;
11923 }
11924 
11925 void VmaBlockMetadata_Buddy::Alloc(
11926  const VmaAllocationRequest& request,
11927  VmaSuballocationType type,
11928  VkDeviceSize allocSize,
11929  VmaAllocation hAllocation)
11930 {
11931  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11932 
11933  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11934  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11935 
11936  Node* currNode = m_FreeList[currLevel].front;
11937  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11938  while(currNode->offset != request.offset)
11939  {
11940  currNode = currNode->free.next;
11941  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11942  }
11943 
11944  // Go down, splitting free nodes.
11945  while(currLevel < targetLevel)
11946  {
11947  // currNode is already first free node at currLevel.
11948  // Remove it from list of free nodes at this currLevel.
11949  RemoveFromFreeList(currLevel, currNode);
11950 
11951  const uint32_t childrenLevel = currLevel + 1;
11952 
11953  // Create two free sub-nodes.
11954  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11955  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11956 
11957  leftChild->offset = currNode->offset;
11958  leftChild->type = Node::TYPE_FREE;
11959  leftChild->parent = currNode;
11960  leftChild->buddy = rightChild;
11961 
11962  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11963  rightChild->type = Node::TYPE_FREE;
11964  rightChild->parent = currNode;
11965  rightChild->buddy = leftChild;
11966 
11967  // Convert current currNode to split type.
11968  currNode->type = Node::TYPE_SPLIT;
11969  currNode->split.leftChild = leftChild;
11970 
11971  // Add child nodes to free list. Order is important!
11972  AddToFreeListFront(childrenLevel, rightChild);
11973  AddToFreeListFront(childrenLevel, leftChild);
11974 
11975  ++m_FreeCount;
11976  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11977  ++currLevel;
11978  currNode = m_FreeList[currLevel].front;
11979 
11980  /*
11981  We can be sure that currNode, as left child of node previously split,
11982  also fullfills the alignment requirement.
11983  */
11984  }
11985 
11986  // Remove from free list.
11987  VMA_ASSERT(currLevel == targetLevel &&
11988  currNode != VMA_NULL &&
11989  currNode->type == Node::TYPE_FREE);
11990  RemoveFromFreeList(currLevel, currNode);
11991 
11992  // Convert to allocation node.
11993  currNode->type = Node::TYPE_ALLOCATION;
11994  currNode->allocation.alloc = hAllocation;
11995 
11996  ++m_AllocationCount;
11997  --m_FreeCount;
11998  m_SumFreeSize -= allocSize;
11999 }
12000 
12001 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
12002 {
12003  if(node->type == Node::TYPE_SPLIT)
12004  {
12005  DeleteNode(node->split.leftChild->buddy);
12006  DeleteNode(node->split.leftChild);
12007  }
12008 
12009  vma_delete(GetAllocationCallbacks(), node);
12010 }
12011 
12012 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
12013 {
12014  VMA_VALIDATE(level < m_LevelCount);
12015  VMA_VALIDATE(curr->parent == parent);
12016  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
12017  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
12018  switch(curr->type)
12019  {
12020  case Node::TYPE_FREE:
12021  // curr->free.prev, next are validated separately.
12022  ctx.calculatedSumFreeSize += levelNodeSize;
12023  ++ctx.calculatedFreeCount;
12024  break;
12025  case Node::TYPE_ALLOCATION:
12026  ++ctx.calculatedAllocationCount;
12027  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
12028  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
12029  break;
12030  case Node::TYPE_SPLIT:
12031  {
12032  const uint32_t childrenLevel = level + 1;
12033  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
12034  const Node* const leftChild = curr->split.leftChild;
12035  VMA_VALIDATE(leftChild != VMA_NULL);
12036  VMA_VALIDATE(leftChild->offset == curr->offset);
12037  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12038  {
12039  VMA_VALIDATE(false && "ValidateNode for left child failed.");
12040  }
12041  const Node* const rightChild = leftChild->buddy;
12042  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12043  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12044  {
12045  VMA_VALIDATE(false && "ValidateNode for right child failed.");
12046  }
12047  }
12048  break;
12049  default:
12050  return false;
12051  }
12052 
12053  return true;
12054 }
12055 
12056 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12057 {
12058  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12059  uint32_t level = 0;
12060  VkDeviceSize currLevelNodeSize = m_UsableSize;
12061  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12062  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12063  {
12064  ++level;
12065  currLevelNodeSize = nextLevelNodeSize;
12066  nextLevelNodeSize = currLevelNodeSize >> 1;
12067  }
12068  return level;
12069 }
12070 
12071 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12072 {
12073  // Find node and level.
12074  Node* node = m_Root;
12075  VkDeviceSize nodeOffset = 0;
12076  uint32_t level = 0;
12077  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12078  while(node->type == Node::TYPE_SPLIT)
12079  {
12080  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12081  if(offset < nodeOffset + nextLevelSize)
12082  {
12083  node = node->split.leftChild;
12084  }
12085  else
12086  {
12087  node = node->split.leftChild->buddy;
12088  nodeOffset += nextLevelSize;
12089  }
12090  ++level;
12091  levelNodeSize = nextLevelSize;
12092  }
12093 
12094  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12095  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12096 
12097  ++m_FreeCount;
12098  --m_AllocationCount;
12099  m_SumFreeSize += alloc->GetSize();
12100 
12101  node->type = Node::TYPE_FREE;
12102 
12103  // Join free nodes if possible.
12104  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12105  {
12106  RemoveFromFreeList(level, node->buddy);
12107  Node* const parent = node->parent;
12108 
12109  vma_delete(GetAllocationCallbacks(), node->buddy);
12110  vma_delete(GetAllocationCallbacks(), node);
12111  parent->type = Node::TYPE_FREE;
12112 
12113  node = parent;
12114  --level;
12115  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12116  --m_FreeCount;
12117  }
12118 
12119  AddToFreeListFront(level, node);
12120 }
12121 
12122 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12123 {
12124  switch(node->type)
12125  {
12126  case Node::TYPE_FREE:
12127  ++outInfo.unusedRangeCount;
12128  outInfo.unusedBytes += levelNodeSize;
12129  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12130  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12131  break;
12132  case Node::TYPE_ALLOCATION:
12133  {
12134  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12135  ++outInfo.allocationCount;
12136  outInfo.usedBytes += allocSize;
12137  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12138  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12139 
12140  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12141  if(unusedRangeSize > 0)
12142  {
12143  ++outInfo.unusedRangeCount;
12144  outInfo.unusedBytes += unusedRangeSize;
12145  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12146  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12147  }
12148  }
12149  break;
12150  case Node::TYPE_SPLIT:
12151  {
12152  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12153  const Node* const leftChild = node->split.leftChild;
12154  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12155  const Node* const rightChild = leftChild->buddy;
12156  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12157  }
12158  break;
12159  default:
12160  VMA_ASSERT(0);
12161  }
12162 }
12163 
12164 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12165 {
12166  VMA_ASSERT(node->type == Node::TYPE_FREE);
12167 
12168  // List is empty.
12169  Node* const frontNode = m_FreeList[level].front;
12170  if(frontNode == VMA_NULL)
12171  {
12172  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12173  node->free.prev = node->free.next = VMA_NULL;
12174  m_FreeList[level].front = m_FreeList[level].back = node;
12175  }
12176  else
12177  {
12178  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12179  node->free.prev = VMA_NULL;
12180  node->free.next = frontNode;
12181  frontNode->free.prev = node;
12182  m_FreeList[level].front = node;
12183  }
12184 }
12185 
12186 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12187 {
12188  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12189 
12190  // It is at the front.
12191  if(node->free.prev == VMA_NULL)
12192  {
12193  VMA_ASSERT(m_FreeList[level].front == node);
12194  m_FreeList[level].front = node->free.next;
12195  }
12196  else
12197  {
12198  Node* const prevFreeNode = node->free.prev;
12199  VMA_ASSERT(prevFreeNode->free.next == node);
12200  prevFreeNode->free.next = node->free.next;
12201  }
12202 
12203  // It is at the back.
12204  if(node->free.next == VMA_NULL)
12205  {
12206  VMA_ASSERT(m_FreeList[level].back == node);
12207  m_FreeList[level].back = node->free.prev;
12208  }
12209  else
12210  {
12211  Node* const nextFreeNode = node->free.next;
12212  VMA_ASSERT(nextFreeNode->free.prev == node);
12213  nextFreeNode->free.prev = node->free.prev;
12214  }
12215 }
12216 
12217 #if VMA_STATS_STRING_ENABLED
12218 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12219 {
12220  switch(node->type)
12221  {
12222  case Node::TYPE_FREE:
12223  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12224  break;
12225  case Node::TYPE_ALLOCATION:
12226  {
12227  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12228  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12229  if(allocSize < levelNodeSize)
12230  {
12231  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12232  }
12233  }
12234  break;
12235  case Node::TYPE_SPLIT:
12236  {
12237  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12238  const Node* const leftChild = node->split.leftChild;
12239  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12240  const Node* const rightChild = leftChild->buddy;
12241  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12242  }
12243  break;
12244  default:
12245  VMA_ASSERT(0);
12246  }
12247 }
12248 #endif // #if VMA_STATS_STRING_ENABLED
12249 
12250 
12252 // class VmaDeviceMemoryBlock
12253 
12254 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12255  m_pMetadata(VMA_NULL),
12256  m_MemoryTypeIndex(UINT32_MAX),
12257  m_Id(0),
12258  m_hMemory(VK_NULL_HANDLE),
12259  m_MapCount(0),
12260  m_pMappedData(VMA_NULL)
12261 {
12262 }
12263 
12264 void VmaDeviceMemoryBlock::Init(
12265  VmaAllocator hAllocator,
12266  VmaPool hParentPool,
12267  uint32_t newMemoryTypeIndex,
12268  VkDeviceMemory newMemory,
12269  VkDeviceSize newSize,
12270  uint32_t id,
12271  uint32_t algorithm)
12272 {
12273  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12274 
12275  m_hParentPool = hParentPool;
12276  m_MemoryTypeIndex = newMemoryTypeIndex;
12277  m_Id = id;
12278  m_hMemory = newMemory;
12279 
12280  switch(algorithm)
12281  {
12283  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12284  break;
12286  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12287  break;
12288  default:
12289  VMA_ASSERT(0);
12290  // Fall-through.
12291  case 0:
12292  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12293  }
12294  m_pMetadata->Init(newSize);
12295 }
12296 
12297 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12298 {
12299  // This is the most important assert in the entire library.
12300  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12301  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12302 
12303  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12304  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12305  m_hMemory = VK_NULL_HANDLE;
12306 
12307  vma_delete(allocator, m_pMetadata);
12308  m_pMetadata = VMA_NULL;
12309 }
12310 
12311 bool VmaDeviceMemoryBlock::Validate() const
12312 {
12313  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12314  (m_pMetadata->GetSize() != 0));
12315 
12316  return m_pMetadata->Validate();
12317 }
12318 
12319 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12320 {
12321  void* pData = nullptr;
12322  VkResult res = Map(hAllocator, 1, &pData);
12323  if(res != VK_SUCCESS)
12324  {
12325  return res;
12326  }
12327 
12328  res = m_pMetadata->CheckCorruption(pData);
12329 
12330  Unmap(hAllocator, 1);
12331 
12332  return res;
12333 }
12334 
12335 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12336 {
12337  if(count == 0)
12338  {
12339  return VK_SUCCESS;
12340  }
12341 
12342  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12343  if(m_MapCount != 0)
12344  {
12345  m_MapCount += count;
12346  VMA_ASSERT(m_pMappedData != VMA_NULL);
12347  if(ppData != VMA_NULL)
12348  {
12349  *ppData = m_pMappedData;
12350  }
12351  return VK_SUCCESS;
12352  }
12353  else
12354  {
12355  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12356  hAllocator->m_hDevice,
12357  m_hMemory,
12358  0, // offset
12359  VK_WHOLE_SIZE,
12360  0, // flags
12361  &m_pMappedData);
12362  if(result == VK_SUCCESS)
12363  {
12364  if(ppData != VMA_NULL)
12365  {
12366  *ppData = m_pMappedData;
12367  }
12368  m_MapCount = count;
12369  }
12370  return result;
12371  }
12372 }
12373 
12374 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12375 {
12376  if(count == 0)
12377  {
12378  return;
12379  }
12380 
12381  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12382  if(m_MapCount >= count)
12383  {
12384  m_MapCount -= count;
12385  if(m_MapCount == 0)
12386  {
12387  m_pMappedData = VMA_NULL;
12388  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12389  }
12390  }
12391  else
12392  {
12393  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12394  }
12395 }
12396 
12397 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12398 {
12399  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12400  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12401 
12402  void* pData;
12403  VkResult res = Map(hAllocator, 1, &pData);
12404  if(res != VK_SUCCESS)
12405  {
12406  return res;
12407  }
12408 
12409  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12410  VmaWriteMagicValue(pData, allocOffset + allocSize);
12411 
12412  Unmap(hAllocator, 1);
12413 
12414  return VK_SUCCESS;
12415 }
12416 
12417 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12418 {
12419  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12420  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12421 
12422  void* pData;
12423  VkResult res = Map(hAllocator, 1, &pData);
12424  if(res != VK_SUCCESS)
12425  {
12426  return res;
12427  }
12428 
12429  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12430  {
12431  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12432  }
12433  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12434  {
12435  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12436  }
12437 
12438  Unmap(hAllocator, 1);
12439 
12440  return VK_SUCCESS;
12441 }
12442 
12443 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12444  const VmaAllocator hAllocator,
12445  const VmaAllocation hAllocation,
12446  VkDeviceSize allocationLocalOffset,
12447  VkBuffer hBuffer,
12448  const void* pNext)
12449 {
12450  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12451  hAllocation->GetBlock() == this);
12452  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12453  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12454  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12455  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12456  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12457  return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12458 }
12459 
12460 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12461  const VmaAllocator hAllocator,
12462  const VmaAllocation hAllocation,
12463  VkDeviceSize allocationLocalOffset,
12464  VkImage hImage,
12465  const void* pNext)
12466 {
12467  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12468  hAllocation->GetBlock() == this);
12469  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12470  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12471  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12472  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12473  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12474  return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12475 }
12476 
12477 static void InitStatInfo(VmaStatInfo& outInfo)
12478 {
12479  memset(&outInfo, 0, sizeof(outInfo));
12480  outInfo.allocationSizeMin = UINT64_MAX;
12481  outInfo.unusedRangeSizeMin = UINT64_MAX;
12482 }
12483 
12484 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12485 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12486 {
12487  inoutInfo.blockCount += srcInfo.blockCount;
12488  inoutInfo.allocationCount += srcInfo.allocationCount;
12489  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12490  inoutInfo.usedBytes += srcInfo.usedBytes;
12491  inoutInfo.unusedBytes += srcInfo.unusedBytes;
12492  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12493  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12494  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12495  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12496 }
12497 
12498 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12499 {
12500  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12501  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12502  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12503  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12504 }
12505 
12506 VmaPool_T::VmaPool_T(
12507  VmaAllocator hAllocator,
12508  const VmaPoolCreateInfo& createInfo,
12509  VkDeviceSize preferredBlockSize) :
12510  m_BlockVector(
12511  hAllocator,
12512  this, // hParentPool
12513  createInfo.memoryTypeIndex,
12514  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12515  createInfo.minBlockCount,
12516  createInfo.maxBlockCount,
12517  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12518  createInfo.frameInUseCount,
12519  createInfo.blockSize != 0, // explicitBlockSize
12520  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12521  m_Id(0),
12522  m_Name(VMA_NULL)
12523 {
12524 }
12525 
12526 VmaPool_T::~VmaPool_T()
12527 {
12528 }
12529 
12530 void VmaPool_T::SetName(const char* pName)
12531 {
12532  const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12533  VmaFreeString(allocs, m_Name);
12534 
12535  if(pName != VMA_NULL)
12536  {
12537  m_Name = VmaCreateStringCopy(allocs, pName);
12538  }
12539  else
12540  {
12541  m_Name = VMA_NULL;
12542  }
12543 }
12544 
12545 #if VMA_STATS_STRING_ENABLED
12546 
12547 #endif // #if VMA_STATS_STRING_ENABLED
12548 
12549 VmaBlockVector::VmaBlockVector(
12550  VmaAllocator hAllocator,
12551  VmaPool hParentPool,
12552  uint32_t memoryTypeIndex,
12553  VkDeviceSize preferredBlockSize,
12554  size_t minBlockCount,
12555  size_t maxBlockCount,
12556  VkDeviceSize bufferImageGranularity,
12557  uint32_t frameInUseCount,
12558  bool explicitBlockSize,
12559  uint32_t algorithm) :
12560  m_hAllocator(hAllocator),
12561  m_hParentPool(hParentPool),
12562  m_MemoryTypeIndex(memoryTypeIndex),
12563  m_PreferredBlockSize(preferredBlockSize),
12564  m_MinBlockCount(minBlockCount),
12565  m_MaxBlockCount(maxBlockCount),
12566  m_BufferImageGranularity(bufferImageGranularity),
12567  m_FrameInUseCount(frameInUseCount),
12568  m_ExplicitBlockSize(explicitBlockSize),
12569  m_Algorithm(algorithm),
12570  m_HasEmptyBlock(false),
12571  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12572  m_NextBlockId(0)
12573 {
12574 }
12575 
12576 VmaBlockVector::~VmaBlockVector()
12577 {
12578  for(size_t i = m_Blocks.size(); i--; )
12579  {
12580  m_Blocks[i]->Destroy(m_hAllocator);
12581  vma_delete(m_hAllocator, m_Blocks[i]);
12582  }
12583 }
12584 
12585 VkResult VmaBlockVector::CreateMinBlocks()
12586 {
12587  for(size_t i = 0; i < m_MinBlockCount; ++i)
12588  {
12589  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12590  if(res != VK_SUCCESS)
12591  {
12592  return res;
12593  }
12594  }
12595  return VK_SUCCESS;
12596 }
12597 
12598 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12599 {
12600  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12601 
12602  const size_t blockCount = m_Blocks.size();
12603 
12604  pStats->size = 0;
12605  pStats->unusedSize = 0;
12606  pStats->allocationCount = 0;
12607  pStats->unusedRangeCount = 0;
12608  pStats->unusedRangeSizeMax = 0;
12609  pStats->blockCount = blockCount;
12610 
12611  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12612  {
12613  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12614  VMA_ASSERT(pBlock);
12615  VMA_HEAVY_ASSERT(pBlock->Validate());
12616  pBlock->m_pMetadata->AddPoolStats(*pStats);
12617  }
12618 }
12619 
12620 bool VmaBlockVector::IsEmpty()
12621 {
12622  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12623  return m_Blocks.empty();
12624 }
12625 
12626 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12627 {
12628  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12629  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12630  (VMA_DEBUG_MARGIN > 0) &&
12631  (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12632  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12633 }
12634 
12635 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12636 
12637 VkResult VmaBlockVector::Allocate(
12638  uint32_t currentFrameIndex,
12639  VkDeviceSize size,
12640  VkDeviceSize alignment,
12641  const VmaAllocationCreateInfo& createInfo,
12642  VmaSuballocationType suballocType,
12643  size_t allocationCount,
12644  VmaAllocation* pAllocations)
12645 {
12646  size_t allocIndex;
12647  VkResult res = VK_SUCCESS;
12648 
12649  if(IsCorruptionDetectionEnabled())
12650  {
12651  size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12652  alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12653  }
12654 
12655  {
12656  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12657  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12658  {
12659  res = AllocatePage(
12660  currentFrameIndex,
12661  size,
12662  alignment,
12663  createInfo,
12664  suballocType,
12665  pAllocations + allocIndex);
12666  if(res != VK_SUCCESS)
12667  {
12668  break;
12669  }
12670  }
12671  }
12672 
12673  if(res != VK_SUCCESS)
12674  {
12675  // Free all already created allocations.
12676  while(allocIndex--)
12677  {
12678  Free(pAllocations[allocIndex]);
12679  }
12680  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12681  }
12682 
12683  return res;
12684 }
12685 
12686 VkResult VmaBlockVector::AllocatePage(
12687  uint32_t currentFrameIndex,
12688  VkDeviceSize size,
12689  VkDeviceSize alignment,
12690  const VmaAllocationCreateInfo& createInfo,
12691  VmaSuballocationType suballocType,
12692  VmaAllocation* pAllocation)
12693 {
12694  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12695  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12696  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12697  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12698 
12699  VkDeviceSize freeMemory;
12700  {
12701  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12702  VmaBudget heapBudget = {};
12703  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12704  freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12705  }
12706 
12707  const bool canFallbackToDedicated = !IsCustomPool();
12708  const bool canCreateNewBlock =
12709  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12710  (m_Blocks.size() < m_MaxBlockCount) &&
12711  (freeMemory >= size || !canFallbackToDedicated);
12712  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12713 
12714  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12715  // Which in turn is available only when maxBlockCount = 1.
12716  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12717  {
12718  canMakeOtherLost = false;
12719  }
12720 
12721  // Upper address can only be used with linear allocator and within single memory block.
12722  if(isUpperAddress &&
12723  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12724  {
12725  return VK_ERROR_FEATURE_NOT_PRESENT;
12726  }
12727 
12728  // Validate strategy.
12729  switch(strategy)
12730  {
12731  case 0:
12733  break;
12737  break;
12738  default:
12739  return VK_ERROR_FEATURE_NOT_PRESENT;
12740  }
12741 
12742  // Early reject: requested allocation size is larger that maximum block size for this block vector.
12743  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12744  {
12745  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12746  }
12747 
12748  /*
12749  Under certain condition, this whole section can be skipped for optimization, so
12750  we move on directly to trying to allocate with canMakeOtherLost. That's the case
12751  e.g. for custom pools with linear algorithm.
12752  */
12753  if(!canMakeOtherLost || canCreateNewBlock)
12754  {
12755  // 1. Search existing allocations. Try to allocate without making other allocations lost.
12756  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12758 
12759  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12760  {
12761  // Use only last block.
12762  if(!m_Blocks.empty())
12763  {
12764  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12765  VMA_ASSERT(pCurrBlock);
12766  VkResult res = AllocateFromBlock(
12767  pCurrBlock,
12768  currentFrameIndex,
12769  size,
12770  alignment,
12771  allocFlagsCopy,
12772  createInfo.pUserData,
12773  suballocType,
12774  strategy,
12775  pAllocation);
12776  if(res == VK_SUCCESS)
12777  {
12778  VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12779  return VK_SUCCESS;
12780  }
12781  }
12782  }
12783  else
12784  {
12786  {
12787  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12788  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12789  {
12790  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12791  VMA_ASSERT(pCurrBlock);
12792  VkResult res = AllocateFromBlock(
12793  pCurrBlock,
12794  currentFrameIndex,
12795  size,
12796  alignment,
12797  allocFlagsCopy,
12798  createInfo.pUserData,
12799  suballocType,
12800  strategy,
12801  pAllocation);
12802  if(res == VK_SUCCESS)
12803  {
12804  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12805  return VK_SUCCESS;
12806  }
12807  }
12808  }
12809  else // WORST_FIT, FIRST_FIT
12810  {
12811  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12812  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12813  {
12814  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12815  VMA_ASSERT(pCurrBlock);
12816  VkResult res = AllocateFromBlock(
12817  pCurrBlock,
12818  currentFrameIndex,
12819  size,
12820  alignment,
12821  allocFlagsCopy,
12822  createInfo.pUserData,
12823  suballocType,
12824  strategy,
12825  pAllocation);
12826  if(res == VK_SUCCESS)
12827  {
12828  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12829  return VK_SUCCESS;
12830  }
12831  }
12832  }
12833  }
12834 
12835  // 2. Try to create new block.
12836  if(canCreateNewBlock)
12837  {
12838  // Calculate optimal size for new block.
12839  VkDeviceSize newBlockSize = m_PreferredBlockSize;
12840  uint32_t newBlockSizeShift = 0;
12841  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12842 
12843  if(!m_ExplicitBlockSize)
12844  {
12845  // Allocate 1/8, 1/4, 1/2 as first blocks.
12846  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12847  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12848  {
12849  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12850  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12851  {
12852  newBlockSize = smallerNewBlockSize;
12853  ++newBlockSizeShift;
12854  }
12855  else
12856  {
12857  break;
12858  }
12859  }
12860  }
12861 
12862  size_t newBlockIndex = 0;
12863  VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12864  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12865  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12866  if(!m_ExplicitBlockSize)
12867  {
12868  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12869  {
12870  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12871  if(smallerNewBlockSize >= size)
12872  {
12873  newBlockSize = smallerNewBlockSize;
12874  ++newBlockSizeShift;
12875  res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12876  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12877  }
12878  else
12879  {
12880  break;
12881  }
12882  }
12883  }
12884 
12885  if(res == VK_SUCCESS)
12886  {
12887  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12888  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12889 
12890  res = AllocateFromBlock(
12891  pBlock,
12892  currentFrameIndex,
12893  size,
12894  alignment,
12895  allocFlagsCopy,
12896  createInfo.pUserData,
12897  suballocType,
12898  strategy,
12899  pAllocation);
12900  if(res == VK_SUCCESS)
12901  {
12902  VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12903  return VK_SUCCESS;
12904  }
12905  else
12906  {
12907  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12908  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12909  }
12910  }
12911  }
12912  }
12913 
12914  // 3. Try to allocate from existing blocks with making other allocations lost.
12915  if(canMakeOtherLost)
12916  {
12917  uint32_t tryIndex = 0;
12918  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12919  {
12920  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12921  VmaAllocationRequest bestRequest = {};
12922  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12923 
12924  // 1. Search existing allocations.
12926  {
12927  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12928  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12929  {
12930  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12931  VMA_ASSERT(pCurrBlock);
12932  VmaAllocationRequest currRequest = {};
12933  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12934  currentFrameIndex,
12935  m_FrameInUseCount,
12936  m_BufferImageGranularity,
12937  size,
12938  alignment,
12939  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12940  suballocType,
12941  canMakeOtherLost,
12942  strategy,
12943  &currRequest))
12944  {
12945  const VkDeviceSize currRequestCost = currRequest.CalcCost();
12946  if(pBestRequestBlock == VMA_NULL ||
12947  currRequestCost < bestRequestCost)
12948  {
12949  pBestRequestBlock = pCurrBlock;
12950  bestRequest = currRequest;
12951  bestRequestCost = currRequestCost;
12952 
12953  if(bestRequestCost == 0)
12954  {
12955  break;
12956  }
12957  }
12958  }
12959  }
12960  }
12961  else // WORST_FIT, FIRST_FIT
12962  {
12963  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12964  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12965  {
12966  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12967  VMA_ASSERT(pCurrBlock);
12968  VmaAllocationRequest currRequest = {};
12969  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12970  currentFrameIndex,
12971  m_FrameInUseCount,
12972  m_BufferImageGranularity,
12973  size,
12974  alignment,
12975  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12976  suballocType,
12977  canMakeOtherLost,
12978  strategy,
12979  &currRequest))
12980  {
12981  const VkDeviceSize currRequestCost = currRequest.CalcCost();
12982  if(pBestRequestBlock == VMA_NULL ||
12983  currRequestCost < bestRequestCost ||
12985  {
12986  pBestRequestBlock = pCurrBlock;
12987  bestRequest = currRequest;
12988  bestRequestCost = currRequestCost;
12989 
12990  if(bestRequestCost == 0 ||
12992  {
12993  break;
12994  }
12995  }
12996  }
12997  }
12998  }
12999 
13000  if(pBestRequestBlock != VMA_NULL)
13001  {
13002  if(mapped)
13003  {
13004  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13005  if(res != VK_SUCCESS)
13006  {
13007  return res;
13008  }
13009  }
13010 
13011  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13012  currentFrameIndex,
13013  m_FrameInUseCount,
13014  &bestRequest))
13015  {
13016  // Allocate from this pBlock.
13017  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13018  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13019  UpdateHasEmptyBlock();
13020  (*pAllocation)->InitBlockAllocation(
13021  pBestRequestBlock,
13022  bestRequest.offset,
13023  alignment,
13024  size,
13025  m_MemoryTypeIndex,
13026  suballocType,
13027  mapped,
13028  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13029  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13030  VMA_DEBUG_LOG(" Returned from existing block");
13031  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13032  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13033  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13034  {
13035  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13036  }
13037  if(IsCorruptionDetectionEnabled())
13038  {
13039  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13040  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13041  }
13042  return VK_SUCCESS;
13043  }
13044  // else: Some allocations must have been touched while we are here. Next try.
13045  }
13046  else
13047  {
13048  // Could not find place in any of the blocks - break outer loop.
13049  break;
13050  }
13051  }
13052  /* Maximum number of tries exceeded - a very unlike event when many other
13053  threads are simultaneously touching allocations making it impossible to make
13054  lost at the same time as we try to allocate. */
13055  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13056  {
13057  return VK_ERROR_TOO_MANY_OBJECTS;
13058  }
13059  }
13060 
13061  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13062 }
13063 
13064 void VmaBlockVector::Free(
13065  const VmaAllocation hAllocation)
13066 {
13067  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13068 
13069  bool budgetExceeded = false;
13070  {
13071  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13072  VmaBudget heapBudget = {};
13073  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13074  budgetExceeded = heapBudget.usage >= heapBudget.budget;
13075  }
13076 
13077  // Scope for lock.
13078  {
13079  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13080 
13081  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13082 
13083  if(IsCorruptionDetectionEnabled())
13084  {
13085  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13086  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13087  }
13088 
13089  if(hAllocation->IsPersistentMap())
13090  {
13091  pBlock->Unmap(m_hAllocator, 1);
13092  }
13093 
13094  pBlock->m_pMetadata->Free(hAllocation);
13095  VMA_HEAVY_ASSERT(pBlock->Validate());
13096 
13097  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13098 
13099  const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13100  // pBlock became empty after this deallocation.
13101  if(pBlock->m_pMetadata->IsEmpty())
13102  {
13103  // Already has empty block. We don't want to have two, so delete this one.
13104  if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13105  {
13106  pBlockToDelete = pBlock;
13107  Remove(pBlock);
13108  }
13109  // else: We now have an empty block - leave it.
13110  }
13111  // pBlock didn't become empty, but we have another empty block - find and free that one.
13112  // (This is optional, heuristics.)
13113  else if(m_HasEmptyBlock && canDeleteBlock)
13114  {
13115  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13116  if(pLastBlock->m_pMetadata->IsEmpty())
13117  {
13118  pBlockToDelete = pLastBlock;
13119  m_Blocks.pop_back();
13120  }
13121  }
13122 
13123  UpdateHasEmptyBlock();
13124  IncrementallySortBlocks();
13125  }
13126 
13127  // Destruction of a free block. Deferred until this point, outside of mutex
13128  // lock, for performance reason.
13129  if(pBlockToDelete != VMA_NULL)
13130  {
13131  VMA_DEBUG_LOG(" Deleted empty block");
13132  pBlockToDelete->Destroy(m_hAllocator);
13133  vma_delete(m_hAllocator, pBlockToDelete);
13134  }
13135 }
13136 
13137 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13138 {
13139  VkDeviceSize result = 0;
13140  for(size_t i = m_Blocks.size(); i--; )
13141  {
13142  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13143  if(result >= m_PreferredBlockSize)
13144  {
13145  break;
13146  }
13147  }
13148  return result;
13149 }
13150 
13151 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13152 {
13153  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13154  {
13155  if(m_Blocks[blockIndex] == pBlock)
13156  {
13157  VmaVectorRemove(m_Blocks, blockIndex);
13158  return;
13159  }
13160  }
13161  VMA_ASSERT(0);
13162 }
13163 
13164 void VmaBlockVector::IncrementallySortBlocks()
13165 {
13166  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13167  {
13168  // Bubble sort only until first swap.
13169  for(size_t i = 1; i < m_Blocks.size(); ++i)
13170  {
13171  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13172  {
13173  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13174  return;
13175  }
13176  }
13177  }
13178 }
13179 
13180 VkResult VmaBlockVector::AllocateFromBlock(
13181  VmaDeviceMemoryBlock* pBlock,
13182  uint32_t currentFrameIndex,
13183  VkDeviceSize size,
13184  VkDeviceSize alignment,
13185  VmaAllocationCreateFlags allocFlags,
13186  void* pUserData,
13187  VmaSuballocationType suballocType,
13188  uint32_t strategy,
13189  VmaAllocation* pAllocation)
13190 {
13191  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13192  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13193  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13194  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13195 
13196  VmaAllocationRequest currRequest = {};
13197  if(pBlock->m_pMetadata->CreateAllocationRequest(
13198  currentFrameIndex,
13199  m_FrameInUseCount,
13200  m_BufferImageGranularity,
13201  size,
13202  alignment,
13203  isUpperAddress,
13204  suballocType,
13205  false, // canMakeOtherLost
13206  strategy,
13207  &currRequest))
13208  {
13209  // Allocate from pCurrBlock.
13210  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13211 
13212  if(mapped)
13213  {
13214  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13215  if(res != VK_SUCCESS)
13216  {
13217  return res;
13218  }
13219  }
13220 
13221  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13222  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13223  UpdateHasEmptyBlock();
13224  (*pAllocation)->InitBlockAllocation(
13225  pBlock,
13226  currRequest.offset,
13227  alignment,
13228  size,
13229  m_MemoryTypeIndex,
13230  suballocType,
13231  mapped,
13232  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13233  VMA_HEAVY_ASSERT(pBlock->Validate());
13234  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13235  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13236  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13237  {
13238  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13239  }
13240  if(IsCorruptionDetectionEnabled())
13241  {
13242  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13243  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13244  }
13245  return VK_SUCCESS;
13246  }
13247  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13248 }
13249 
13250 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13251 {
13252  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13253  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13254  allocInfo.allocationSize = blockSize;
13255 
13256 #if VMA_BUFFER_DEVICE_ADDRESS
13257  // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13258  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13259  if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13260  {
13261  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13262  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13263  }
13264 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13265 
13266  VkDeviceMemory mem = VK_NULL_HANDLE;
13267  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13268  if(res < 0)
13269  {
13270  return res;
13271  }
13272 
13273  // New VkDeviceMemory successfully created.
13274 
13275  // Create new Allocation for it.
13276  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13277  pBlock->Init(
13278  m_hAllocator,
13279  m_hParentPool,
13280  m_MemoryTypeIndex,
13281  mem,
13282  allocInfo.allocationSize,
13283  m_NextBlockId++,
13284  m_Algorithm);
13285 
13286  m_Blocks.push_back(pBlock);
13287  if(pNewBlockIndex != VMA_NULL)
13288  {
13289  *pNewBlockIndex = m_Blocks.size() - 1;
13290  }
13291 
13292  return VK_SUCCESS;
13293 }
13294 
13295 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13296  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13297  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13298 {
13299  const size_t blockCount = m_Blocks.size();
13300  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13301 
13302  enum BLOCK_FLAG
13303  {
13304  BLOCK_FLAG_USED = 0x00000001,
13305  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13306  };
13307 
13308  struct BlockInfo
13309  {
13310  uint32_t flags;
13311  void* pMappedData;
13312  };
13313  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13314  blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13315  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13316 
13317  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13318  const size_t moveCount = moves.size();
13319  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13320  {
13321  const VmaDefragmentationMove& move = moves[moveIndex];
13322  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13323  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13324  }
13325 
13326  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13327 
13328  // Go over all blocks. Get mapped pointer or map if necessary.
13329  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13330  {
13331  BlockInfo& currBlockInfo = blockInfo[blockIndex];
13332  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13333  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13334  {
13335  currBlockInfo.pMappedData = pBlock->GetMappedData();
13336  // It is not originally mapped - map it.
13337  if(currBlockInfo.pMappedData == VMA_NULL)
13338  {
13339  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13340  if(pDefragCtx->res == VK_SUCCESS)
13341  {
13342  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13343  }
13344  }
13345  }
13346  }
13347 
13348  // Go over all moves. Do actual data transfer.
13349  if(pDefragCtx->res == VK_SUCCESS)
13350  {
13351  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13352  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13353 
13354  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13355  {
13356  const VmaDefragmentationMove& move = moves[moveIndex];
13357 
13358  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13359  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13360 
13361  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13362 
13363  // Invalidate source.
13364  if(isNonCoherent)
13365  {
13366  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13367  memRange.memory = pSrcBlock->GetDeviceMemory();
13368  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13369  memRange.size = VMA_MIN(
13370  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13371  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13372  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13373  }
13374 
13375  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13376  memmove(
13377  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13378  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13379  static_cast<size_t>(move.size));
13380 
13381  if(IsCorruptionDetectionEnabled())
13382  {
13383  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13384  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13385  }
13386 
13387  // Flush destination.
13388  if(isNonCoherent)
13389  {
13390  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13391  memRange.memory = pDstBlock->GetDeviceMemory();
13392  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13393  memRange.size = VMA_MIN(
13394  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13395  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13396  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13397  }
13398  }
13399  }
13400 
13401  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13402  // Regardless of pCtx->res == VK_SUCCESS.
13403  for(size_t blockIndex = blockCount; blockIndex--; )
13404  {
13405  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13406  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13407  {
13408  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13409  pBlock->Unmap(m_hAllocator, 1);
13410  }
13411  }
13412 }
13413 
13414 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13415  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13416  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13417  VkCommandBuffer commandBuffer)
13418 {
13419  const size_t blockCount = m_Blocks.size();
13420 
13421  pDefragCtx->blockContexts.resize(blockCount);
13422  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13423 
13424  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13425  const size_t moveCount = moves.size();
13426  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13427  {
13428  const VmaDefragmentationMove& move = moves[moveIndex];
13429 
13430  //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13431  {
13432  // Old school move still require us to map the whole block
13433  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13434  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13435  }
13436  }
13437 
13438  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13439 
13440  // Go over all blocks. Create and bind buffer for whole block if necessary.
13441  {
13442  VkBufferCreateInfo bufCreateInfo;
13443  VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13444 
13445  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13446  {
13447  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13448  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13449  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13450  {
13451  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13452  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13453  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13454  if(pDefragCtx->res == VK_SUCCESS)
13455  {
13456  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13457  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13458  }
13459  }
13460  }
13461  }
13462 
13463  // Go over all moves. Post data transfer commands to command buffer.
13464  if(pDefragCtx->res == VK_SUCCESS)
13465  {
13466  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13467  {
13468  const VmaDefragmentationMove& move = moves[moveIndex];
13469 
13470  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13471  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13472 
13473  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13474 
13475  VkBufferCopy region = {
13476  move.srcOffset,
13477  move.dstOffset,
13478  move.size };
13479  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13480  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13481  }
13482  }
13483 
13484  // Save buffers to defrag context for later destruction.
13485  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13486  {
13487  pDefragCtx->res = VK_NOT_READY;
13488  }
13489 }
13490 
13491 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13492 {
13493  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13494  {
13495  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13496  if(pBlock->m_pMetadata->IsEmpty())
13497  {
13498  if(m_Blocks.size() > m_MinBlockCount)
13499  {
13500  if(pDefragmentationStats != VMA_NULL)
13501  {
13502  ++pDefragmentationStats->deviceMemoryBlocksFreed;
13503  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13504  }
13505 
13506  VmaVectorRemove(m_Blocks, blockIndex);
13507  pBlock->Destroy(m_hAllocator);
13508  vma_delete(m_hAllocator, pBlock);
13509  }
13510  else
13511  {
13512  break;
13513  }
13514  }
13515  }
13516  UpdateHasEmptyBlock();
13517 }
13518 
13519 void VmaBlockVector::UpdateHasEmptyBlock()
13520 {
13521  m_HasEmptyBlock = false;
13522  for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13523  {
13524  VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13525  if(pBlock->m_pMetadata->IsEmpty())
13526  {
13527  m_HasEmptyBlock = true;
13528  break;
13529  }
13530  }
13531 }
13532 
13533 #if VMA_STATS_STRING_ENABLED
13534 
13535 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13536 {
13537  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13538 
13539  json.BeginObject();
13540 
13541  if(IsCustomPool())
13542  {
13543  const char* poolName = m_hParentPool->GetName();
13544  if(poolName != VMA_NULL && poolName[0] != '\0')
13545  {
13546  json.WriteString("Name");
13547  json.WriteString(poolName);
13548  }
13549 
13550  json.WriteString("MemoryTypeIndex");
13551  json.WriteNumber(m_MemoryTypeIndex);
13552 
13553  json.WriteString("BlockSize");
13554  json.WriteNumber(m_PreferredBlockSize);
13555 
13556  json.WriteString("BlockCount");
13557  json.BeginObject(true);
13558  if(m_MinBlockCount > 0)
13559  {
13560  json.WriteString("Min");
13561  json.WriteNumber((uint64_t)m_MinBlockCount);
13562  }
13563  if(m_MaxBlockCount < SIZE_MAX)
13564  {
13565  json.WriteString("Max");
13566  json.WriteNumber((uint64_t)m_MaxBlockCount);
13567  }
13568  json.WriteString("Cur");
13569  json.WriteNumber((uint64_t)m_Blocks.size());
13570  json.EndObject();
13571 
13572  if(m_FrameInUseCount > 0)
13573  {
13574  json.WriteString("FrameInUseCount");
13575  json.WriteNumber(m_FrameInUseCount);
13576  }
13577 
13578  if(m_Algorithm != 0)
13579  {
13580  json.WriteString("Algorithm");
13581  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13582  }
13583  }
13584  else
13585  {
13586  json.WriteString("PreferredBlockSize");
13587  json.WriteNumber(m_PreferredBlockSize);
13588  }
13589 
13590  json.WriteString("Blocks");
13591  json.BeginObject();
13592  for(size_t i = 0; i < m_Blocks.size(); ++i)
13593  {
13594  json.BeginString();
13595  json.ContinueString(m_Blocks[i]->GetId());
13596  json.EndString();
13597 
13598  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13599  }
13600  json.EndObject();
13601 
13602  json.EndObject();
13603 }
13604 
13605 #endif // #if VMA_STATS_STRING_ENABLED
13606 
13607 void VmaBlockVector::Defragment(
13608  class VmaBlockVectorDefragmentationContext* pCtx,
13610  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13611  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13612  VkCommandBuffer commandBuffer)
13613 {
13614  pCtx->res = VK_SUCCESS;
13615 
13616  const VkMemoryPropertyFlags memPropFlags =
13617  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13618  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13619 
13620  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13621  isHostVisible;
13622  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13623  !IsCorruptionDetectionEnabled() &&
13624  ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13625 
13626  // There are options to defragment this memory type.
13627  if(canDefragmentOnCpu || canDefragmentOnGpu)
13628  {
13629  bool defragmentOnGpu;
13630  // There is only one option to defragment this memory type.
13631  if(canDefragmentOnGpu != canDefragmentOnCpu)
13632  {
13633  defragmentOnGpu = canDefragmentOnGpu;
13634  }
13635  // Both options are available: Heuristics to choose the best one.
13636  else
13637  {
13638  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13639  m_hAllocator->IsIntegratedGpu();
13640  }
13641 
13642  bool overlappingMoveSupported = !defragmentOnGpu;
13643 
13644  if(m_hAllocator->m_UseMutex)
13645  {
13647  {
13648  if(!m_Mutex.TryLockWrite())
13649  {
13650  pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13651  return;
13652  }
13653  }
13654  else
13655  {
13656  m_Mutex.LockWrite();
13657  pCtx->mutexLocked = true;
13658  }
13659  }
13660 
13661  pCtx->Begin(overlappingMoveSupported, flags);
13662 
13663  // Defragment.
13664 
13665  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13666  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13667  pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13668 
13669  // Accumulate statistics.
13670  if(pStats != VMA_NULL)
13671  {
13672  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13673  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13674  pStats->bytesMoved += bytesMoved;
13675  pStats->allocationsMoved += allocationsMoved;
13676  VMA_ASSERT(bytesMoved <= maxBytesToMove);
13677  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13678  if(defragmentOnGpu)
13679  {
13680  maxGpuBytesToMove -= bytesMoved;
13681  maxGpuAllocationsToMove -= allocationsMoved;
13682  }
13683  else
13684  {
13685  maxCpuBytesToMove -= bytesMoved;
13686  maxCpuAllocationsToMove -= allocationsMoved;
13687  }
13688  }
13689 
13691  {
13692  if(m_hAllocator->m_UseMutex)
13693  m_Mutex.UnlockWrite();
13694 
13695  if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13696  pCtx->res = VK_NOT_READY;
13697 
13698  return;
13699  }
13700 
13701  if(pCtx->res >= VK_SUCCESS)
13702  {
13703  if(defragmentOnGpu)
13704  {
13705  ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13706  }
13707  else
13708  {
13709  ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13710  }
13711  }
13712  }
13713 }
13714 
13715 void VmaBlockVector::DefragmentationEnd(
13716  class VmaBlockVectorDefragmentationContext* pCtx,
13717  uint32_t flags,
13718  VmaDefragmentationStats* pStats)
13719 {
13720  if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13721  {
13722  VMA_ASSERT(pCtx->mutexLocked == false);
13723 
13724  // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13725  // lock protecting us. Since we mutate state here, we have to take the lock out now
13726  m_Mutex.LockWrite();
13727  pCtx->mutexLocked = true;
13728  }
13729 
13730  // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13731  if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13732  {
13733  // Destroy buffers.
13734  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13735  {
13736  VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13737  if(blockCtx.hBuffer)
13738  {
13739  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13740  }
13741  }
13742 
13743  if(pCtx->res >= VK_SUCCESS)
13744  {
13745  FreeEmptyBlocks(pStats);
13746  }
13747  }
13748 
13749  if(pCtx->mutexLocked)
13750  {
13751  VMA_ASSERT(m_hAllocator->m_UseMutex);
13752  m_Mutex.UnlockWrite();
13753  }
13754 }
13755 
13756 uint32_t VmaBlockVector::ProcessDefragmentations(
13757  class VmaBlockVectorDefragmentationContext *pCtx,
13758  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13759 {
13760  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13761 
13762  const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13763 
13764  for(uint32_t i = 0; i < moveCount; ++ i)
13765  {
13766  VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13767 
13768  pMove->allocation = move.hAllocation;
13769  pMove->memory = move.pDstBlock->GetDeviceMemory();
13770  pMove->offset = move.dstOffset;
13771 
13772  ++ pMove;
13773  }
13774 
13775  pCtx->defragmentationMovesProcessed += moveCount;
13776 
13777  return moveCount;
13778 }
13779 
13780 void VmaBlockVector::CommitDefragmentations(
13781  class VmaBlockVectorDefragmentationContext *pCtx,
13782  VmaDefragmentationStats* pStats)
13783 {
13784  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13785 
13786  for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13787  {
13788  const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13789 
13790  move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13791  move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13792  }
13793 
13794  pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13795  FreeEmptyBlocks(pStats);
13796 }
13797 
13798 size_t VmaBlockVector::CalcAllocationCount() const
13799 {
13800  size_t result = 0;
13801  for(size_t i = 0; i < m_Blocks.size(); ++i)
13802  {
13803  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13804  }
13805  return result;
13806 }
13807 
13808 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13809 {
13810  if(m_BufferImageGranularity == 1)
13811  {
13812  return false;
13813  }
13814  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13815  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13816  {
13817  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13818  VMA_ASSERT(m_Algorithm == 0);
13819  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13820  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13821  {
13822  return true;
13823  }
13824  }
13825  return false;
13826 }
13827 
13828 void VmaBlockVector::MakePoolAllocationsLost(
13829  uint32_t currentFrameIndex,
13830  size_t* pLostAllocationCount)
13831 {
13832  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13833  size_t lostAllocationCount = 0;
13834  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13835  {
13836  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13837  VMA_ASSERT(pBlock);
13838  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13839  }
13840  if(pLostAllocationCount != VMA_NULL)
13841  {
13842  *pLostAllocationCount = lostAllocationCount;
13843  }
13844 }
13845 
13846 VkResult VmaBlockVector::CheckCorruption()
13847 {
13848  if(!IsCorruptionDetectionEnabled())
13849  {
13850  return VK_ERROR_FEATURE_NOT_PRESENT;
13851  }
13852 
13853  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13854  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13855  {
13856  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13857  VMA_ASSERT(pBlock);
13858  VkResult res = pBlock->CheckCorruption(m_hAllocator);
13859  if(res != VK_SUCCESS)
13860  {
13861  return res;
13862  }
13863  }
13864  return VK_SUCCESS;
13865 }
13866 
13867 void VmaBlockVector::AddStats(VmaStats* pStats)
13868 {
13869  const uint32_t memTypeIndex = m_MemoryTypeIndex;
13870  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13871 
13872  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13873 
13874  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13875  {
13876  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13877  VMA_ASSERT(pBlock);
13878  VMA_HEAVY_ASSERT(pBlock->Validate());
13879  VmaStatInfo allocationStatInfo;
13880  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13881  VmaAddStatInfo(pStats->total, allocationStatInfo);
13882  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13883  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13884  }
13885 }
13886 
13888 // VmaDefragmentationAlgorithm_Generic members definition
13889 
13890 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13891  VmaAllocator hAllocator,
13892  VmaBlockVector* pBlockVector,
13893  uint32_t currentFrameIndex,
13894  bool overlappingMoveSupported) :
13895  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13896  m_AllocationCount(0),
13897  m_AllAllocations(false),
13898  m_BytesMoved(0),
13899  m_AllocationsMoved(0),
13900  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13901 {
13902  // Create block info for each block.
13903  const size_t blockCount = m_pBlockVector->m_Blocks.size();
13904  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13905  {
13906  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13907  pBlockInfo->m_OriginalBlockIndex = blockIndex;
13908  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13909  m_Blocks.push_back(pBlockInfo);
13910  }
13911 
13912  // Sort them by m_pBlock pointer value.
13913  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13914 }
13915 
13916 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13917 {
13918  for(size_t i = m_Blocks.size(); i--; )
13919  {
13920  vma_delete(m_hAllocator, m_Blocks[i]);
13921  }
13922 }
13923 
13924 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13925 {
13926  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13927  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13928  {
13929  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13930  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13931  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13932  {
13933  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13934  (*it)->m_Allocations.push_back(allocInfo);
13935  }
13936  else
13937  {
13938  VMA_ASSERT(0);
13939  }
13940 
13941  ++m_AllocationCount;
13942  }
13943 }
13944 
13945 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
13946  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13947  VkDeviceSize maxBytesToMove,
13948  uint32_t maxAllocationsToMove,
13949  bool freeOldAllocations)
13950 {
13951  if(m_Blocks.empty())
13952  {
13953  return VK_SUCCESS;
13954  }
13955 
13956  // This is a choice based on research.
13957  // Option 1:
13958  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
13959  // Option 2:
13960  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
13961  // Option 3:
13962  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
13963 
13964  size_t srcBlockMinIndex = 0;
13965  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
13966  /*
13967  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
13968  {
13969  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
13970  if(blocksWithNonMovableCount > 0)
13971  {
13972  srcBlockMinIndex = blocksWithNonMovableCount - 1;
13973  }
13974  }
13975  */
13976 
13977  size_t srcBlockIndex = m_Blocks.size() - 1;
13978  size_t srcAllocIndex = SIZE_MAX;
13979  for(;;)
13980  {
13981  // 1. Find next allocation to move.
13982  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
13983  // 1.2. Then start from last to first m_Allocations.
13984  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
13985  {
13986  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
13987  {
13988  // Finished: no more allocations to process.
13989  if(srcBlockIndex == srcBlockMinIndex)
13990  {
13991  return VK_SUCCESS;
13992  }
13993  else
13994  {
13995  --srcBlockIndex;
13996  srcAllocIndex = SIZE_MAX;
13997  }
13998  }
13999  else
14000  {
14001  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14002  }
14003  }
14004 
14005  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14006  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14007 
14008  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14009  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14010  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14011  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14012 
14013  // 2. Try to find new place for this allocation in preceding or current block.
14014  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14015  {
14016  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14017  VmaAllocationRequest dstAllocRequest;
14018  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14019  m_CurrentFrameIndex,
14020  m_pBlockVector->GetFrameInUseCount(),
14021  m_pBlockVector->GetBufferImageGranularity(),
14022  size,
14023  alignment,
14024  false, // upperAddress
14025  suballocType,
14026  false, // canMakeOtherLost
14027  strategy,
14028  &dstAllocRequest) &&
14029  MoveMakesSense(
14030  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14031  {
14032  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14033 
14034  // Reached limit on number of allocations or bytes to move.
14035  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14036  (m_BytesMoved + size > maxBytesToMove))
14037  {
14038  return VK_SUCCESS;
14039  }
14040 
14041  VmaDefragmentationMove move = {};
14042  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14043  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14044  move.srcOffset = srcOffset;
14045  move.dstOffset = dstAllocRequest.offset;
14046  move.size = size;
14047  move.hAllocation = allocInfo.m_hAllocation;
14048  move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14049  move.pDstBlock = pDstBlockInfo->m_pBlock;
14050 
14051  moves.push_back(move);
14052 
14053  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14054  dstAllocRequest,
14055  suballocType,
14056  size,
14057  allocInfo.m_hAllocation);
14058 
14059  if(freeOldAllocations)
14060  {
14061  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14062  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14063  }
14064 
14065  if(allocInfo.m_pChanged != VMA_NULL)
14066  {
14067  *allocInfo.m_pChanged = VK_TRUE;
14068  }
14069 
14070  ++m_AllocationsMoved;
14071  m_BytesMoved += size;
14072 
14073  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14074 
14075  break;
14076  }
14077  }
14078 
14079  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14080 
14081  if(srcAllocIndex > 0)
14082  {
14083  --srcAllocIndex;
14084  }
14085  else
14086  {
14087  if(srcBlockIndex > 0)
14088  {
14089  --srcBlockIndex;
14090  srcAllocIndex = SIZE_MAX;
14091  }
14092  else
14093  {
14094  return VK_SUCCESS;
14095  }
14096  }
14097  }
14098 }
14099 
14100 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14101 {
14102  size_t result = 0;
14103  for(size_t i = 0; i < m_Blocks.size(); ++i)
14104  {
14105  if(m_Blocks[i]->m_HasNonMovableAllocations)
14106  {
14107  ++result;
14108  }
14109  }
14110  return result;
14111 }
14112 
14113 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14114  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14115  VkDeviceSize maxBytesToMove,
14116  uint32_t maxAllocationsToMove,
14118 {
14119  if(!m_AllAllocations && m_AllocationCount == 0)
14120  {
14121  return VK_SUCCESS;
14122  }
14123 
14124  const size_t blockCount = m_Blocks.size();
14125  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14126  {
14127  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14128 
14129  if(m_AllAllocations)
14130  {
14131  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14132  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14133  it != pMetadata->m_Suballocations.end();
14134  ++it)
14135  {
14136  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14137  {
14138  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14139  pBlockInfo->m_Allocations.push_back(allocInfo);
14140  }
14141  }
14142  }
14143 
14144  pBlockInfo->CalcHasNonMovableAllocations();
14145 
14146  // This is a choice based on research.
14147  // Option 1:
14148  pBlockInfo->SortAllocationsByOffsetDescending();
14149  // Option 2:
14150  //pBlockInfo->SortAllocationsBySizeDescending();
14151  }
14152 
14153  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14154  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14155 
14156  // This is a choice based on research.
14157  const uint32_t roundCount = 2;
14158 
14159  // Execute defragmentation rounds (the main part).
14160  VkResult result = VK_SUCCESS;
14161  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14162  {
14163  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14164  }
14165 
14166  return result;
14167 }
14168 
14169 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14170  size_t dstBlockIndex, VkDeviceSize dstOffset,
14171  size_t srcBlockIndex, VkDeviceSize srcOffset)
14172 {
14173  if(dstBlockIndex < srcBlockIndex)
14174  {
14175  return true;
14176  }
14177  if(dstBlockIndex > srcBlockIndex)
14178  {
14179  return false;
14180  }
14181  if(dstOffset < srcOffset)
14182  {
14183  return true;
14184  }
14185  return false;
14186 }
14187 
14189 // VmaDefragmentationAlgorithm_Fast
14190 
14191 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14192  VmaAllocator hAllocator,
14193  VmaBlockVector* pBlockVector,
14194  uint32_t currentFrameIndex,
14195  bool overlappingMoveSupported) :
14196  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14197  m_OverlappingMoveSupported(overlappingMoveSupported),
14198  m_AllocationCount(0),
14199  m_AllAllocations(false),
14200  m_BytesMoved(0),
14201  m_AllocationsMoved(0),
14202  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14203 {
14204  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14205 
14206 }
14207 
14208 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14209 {
14210 }
14211 
14212 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14213  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14214  VkDeviceSize maxBytesToMove,
14215  uint32_t maxAllocationsToMove,
14217 {
14218  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14219 
14220  const size_t blockCount = m_pBlockVector->GetBlockCount();
14221  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14222  {
14223  return VK_SUCCESS;
14224  }
14225 
14226  PreprocessMetadata();
14227 
14228  // Sort blocks in order from most destination.
14229 
14230  m_BlockInfos.resize(blockCount);
14231  for(size_t i = 0; i < blockCount; ++i)
14232  {
14233  m_BlockInfos[i].origBlockIndex = i;
14234  }
14235 
14236  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14237  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14238  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14239  });
14240 
14241  // THE MAIN ALGORITHM
14242 
14243  FreeSpaceDatabase freeSpaceDb;
14244 
14245  size_t dstBlockInfoIndex = 0;
14246  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14247  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14248  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14249  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14250  VkDeviceSize dstOffset = 0;
14251 
14252  bool end = false;
14253  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14254  {
14255  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14256  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14257  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14258  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14259  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14260  {
14261  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14262  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14263  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14264  if(m_AllocationsMoved == maxAllocationsToMove ||
14265  m_BytesMoved + srcAllocSize > maxBytesToMove)
14266  {
14267  end = true;
14268  break;
14269  }
14270  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14271 
14272  VmaDefragmentationMove move = {};
14273  // Try to place it in one of free spaces from the database.
14274  size_t freeSpaceInfoIndex;
14275  VkDeviceSize dstAllocOffset;
14276  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14277  freeSpaceInfoIndex, dstAllocOffset))
14278  {
14279  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14280  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14281  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14282 
14283  // Same block
14284  if(freeSpaceInfoIndex == srcBlockInfoIndex)
14285  {
14286  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14287 
14288  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14289 
14290  VmaSuballocation suballoc = *srcSuballocIt;
14291  suballoc.offset = dstAllocOffset;
14292  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14293  m_BytesMoved += srcAllocSize;
14294  ++m_AllocationsMoved;
14295 
14296  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14297  ++nextSuballocIt;
14298  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14299  srcSuballocIt = nextSuballocIt;
14300 
14301  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14302 
14303  move.srcBlockIndex = srcOrigBlockIndex;
14304  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14305  move.srcOffset = srcAllocOffset;
14306  move.dstOffset = dstAllocOffset;
14307  move.size = srcAllocSize;
14308 
14309  moves.push_back(move);
14310  }
14311  // Different block
14312  else
14313  {
14314  // MOVE OPTION 2: Move the allocation to a different block.
14315 
14316  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14317 
14318  VmaSuballocation suballoc = *srcSuballocIt;
14319  suballoc.offset = dstAllocOffset;
14320  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14321  m_BytesMoved += srcAllocSize;
14322  ++m_AllocationsMoved;
14323 
14324  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14325  ++nextSuballocIt;
14326  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14327  srcSuballocIt = nextSuballocIt;
14328 
14329  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14330 
14331  move.srcBlockIndex = srcOrigBlockIndex;
14332  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14333  move.srcOffset = srcAllocOffset;
14334  move.dstOffset = dstAllocOffset;
14335  move.size = srcAllocSize;
14336 
14337  moves.push_back(move);
14338  }
14339  }
14340  else
14341  {
14342  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14343 
14344  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14345  while(dstBlockInfoIndex < srcBlockInfoIndex &&
14346  dstAllocOffset + srcAllocSize > dstBlockSize)
14347  {
14348  // But before that, register remaining free space at the end of dst block.
14349  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14350 
14351  ++dstBlockInfoIndex;
14352  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14353  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14354  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14355  dstBlockSize = pDstMetadata->GetSize();
14356  dstOffset = 0;
14357  dstAllocOffset = 0;
14358  }
14359 
14360  // Same block
14361  if(dstBlockInfoIndex == srcBlockInfoIndex)
14362  {
14363  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14364 
14365  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14366 
14367  bool skipOver = overlap;
14368  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14369  {
14370  // If destination and source place overlap, skip if it would move it
14371  // by only < 1/64 of its size.
14372  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14373  }
14374 
14375  if(skipOver)
14376  {
14377  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14378 
14379  dstOffset = srcAllocOffset + srcAllocSize;
14380  ++srcSuballocIt;
14381  }
14382  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14383  else
14384  {
14385  srcSuballocIt->offset = dstAllocOffset;
14386  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14387  dstOffset = dstAllocOffset + srcAllocSize;
14388  m_BytesMoved += srcAllocSize;
14389  ++m_AllocationsMoved;
14390  ++srcSuballocIt;
14391 
14392  move.srcBlockIndex = srcOrigBlockIndex;
14393  move.dstBlockIndex = dstOrigBlockIndex;
14394  move.srcOffset = srcAllocOffset;
14395  move.dstOffset = dstAllocOffset;
14396  move.size = srcAllocSize;
14397 
14398  moves.push_back(move);
14399  }
14400  }
14401  // Different block
14402  else
14403  {
14404  // MOVE OPTION 2: Move the allocation to a different block.
14405 
14406  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14407  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14408 
14409  VmaSuballocation suballoc = *srcSuballocIt;
14410  suballoc.offset = dstAllocOffset;
14411  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14412  dstOffset = dstAllocOffset + srcAllocSize;
14413  m_BytesMoved += srcAllocSize;
14414  ++m_AllocationsMoved;
14415 
14416  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14417  ++nextSuballocIt;
14418  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14419  srcSuballocIt = nextSuballocIt;
14420 
14421  pDstMetadata->m_Suballocations.push_back(suballoc);
14422 
14423  move.srcBlockIndex = srcOrigBlockIndex;
14424  move.dstBlockIndex = dstOrigBlockIndex;
14425  move.srcOffset = srcAllocOffset;
14426  move.dstOffset = dstAllocOffset;
14427  move.size = srcAllocSize;
14428 
14429  moves.push_back(move);
14430  }
14431  }
14432  }
14433  }
14434 
14435  m_BlockInfos.clear();
14436 
14437  PostprocessMetadata();
14438 
14439  return VK_SUCCESS;
14440 }
14441 
14442 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14443 {
14444  const size_t blockCount = m_pBlockVector->GetBlockCount();
14445  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14446  {
14447  VmaBlockMetadata_Generic* const pMetadata =
14448  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14449  pMetadata->m_FreeCount = 0;
14450  pMetadata->m_SumFreeSize = pMetadata->GetSize();
14451  pMetadata->m_FreeSuballocationsBySize.clear();
14452  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14453  it != pMetadata->m_Suballocations.end(); )
14454  {
14455  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14456  {
14457  VmaSuballocationList::iterator nextIt = it;
14458  ++nextIt;
14459  pMetadata->m_Suballocations.erase(it);
14460  it = nextIt;
14461  }
14462  else
14463  {
14464  ++it;
14465  }
14466  }
14467  }
14468 }
14469 
14470 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14471 {
14472  const size_t blockCount = m_pBlockVector->GetBlockCount();
14473  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14474  {
14475  VmaBlockMetadata_Generic* const pMetadata =
14476  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14477  const VkDeviceSize blockSize = pMetadata->GetSize();
14478 
14479  // No allocations in this block - entire area is free.
14480  if(pMetadata->m_Suballocations.empty())
14481  {
14482  pMetadata->m_FreeCount = 1;
14483  //pMetadata->m_SumFreeSize is already set to blockSize.
14484  VmaSuballocation suballoc = {
14485  0, // offset
14486  blockSize, // size
14487  VMA_NULL, // hAllocation
14488  VMA_SUBALLOCATION_TYPE_FREE };
14489  pMetadata->m_Suballocations.push_back(suballoc);
14490  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14491  }
14492  // There are some allocations in this block.
14493  else
14494  {
14495  VkDeviceSize offset = 0;
14496  VmaSuballocationList::iterator it;
14497  for(it = pMetadata->m_Suballocations.begin();
14498  it != pMetadata->m_Suballocations.end();
14499  ++it)
14500  {
14501  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14502  VMA_ASSERT(it->offset >= offset);
14503 
14504  // Need to insert preceding free space.
14505  if(it->offset > offset)
14506  {
14507  ++pMetadata->m_FreeCount;
14508  const VkDeviceSize freeSize = it->offset - offset;
14509  VmaSuballocation suballoc = {
14510  offset, // offset
14511  freeSize, // size
14512  VMA_NULL, // hAllocation
14513  VMA_SUBALLOCATION_TYPE_FREE };
14514  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14515  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14516  {
14517  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14518  }
14519  }
14520 
14521  pMetadata->m_SumFreeSize -= it->size;
14522  offset = it->offset + it->size;
14523  }
14524 
14525  // Need to insert trailing free space.
14526  if(offset < blockSize)
14527  {
14528  ++pMetadata->m_FreeCount;
14529  const VkDeviceSize freeSize = blockSize - offset;
14530  VmaSuballocation suballoc = {
14531  offset, // offset
14532  freeSize, // size
14533  VMA_NULL, // hAllocation
14534  VMA_SUBALLOCATION_TYPE_FREE };
14535  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14536  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14537  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14538  {
14539  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14540  }
14541  }
14542 
14543  VMA_SORT(
14544  pMetadata->m_FreeSuballocationsBySize.begin(),
14545  pMetadata->m_FreeSuballocationsBySize.end(),
14546  VmaSuballocationItemSizeLess());
14547  }
14548 
14549  VMA_HEAVY_ASSERT(pMetadata->Validate());
14550  }
14551 }
14552 
14553 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14554 {
14555  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14556  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14557  while(it != pMetadata->m_Suballocations.end())
14558  {
14559  if(it->offset < suballoc.offset)
14560  {
14561  ++it;
14562  }
14563  }
14564  pMetadata->m_Suballocations.insert(it, suballoc);
14565 }
14566 
14568 // VmaBlockVectorDefragmentationContext
14569 
14570 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14571  VmaAllocator hAllocator,
14572  VmaPool hCustomPool,
14573  VmaBlockVector* pBlockVector,
14574  uint32_t currFrameIndex) :
14575  res(VK_SUCCESS),
14576  mutexLocked(false),
14577  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14578  defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14579  defragmentationMovesProcessed(0),
14580  defragmentationMovesCommitted(0),
14581  hasDefragmentationPlan(0),
14582  m_hAllocator(hAllocator),
14583  m_hCustomPool(hCustomPool),
14584  m_pBlockVector(pBlockVector),
14585  m_CurrFrameIndex(currFrameIndex),
14586  m_pAlgorithm(VMA_NULL),
14587  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14588  m_AllAllocations(false)
14589 {
14590 }
14591 
14592 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14593 {
14594  vma_delete(m_hAllocator, m_pAlgorithm);
14595 }
14596 
14597 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14598 {
14599  AllocInfo info = { hAlloc, pChanged };
14600  m_Allocations.push_back(info);
14601 }
14602 
14603 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14604 {
14605  const bool allAllocations = m_AllAllocations ||
14606  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14607 
14608  /********************************
14609  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14610  ********************************/
14611 
14612  /*
14613  Fast algorithm is supported only when certain criteria are met:
14614  - VMA_DEBUG_MARGIN is 0.
14615  - All allocations in this block vector are moveable.
14616  - There is no possibility of image/buffer granularity conflict.
14617  - The defragmentation is not incremental
14618  */
14619  if(VMA_DEBUG_MARGIN == 0 &&
14620  allAllocations &&
14621  !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14623  {
14624  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14625  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14626  }
14627  else
14628  {
14629  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14630  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14631  }
14632 
14633  if(allAllocations)
14634  {
14635  m_pAlgorithm->AddAll();
14636  }
14637  else
14638  {
14639  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14640  {
14641  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14642  }
14643  }
14644 }
14645 
14647 // VmaDefragmentationContext
14648 
14649 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14650  VmaAllocator hAllocator,
14651  uint32_t currFrameIndex,
14652  uint32_t flags,
14653  VmaDefragmentationStats* pStats) :
14654  m_hAllocator(hAllocator),
14655  m_CurrFrameIndex(currFrameIndex),
14656  m_Flags(flags),
14657  m_pStats(pStats),
14658  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14659 {
14660  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14661 }
14662 
14663 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14664 {
14665  for(size_t i = m_CustomPoolContexts.size(); i--; )
14666  {
14667  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14668  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14669  vma_delete(m_hAllocator, pBlockVectorCtx);
14670  }
14671  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14672  {
14673  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14674  if(pBlockVectorCtx)
14675  {
14676  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14677  vma_delete(m_hAllocator, pBlockVectorCtx);
14678  }
14679  }
14680 }
14681 
14682 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14683 {
14684  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14685  {
14686  VmaPool pool = pPools[poolIndex];
14687  VMA_ASSERT(pool);
14688  // Pools with algorithm other than default are not defragmented.
14689  if(pool->m_BlockVector.GetAlgorithm() == 0)
14690  {
14691  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14692 
14693  for(size_t i = m_CustomPoolContexts.size(); i--; )
14694  {
14695  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14696  {
14697  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14698  break;
14699  }
14700  }
14701 
14702  if(!pBlockVectorDefragCtx)
14703  {
14704  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14705  m_hAllocator,
14706  pool,
14707  &pool->m_BlockVector,
14708  m_CurrFrameIndex);
14709  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14710  }
14711 
14712  pBlockVectorDefragCtx->AddAll();
14713  }
14714  }
14715 }
14716 
14717 void VmaDefragmentationContext_T::AddAllocations(
14718  uint32_t allocationCount,
14719  const VmaAllocation* pAllocations,
14720  VkBool32* pAllocationsChanged)
14721 {
14722  // Dispatch pAllocations among defragmentators. Create them when necessary.
14723  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14724  {
14725  const VmaAllocation hAlloc = pAllocations[allocIndex];
14726  VMA_ASSERT(hAlloc);
14727  // DedicatedAlloc cannot be defragmented.
14728  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14729  // Lost allocation cannot be defragmented.
14730  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14731  {
14732  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14733 
14734  const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14735  // This allocation belongs to custom pool.
14736  if(hAllocPool != VK_NULL_HANDLE)
14737  {
14738  // Pools with algorithm other than default are not defragmented.
14739  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14740  {
14741  for(size_t i = m_CustomPoolContexts.size(); i--; )
14742  {
14743  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14744  {
14745  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14746  break;
14747  }
14748  }
14749  if(!pBlockVectorDefragCtx)
14750  {
14751  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14752  m_hAllocator,
14753  hAllocPool,
14754  &hAllocPool->m_BlockVector,
14755  m_CurrFrameIndex);
14756  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14757  }
14758  }
14759  }
14760  // This allocation belongs to default pool.
14761  else
14762  {
14763  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14764  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14765  if(!pBlockVectorDefragCtx)
14766  {
14767  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14768  m_hAllocator,
14769  VMA_NULL, // hCustomPool
14770  m_hAllocator->m_pBlockVectors[memTypeIndex],
14771  m_CurrFrameIndex);
14772  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14773  }
14774  }
14775 
14776  if(pBlockVectorDefragCtx)
14777  {
14778  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14779  &pAllocationsChanged[allocIndex] : VMA_NULL;
14780  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14781  }
14782  }
14783  }
14784 }
14785 
14786 VkResult VmaDefragmentationContext_T::Defragment(
14787  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14788  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14789  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14790 {
14791  if(pStats)
14792  {
14793  memset(pStats, 0, sizeof(VmaDefragmentationStats));
14794  }
14795 
14797  {
14798  // For incremental defragmetnations, we just earmark how much we can move
14799  // The real meat is in the defragmentation steps
14800  m_MaxCpuBytesToMove = maxCpuBytesToMove;
14801  m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14802 
14803  m_MaxGpuBytesToMove = maxGpuBytesToMove;
14804  m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14805 
14806  if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14807  m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14808  return VK_SUCCESS;
14809 
14810  return VK_NOT_READY;
14811  }
14812 
14813  if(commandBuffer == VK_NULL_HANDLE)
14814  {
14815  maxGpuBytesToMove = 0;
14816  maxGpuAllocationsToMove = 0;
14817  }
14818 
14819  VkResult res = VK_SUCCESS;
14820 
14821  // Process default pools.
14822  for(uint32_t memTypeIndex = 0;
14823  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14824  ++memTypeIndex)
14825  {
14826  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14827  if(pBlockVectorCtx)
14828  {
14829  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14830  pBlockVectorCtx->GetBlockVector()->Defragment(
14831  pBlockVectorCtx,
14832  pStats, flags,
14833  maxCpuBytesToMove, maxCpuAllocationsToMove,
14834  maxGpuBytesToMove, maxGpuAllocationsToMove,
14835  commandBuffer);
14836  if(pBlockVectorCtx->res != VK_SUCCESS)
14837  {
14838  res = pBlockVectorCtx->res;
14839  }
14840  }
14841  }
14842 
14843  // Process custom pools.
14844  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14845  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14846  ++customCtxIndex)
14847  {
14848  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14849  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14850  pBlockVectorCtx->GetBlockVector()->Defragment(
14851  pBlockVectorCtx,
14852  pStats, flags,
14853  maxCpuBytesToMove, maxCpuAllocationsToMove,
14854  maxGpuBytesToMove, maxGpuAllocationsToMove,
14855  commandBuffer);
14856  if(pBlockVectorCtx->res != VK_SUCCESS)
14857  {
14858  res = pBlockVectorCtx->res;
14859  }
14860  }
14861 
14862  return res;
14863 }
14864 
14865 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14866 {
14867  VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14868  uint32_t movesLeft = pInfo->moveCount;
14869 
14870  // Process default pools.
14871  for(uint32_t memTypeIndex = 0;
14872  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14873  ++memTypeIndex)
14874  {
14875  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14876  if(pBlockVectorCtx)
14877  {
14878  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14879 
14880  if(!pBlockVectorCtx->hasDefragmentationPlan)
14881  {
14882  pBlockVectorCtx->GetBlockVector()->Defragment(
14883  pBlockVectorCtx,
14884  m_pStats, m_Flags,
14885  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14886  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14887  VK_NULL_HANDLE);
14888 
14889  if(pBlockVectorCtx->res < VK_SUCCESS)
14890  continue;
14891 
14892  pBlockVectorCtx->hasDefragmentationPlan = true;
14893  }
14894 
14895  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14896  pBlockVectorCtx,
14897  pCurrentMove, movesLeft);
14898 
14899  movesLeft -= processed;
14900  pCurrentMove += processed;
14901  }
14902  }
14903 
14904  // Process custom pools.
14905  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14906  customCtxIndex < customCtxCount;
14907  ++customCtxIndex)
14908  {
14909  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14910  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14911 
14912  if(!pBlockVectorCtx->hasDefragmentationPlan)
14913  {
14914  pBlockVectorCtx->GetBlockVector()->Defragment(
14915  pBlockVectorCtx,
14916  m_pStats, m_Flags,
14917  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14918  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14919  VK_NULL_HANDLE);
14920 
14921  if(pBlockVectorCtx->res < VK_SUCCESS)
14922  continue;
14923 
14924  pBlockVectorCtx->hasDefragmentationPlan = true;
14925  }
14926 
14927  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14928  pBlockVectorCtx,
14929  pCurrentMove, movesLeft);
14930 
14931  movesLeft -= processed;
14932  pCurrentMove += processed;
14933  }
14934 
14935  pInfo->moveCount = pInfo->moveCount - movesLeft;
14936 
14937  return VK_SUCCESS;
14938 }
14939 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
14940 {
14941  VkResult res = VK_SUCCESS;
14942 
14943  // Process default pools.
14944  for(uint32_t memTypeIndex = 0;
14945  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14946  ++memTypeIndex)
14947  {
14948  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14949  if(pBlockVectorCtx)
14950  {
14951  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14952 
14953  if(!pBlockVectorCtx->hasDefragmentationPlan)
14954  {
14955  res = VK_NOT_READY;
14956  continue;
14957  }
14958 
14959  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14960  pBlockVectorCtx, m_pStats);
14961 
14962  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14963  res = VK_NOT_READY;
14964  }
14965  }
14966 
14967  // Process custom pools.
14968  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14969  customCtxIndex < customCtxCount;
14970  ++customCtxIndex)
14971  {
14972  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14973  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14974 
14975  if(!pBlockVectorCtx->hasDefragmentationPlan)
14976  {
14977  res = VK_NOT_READY;
14978  continue;
14979  }
14980 
14981  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14982  pBlockVectorCtx, m_pStats);
14983 
14984  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14985  res = VK_NOT_READY;
14986  }
14987 
14988  return res;
14989 }
14990 
14992 // VmaRecorder
14993 
14994 #if VMA_RECORDING_ENABLED
14995 
14996 VmaRecorder::VmaRecorder() :
14997  m_UseMutex(true),
14998  m_Flags(0),
14999  m_File(VMA_NULL),
15000  m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15001 {
15002 }
15003 
15004 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15005 {
15006  m_UseMutex = useMutex;
15007  m_Flags = settings.flags;
15008 
15009 #if defined(_WIN32)
15010  // Open file for writing.
15011  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15012 
15013  if(err != 0)
15014  {
15015  return VK_ERROR_INITIALIZATION_FAILED;
15016  }
15017 #else
15018  // Open file for writing.
15019  m_File = fopen(settings.pFilePath, "wb");
15020 
15021  if(m_File == 0)
15022  {
15023  return VK_ERROR_INITIALIZATION_FAILED;
15024  }
15025 #endif
15026 
15027  // Write header.
15028  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15029  fprintf(m_File, "%s\n", "1,8");
15030 
15031  return VK_SUCCESS;
15032 }
15033 
15034 VmaRecorder::~VmaRecorder()
15035 {
15036  if(m_File != VMA_NULL)
15037  {
15038  fclose(m_File);
15039  }
15040 }
15041 
15042 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15043 {
15044  CallParams callParams;
15045  GetBasicParams(callParams);
15046 
15047  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15048  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15049  Flush();
15050 }
15051 
15052 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15053 {
15054  CallParams callParams;
15055  GetBasicParams(callParams);
15056 
15057  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15058  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15059  Flush();
15060 }
15061 
15062 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15063 {
15064  CallParams callParams;
15065  GetBasicParams(callParams);
15066 
15067  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15068  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15069  createInfo.memoryTypeIndex,
15070  createInfo.flags,
15071  createInfo.blockSize,
15072  (uint64_t)createInfo.minBlockCount,
15073  (uint64_t)createInfo.maxBlockCount,
15074  createInfo.frameInUseCount,
15075  pool);
15076  Flush();
15077 }
15078 
15079 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15080 {
15081  CallParams callParams;
15082  GetBasicParams(callParams);
15083 
15084  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15085  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15086  pool);
15087  Flush();
15088 }
15089 
15090 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15091  const VkMemoryRequirements& vkMemReq,
15092  const VmaAllocationCreateInfo& createInfo,
15093  VmaAllocation allocation)
15094 {
15095  CallParams callParams;
15096  GetBasicParams(callParams);
15097 
15098  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15099  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15100  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15101  vkMemReq.size,
15102  vkMemReq.alignment,
15103  vkMemReq.memoryTypeBits,
15104  createInfo.flags,
15105  createInfo.usage,
15106  createInfo.requiredFlags,
15107  createInfo.preferredFlags,
15108  createInfo.memoryTypeBits,
15109  createInfo.pool,
15110  allocation,
15111  userDataStr.GetString());
15112  Flush();
15113 }
15114 
15115 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15116  const VkMemoryRequirements& vkMemReq,
15117  const VmaAllocationCreateInfo& createInfo,
15118  uint64_t allocationCount,
15119  const VmaAllocation* pAllocations)
15120 {
15121  CallParams callParams;
15122  GetBasicParams(callParams);
15123 
15124  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15125  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15126  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15127  vkMemReq.size,
15128  vkMemReq.alignment,
15129  vkMemReq.memoryTypeBits,
15130  createInfo.flags,
15131  createInfo.usage,
15132  createInfo.requiredFlags,
15133  createInfo.preferredFlags,
15134  createInfo.memoryTypeBits,
15135  createInfo.pool);
15136  PrintPointerList(allocationCount, pAllocations);
15137  fprintf(m_File, ",%s\n", userDataStr.GetString());
15138  Flush();
15139 }
15140 
15141 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15142  const VkMemoryRequirements& vkMemReq,
15143  bool requiresDedicatedAllocation,
15144  bool prefersDedicatedAllocation,
15145  const VmaAllocationCreateInfo& createInfo,
15146  VmaAllocation allocation)
15147 {
15148  CallParams callParams;
15149  GetBasicParams(callParams);
15150 
15151  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15152  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15153  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15154  vkMemReq.size,
15155  vkMemReq.alignment,
15156  vkMemReq.memoryTypeBits,
15157  requiresDedicatedAllocation ? 1 : 0,
15158  prefersDedicatedAllocation ? 1 : 0,
15159  createInfo.flags,
15160  createInfo.usage,
15161  createInfo.requiredFlags,
15162  createInfo.preferredFlags,
15163  createInfo.memoryTypeBits,
15164  createInfo.pool,
15165  allocation,
15166  userDataStr.GetString());
15167  Flush();
15168 }
15169 
15170 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15171  const VkMemoryRequirements& vkMemReq,
15172  bool requiresDedicatedAllocation,
15173  bool prefersDedicatedAllocation,
15174  const VmaAllocationCreateInfo& createInfo,
15175  VmaAllocation allocation)
15176 {
15177  CallParams callParams;
15178  GetBasicParams(callParams);
15179 
15180  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15181  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15182  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15183  vkMemReq.size,
15184  vkMemReq.alignment,
15185  vkMemReq.memoryTypeBits,
15186  requiresDedicatedAllocation ? 1 : 0,
15187  prefersDedicatedAllocation ? 1 : 0,
15188  createInfo.flags,
15189  createInfo.usage,
15190  createInfo.requiredFlags,
15191  createInfo.preferredFlags,
15192  createInfo.memoryTypeBits,
15193  createInfo.pool,
15194  allocation,
15195  userDataStr.GetString());
15196  Flush();
15197 }
15198 
15199 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15200  VmaAllocation allocation)
15201 {
15202  CallParams callParams;
15203  GetBasicParams(callParams);
15204 
15205  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15206  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15207  allocation);
15208  Flush();
15209 }
15210 
15211 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15212  uint64_t allocationCount,
15213  const VmaAllocation* pAllocations)
15214 {
15215  CallParams callParams;
15216  GetBasicParams(callParams);
15217 
15218  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15219  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15220  PrintPointerList(allocationCount, pAllocations);
15221  fprintf(m_File, "\n");
15222  Flush();
15223 }
15224 
15225 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15226  VmaAllocation allocation,
15227  const void* pUserData)
15228 {
15229  CallParams callParams;
15230  GetBasicParams(callParams);
15231 
15232  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15233  UserDataString userDataStr(
15234  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15235  pUserData);
15236  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15237  allocation,
15238  userDataStr.GetString());
15239  Flush();
15240 }
15241 
15242 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15243  VmaAllocation allocation)
15244 {
15245  CallParams callParams;
15246  GetBasicParams(callParams);
15247 
15248  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15249  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15250  allocation);
15251  Flush();
15252 }
15253 
15254 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15255  VmaAllocation allocation)
15256 {
15257  CallParams callParams;
15258  GetBasicParams(callParams);
15259 
15260  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15261  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15262  allocation);
15263  Flush();
15264 }
15265 
15266 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15267  VmaAllocation allocation)
15268 {
15269  CallParams callParams;
15270  GetBasicParams(callParams);
15271 
15272  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15273  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15274  allocation);
15275  Flush();
15276 }
15277 
15278 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15279  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15280 {
15281  CallParams callParams;
15282  GetBasicParams(callParams);
15283 
15284  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15285  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15286  allocation,
15287  offset,
15288  size);
15289  Flush();
15290 }
15291 
15292 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15293  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15294 {
15295  CallParams callParams;
15296  GetBasicParams(callParams);
15297 
15298  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15299  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15300  allocation,
15301  offset,
15302  size);
15303  Flush();
15304 }
15305 
15306 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15307  const VkBufferCreateInfo& bufCreateInfo,
15308  const VmaAllocationCreateInfo& allocCreateInfo,
15309  VmaAllocation allocation)
15310 {
15311  CallParams callParams;
15312  GetBasicParams(callParams);
15313 
15314  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15315  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15316  fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15317  bufCreateInfo.flags,
15318  bufCreateInfo.size,
15319  bufCreateInfo.usage,
15320  bufCreateInfo.sharingMode,
15321  allocCreateInfo.flags,
15322  allocCreateInfo.usage,
15323  allocCreateInfo.requiredFlags,
15324  allocCreateInfo.preferredFlags,
15325  allocCreateInfo.memoryTypeBits,
15326  allocCreateInfo.pool,
15327  allocation,
15328  userDataStr.GetString());
15329  Flush();
15330 }
15331 
15332 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15333  const VkImageCreateInfo& imageCreateInfo,
15334  const VmaAllocationCreateInfo& allocCreateInfo,
15335  VmaAllocation allocation)
15336 {
15337  CallParams callParams;
15338  GetBasicParams(callParams);
15339 
15340  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15341  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15342  fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15343  imageCreateInfo.flags,
15344  imageCreateInfo.imageType,
15345  imageCreateInfo.format,
15346  imageCreateInfo.extent.width,
15347  imageCreateInfo.extent.height,
15348  imageCreateInfo.extent.depth,
15349  imageCreateInfo.mipLevels,
15350  imageCreateInfo.arrayLayers,
15351  imageCreateInfo.samples,
15352  imageCreateInfo.tiling,
15353  imageCreateInfo.usage,
15354  imageCreateInfo.sharingMode,
15355  imageCreateInfo.initialLayout,
15356  allocCreateInfo.flags,
15357  allocCreateInfo.usage,
15358  allocCreateInfo.requiredFlags,
15359  allocCreateInfo.preferredFlags,
15360  allocCreateInfo.memoryTypeBits,
15361  allocCreateInfo.pool,
15362  allocation,
15363  userDataStr.GetString());
15364  Flush();
15365 }
15366 
15367 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15368  VmaAllocation allocation)
15369 {
15370  CallParams callParams;
15371  GetBasicParams(callParams);
15372 
15373  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15374  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15375  allocation);
15376  Flush();
15377 }
15378 
15379 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15380  VmaAllocation allocation)
15381 {
15382  CallParams callParams;
15383  GetBasicParams(callParams);
15384 
15385  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15386  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15387  allocation);
15388  Flush();
15389 }
15390 
15391 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15392  VmaAllocation allocation)
15393 {
15394  CallParams callParams;
15395  GetBasicParams(callParams);
15396 
15397  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15398  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15399  allocation);
15400  Flush();
15401 }
15402 
15403 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15404  VmaAllocation allocation)
15405 {
15406  CallParams callParams;
15407  GetBasicParams(callParams);
15408 
15409  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15410  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15411  allocation);
15412  Flush();
15413 }
15414 
15415 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15416  VmaPool pool)
15417 {
15418  CallParams callParams;
15419  GetBasicParams(callParams);
15420 
15421  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15422  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15423  pool);
15424  Flush();
15425 }
15426 
15427 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15428  const VmaDefragmentationInfo2& info,
15430 {
15431  CallParams callParams;
15432  GetBasicParams(callParams);
15433 
15434  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15435  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15436  info.flags);
15437  PrintPointerList(info.allocationCount, info.pAllocations);
15438  fprintf(m_File, ",");
15439  PrintPointerList(info.poolCount, info.pPools);
15440  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15441  info.maxCpuBytesToMove,
15443  info.maxGpuBytesToMove,
15445  info.commandBuffer,
15446  ctx);
15447  Flush();
15448 }
15449 
15450 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15452 {
15453  CallParams callParams;
15454  GetBasicParams(callParams);
15455 
15456  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15457  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15458  ctx);
15459  Flush();
15460 }
15461 
15462 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15463  VmaPool pool,
15464  const char* name)
15465 {
15466  CallParams callParams;
15467  GetBasicParams(callParams);
15468 
15469  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15470  fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15471  pool, name != VMA_NULL ? name : "");
15472  Flush();
15473 }
15474 
15475 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15476 {
15477  if(pUserData != VMA_NULL)
15478  {
15479  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15480  {
15481  m_Str = (const char*)pUserData;
15482  }
15483  else
15484  {
15485  // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15486  snprintf(m_PtrStr, 17, "%p", pUserData);
15487  m_Str = m_PtrStr;
15488  }
15489  }
15490  else
15491  {
15492  m_Str = "";
15493  }
15494 }
15495 
15496 void VmaRecorder::WriteConfiguration(
15497  const VkPhysicalDeviceProperties& devProps,
15498  const VkPhysicalDeviceMemoryProperties& memProps,
15499  uint32_t vulkanApiVersion,
15500  bool dedicatedAllocationExtensionEnabled,
15501  bool bindMemory2ExtensionEnabled,
15502  bool memoryBudgetExtensionEnabled,
15503  bool deviceCoherentMemoryExtensionEnabled)
15504 {
15505  fprintf(m_File, "Config,Begin\n");
15506 
15507  fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15508 
15509  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15510  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15511  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15512  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15513  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15514  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15515 
15516  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15517  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15518  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15519 
15520  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15521  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15522  {
15523  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15524  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15525  }
15526  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15527  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15528  {
15529  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15530  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15531  }
15532 
15533  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15534  fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15535  fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15536  fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15537 
15538  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15539  fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15540  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15541  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15542  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15543  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15544  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15545  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15546  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15547 
15548  fprintf(m_File, "Config,End\n");
15549 }
15550 
15551 void VmaRecorder::GetBasicParams(CallParams& outParams)
15552 {
15553  #if defined(_WIN32)
15554  outParams.threadId = GetCurrentThreadId();
15555  #else
15556  // Use C++11 features to get thread id and convert it to uint32_t.
15557  // There is room for optimization since sstream is quite slow.
15558  // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15559  std::thread::id thread_id = std::this_thread::get_id();
15560  stringstream thread_id_to_string_converter;
15561  thread_id_to_string_converter << thread_id;
15562  string thread_id_as_string = thread_id_to_string_converter.str();
15563  outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15564  #endif
15565 
15566  auto current_time = std::chrono::high_resolution_clock::now();
15567 
15568  outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15569 }
15570 
15571 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15572 {
15573  if(count)
15574  {
15575  fprintf(m_File, "%p", pItems[0]);
15576  for(uint64_t i = 1; i < count; ++i)
15577  {
15578  fprintf(m_File, " %p", pItems[i]);
15579  }
15580  }
15581 }
15582 
15583 void VmaRecorder::Flush()
15584 {
15585  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15586  {
15587  fflush(m_File);
15588  }
15589 }
15590 
15591 #endif // #if VMA_RECORDING_ENABLED
15592 
15594 // VmaAllocationObjectAllocator
15595 
15596 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15597  m_Allocator(pAllocationCallbacks, 1024)
15598 {
15599 }
15600 
15601 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15602 {
15603  VmaMutexLock mutexLock(m_Mutex);
15604  return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15605 }
15606 
15607 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15608 {
15609  VmaMutexLock mutexLock(m_Mutex);
15610  m_Allocator.Free(hAlloc);
15611 }
15612 
15614 // VmaAllocator_T
15615 
15616 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15617  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15618  m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15619  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15620  m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15621  m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15622  m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15623  m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15624  m_hDevice(pCreateInfo->device),
15625  m_hInstance(pCreateInfo->instance),
15626  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15627  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15628  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15629  m_AllocationObjectAllocator(&m_AllocationCallbacks),
15630  m_HeapSizeLimitMask(0),
15631  m_PreferredLargeHeapBlockSize(0),
15632  m_PhysicalDevice(pCreateInfo->physicalDevice),
15633  m_CurrentFrameIndex(0),
15634  m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15635  m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15636  m_NextPoolId(0),
15637  m_GlobalMemoryTypeBits(UINT32_MAX)
15639  ,m_pRecorder(VMA_NULL)
15640 #endif
15641 {
15642  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15643  {
15644  m_UseKhrDedicatedAllocation = false;
15645  m_UseKhrBindMemory2 = false;
15646  }
15647 
15648  if(VMA_DEBUG_DETECT_CORRUPTION)
15649  {
15650  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15651  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15652  }
15653 
15654  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15655 
15656  if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15657  {
15658 #if !(VMA_DEDICATED_ALLOCATION)
15660  {
15661  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15662  }
15663 #endif
15664 #if !(VMA_BIND_MEMORY2)
15665  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15666  {
15667  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15668  }
15669 #endif
15670  }
15671 #if !(VMA_MEMORY_BUDGET)
15672  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15673  {
15674  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15675  }
15676 #endif
15677 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15678  if(m_UseKhrBufferDeviceAddress)
15679  {
15680  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
15681  }
15682 #endif
15683 #if VMA_VULKAN_VERSION < 1002000
15684  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15685  {
15686  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15687  }
15688 #endif
15689 #if VMA_VULKAN_VERSION < 1001000
15690  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15691  {
15692  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15693  }
15694 #endif
15695 
15696  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15697  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15698  memset(&m_MemProps, 0, sizeof(m_MemProps));
15699 
15700  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15701  memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15702  memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15703 
15704  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15705  {
15706  m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15707  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15708  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15709  }
15710 
15711  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15712 
15713  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15714  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15715 
15716  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15717  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15718  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15719  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15720 
15721  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15722  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15723 
15724  m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15725 
15726  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15727  {
15728  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15729  {
15730  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15731  if(limit != VK_WHOLE_SIZE)
15732  {
15733  m_HeapSizeLimitMask |= 1u << heapIndex;
15734  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15735  {
15736  m_MemProps.memoryHeaps[heapIndex].size = limit;
15737  }
15738  }
15739  }
15740  }
15741 
15742  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15743  {
15744  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15745 
15746  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15747  this,
15748  VK_NULL_HANDLE, // hParentPool
15749  memTypeIndex,
15750  preferredBlockSize,
15751  0,
15752  SIZE_MAX,
15753  GetBufferImageGranularity(),
15754  pCreateInfo->frameInUseCount,
15755  false, // explicitBlockSize
15756  false); // linearAlgorithm
15757  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15758  // becase minBlockCount is 0.
15759  m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15760 
15761  }
15762 }
15763 
15764 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15765 {
15766  VkResult res = VK_SUCCESS;
15767 
15768  if(pCreateInfo->pRecordSettings != VMA_NULL &&
15769  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15770  {
15771 #if VMA_RECORDING_ENABLED
15772  m_pRecorder = vma_new(this, VmaRecorder)();
15773  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15774  if(res != VK_SUCCESS)
15775  {
15776  return res;
15777  }
15778  m_pRecorder->WriteConfiguration(
15779  m_PhysicalDeviceProperties,
15780  m_MemProps,
15781  m_VulkanApiVersion,
15782  m_UseKhrDedicatedAllocation,
15783  m_UseKhrBindMemory2,
15784  m_UseExtMemoryBudget,
15785  m_UseAmdDeviceCoherentMemory);
15786  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15787 #else
15788  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15789  return VK_ERROR_FEATURE_NOT_PRESENT;
15790 #endif
15791  }
15792 
15793 #if VMA_MEMORY_BUDGET
15794  if(m_UseExtMemoryBudget)
15795  {
15796  UpdateVulkanBudget();
15797  }
15798 #endif // #if VMA_MEMORY_BUDGET
15799 
15800  return res;
15801 }
15802 
15803 VmaAllocator_T::~VmaAllocator_T()
15804 {
15805 #if VMA_RECORDING_ENABLED
15806  if(m_pRecorder != VMA_NULL)
15807  {
15808  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15809  vma_delete(this, m_pRecorder);
15810  }
15811 #endif
15812 
15813  VMA_ASSERT(m_Pools.empty());
15814 
15815  for(size_t i = GetMemoryTypeCount(); i--; )
15816  {
15817  if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15818  {
15819  VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15820  }
15821 
15822  vma_delete(this, m_pDedicatedAllocations[i]);
15823  vma_delete(this, m_pBlockVectors[i]);
15824  }
15825 }
15826 
15827 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15828 {
15829 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15830  ImportVulkanFunctions_Static();
15831 #endif
15832 
15833  if(pVulkanFunctions != VMA_NULL)
15834  {
15835  ImportVulkanFunctions_Custom(pVulkanFunctions);
15836  }
15837 
15838 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15839  ImportVulkanFunctions_Dynamic();
15840 #endif
15841 
15842  ValidateVulkanFunctions();
15843 }
15844 
15845 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15846 
15847 void VmaAllocator_T::ImportVulkanFunctions_Static()
15848 {
15849  // Vulkan 1.0
15850  m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15851  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15852  m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15853  m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15854  m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15855  m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15856  m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15857  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15858  m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15859  m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15860  m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15861  m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15862  m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15863  m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15864  m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15865  m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15866  m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15867 
15868  // Vulkan 1.1
15869 #if VMA_VULKAN_VERSION >= 1001000
15870  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15871  {
15872  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15873  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15874  m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15875  m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15876  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15877  }
15878 #endif
15879 }
15880 
15881 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15882 
15883 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15884 {
15885  VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15886 
15887 #define VMA_COPY_IF_NOT_NULL(funcName) \
15888  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15889 
15890  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15891  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15892  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15893  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15894  VMA_COPY_IF_NOT_NULL(vkMapMemory);
15895  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15896  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15897  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15898  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15899  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15900  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15901  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15902  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15903  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15904  VMA_COPY_IF_NOT_NULL(vkCreateImage);
15905  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15906  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15907 
15908 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15909  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15910  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15911 #endif
15912 
15913 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15914  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15915  VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15916 #endif
15917 
15918 #if VMA_MEMORY_BUDGET
15919  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15920 #endif
15921 
15922 #undef VMA_COPY_IF_NOT_NULL
15923 }
15924 
15925 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15926 
15927 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
15928 {
15929 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
15930  if(m_VulkanFunctions.memberName == VMA_NULL) \
15931  m_VulkanFunctions.memberName = \
15932  (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
15933 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
15934  if(m_VulkanFunctions.memberName == VMA_NULL) \
15935  m_VulkanFunctions.memberName = \
15936  (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
15937 
15938  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
15939  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
15940  VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
15941  VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
15942  VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
15943  VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
15944  VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
15945  VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
15946  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
15947  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
15948  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
15949  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
15950  VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
15951  VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
15952  VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
15953  VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
15954  VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
15955 
15956 #if VMA_VULKAN_VERSION >= 1001000
15957  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15958  {
15959  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
15960  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
15961  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
15962  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
15963  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
15964  }
15965 #endif
15966 
15967 #if VMA_DEDICATED_ALLOCATION
15968  if(m_UseKhrDedicatedAllocation)
15969  {
15970  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
15971  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
15972  }
15973 #endif
15974 
15975 #if VMA_BIND_MEMORY2
15976  if(m_UseKhrBindMemory2)
15977  {
15978  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
15979  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
15980  }
15981 #endif // #if VMA_BIND_MEMORY2
15982 
15983 #if VMA_MEMORY_BUDGET
15984  if(m_UseExtMemoryBudget)
15985  {
15986  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
15987  }
15988 #endif // #if VMA_MEMORY_BUDGET
15989 
15990 #undef VMA_FETCH_DEVICE_FUNC
15991 #undef VMA_FETCH_INSTANCE_FUNC
15992 }
15993 
15994 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15995 
15996 void VmaAllocator_T::ValidateVulkanFunctions()
15997 {
15998  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15999  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16000  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16001  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16002  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16003  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16004  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16005  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16006  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16007  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16008  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16009  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16010  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16011  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16012  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16013  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16014  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16015 
16016 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16017  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16018  {
16019  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16020  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16021  }
16022 #endif
16023 
16024 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16025  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16026  {
16027  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16028  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16029  }
16030 #endif
16031 
16032 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16033  if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16034  {
16035  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16036  }
16037 #endif
16038 }
16039 
16040 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16041 {
16042  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16043  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16044  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16045  return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16046 }
16047 
16048 VkResult VmaAllocator_T::AllocateMemoryOfType(
16049  VkDeviceSize size,
16050  VkDeviceSize alignment,
16051  bool dedicatedAllocation,
16052  VkBuffer dedicatedBuffer,
16053  VkBufferUsageFlags dedicatedBufferUsage,
16054  VkImage dedicatedImage,
16055  const VmaAllocationCreateInfo& createInfo,
16056  uint32_t memTypeIndex,
16057  VmaSuballocationType suballocType,
16058  size_t allocationCount,
16059  VmaAllocation* pAllocations)
16060 {
16061  VMA_ASSERT(pAllocations != VMA_NULL);
16062  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16063 
16064  VmaAllocationCreateInfo finalCreateInfo = createInfo;
16065 
16066  // If memory type is not HOST_VISIBLE, disable MAPPED.
16067  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16068  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16069  {
16070  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16071  }
16072  // If memory is lazily allocated, it should be always dedicated.
16073  if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16074  {
16076  }
16077 
16078  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16079  VMA_ASSERT(blockVector);
16080 
16081  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16082  bool preferDedicatedMemory =
16083  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16084  dedicatedAllocation ||
16085  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16086  size > preferredBlockSize / 2;
16087 
16088  if(preferDedicatedMemory &&
16089  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16090  finalCreateInfo.pool == VK_NULL_HANDLE)
16091  {
16093  }
16094 
16095  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16096  {
16097  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16098  {
16099  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16100  }
16101  else
16102  {
16103  return AllocateDedicatedMemory(
16104  size,
16105  suballocType,
16106  memTypeIndex,
16107  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16108  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16109  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16110  finalCreateInfo.pUserData,
16111  dedicatedBuffer,
16112  dedicatedBufferUsage,
16113  dedicatedImage,
16114  allocationCount,
16115  pAllocations);
16116  }
16117  }
16118  else
16119  {
16120  VkResult res = blockVector->Allocate(
16121  m_CurrentFrameIndex.load(),
16122  size,
16123  alignment,
16124  finalCreateInfo,
16125  suballocType,
16126  allocationCount,
16127  pAllocations);
16128  if(res == VK_SUCCESS)
16129  {
16130  return res;
16131  }
16132 
16133  // 5. Try dedicated memory.
16134  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16135  {
16136  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16137  }
16138  else
16139  {
16140  res = AllocateDedicatedMemory(
16141  size,
16142  suballocType,
16143  memTypeIndex,
16144  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16145  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16146  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16147  finalCreateInfo.pUserData,
16148  dedicatedBuffer,
16149  dedicatedBufferUsage,
16150  dedicatedImage,
16151  allocationCount,
16152  pAllocations);
16153  if(res == VK_SUCCESS)
16154  {
16155  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16156  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16157  return VK_SUCCESS;
16158  }
16159  else
16160  {
16161  // Everything failed: Return error code.
16162  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16163  return res;
16164  }
16165  }
16166  }
16167 }
16168 
16169 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16170  VkDeviceSize size,
16171  VmaSuballocationType suballocType,
16172  uint32_t memTypeIndex,
16173  bool withinBudget,
16174  bool map,
16175  bool isUserDataString,
16176  void* pUserData,
16177  VkBuffer dedicatedBuffer,
16178  VkBufferUsageFlags dedicatedBufferUsage,
16179  VkImage dedicatedImage,
16180  size_t allocationCount,
16181  VmaAllocation* pAllocations)
16182 {
16183  VMA_ASSERT(allocationCount > 0 && pAllocations);
16184 
16185  if(withinBudget)
16186  {
16187  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16188  VmaBudget heapBudget = {};
16189  GetBudget(&heapBudget, heapIndex, 1);
16190  if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16191  {
16192  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16193  }
16194  }
16195 
16196  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16197  allocInfo.memoryTypeIndex = memTypeIndex;
16198  allocInfo.allocationSize = size;
16199 
16200 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16201  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16202  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16203  {
16204  if(dedicatedBuffer != VK_NULL_HANDLE)
16205  {
16206  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16207  dedicatedAllocInfo.buffer = dedicatedBuffer;
16208  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16209  }
16210  else if(dedicatedImage != VK_NULL_HANDLE)
16211  {
16212  dedicatedAllocInfo.image = dedicatedImage;
16213  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16214  }
16215  }
16216 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16217 
16218 #if VMA_BUFFER_DEVICE_ADDRESS
16219  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16220  if(m_UseKhrBufferDeviceAddress)
16221  {
16222  bool canContainBufferWithDeviceAddress = true;
16223  if(dedicatedBuffer != VK_NULL_HANDLE)
16224  {
16225  canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16226  (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16227  }
16228  else if(dedicatedImage != VK_NULL_HANDLE)
16229  {
16230  canContainBufferWithDeviceAddress = false;
16231  }
16232  if(canContainBufferWithDeviceAddress)
16233  {
16234  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16235  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16236  }
16237  }
16238 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16239 
16240  size_t allocIndex;
16241  VkResult res = VK_SUCCESS;
16242  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16243  {
16244  res = AllocateDedicatedMemoryPage(
16245  size,
16246  suballocType,
16247  memTypeIndex,
16248  allocInfo,
16249  map,
16250  isUserDataString,
16251  pUserData,
16252  pAllocations + allocIndex);
16253  if(res != VK_SUCCESS)
16254  {
16255  break;
16256  }
16257  }
16258 
16259  if(res == VK_SUCCESS)
16260  {
16261  // Register them in m_pDedicatedAllocations.
16262  {
16263  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16264  AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16265  VMA_ASSERT(pDedicatedAllocations);
16266  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16267  {
16268  VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16269  }
16270  }
16271 
16272  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16273  }
16274  else
16275  {
16276  // Free all already created allocations.
16277  while(allocIndex--)
16278  {
16279  VmaAllocation currAlloc = pAllocations[allocIndex];
16280  VkDeviceMemory hMemory = currAlloc->GetMemory();
16281 
16282  /*
16283  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16284  before vkFreeMemory.
16285 
16286  if(currAlloc->GetMappedData() != VMA_NULL)
16287  {
16288  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16289  }
16290  */
16291 
16292  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16293  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16294  currAlloc->SetUserData(this, VMA_NULL);
16295  m_AllocationObjectAllocator.Free(currAlloc);
16296  }
16297 
16298  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16299  }
16300 
16301  return res;
16302 }
16303 
16304 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16305  VkDeviceSize size,
16306  VmaSuballocationType suballocType,
16307  uint32_t memTypeIndex,
16308  const VkMemoryAllocateInfo& allocInfo,
16309  bool map,
16310  bool isUserDataString,
16311  void* pUserData,
16312  VmaAllocation* pAllocation)
16313 {
16314  VkDeviceMemory hMemory = VK_NULL_HANDLE;
16315  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16316  if(res < 0)
16317  {
16318  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16319  return res;
16320  }
16321 
16322  void* pMappedData = VMA_NULL;
16323  if(map)
16324  {
16325  res = (*m_VulkanFunctions.vkMapMemory)(
16326  m_hDevice,
16327  hMemory,
16328  0,
16329  VK_WHOLE_SIZE,
16330  0,
16331  &pMappedData);
16332  if(res < 0)
16333  {
16334  VMA_DEBUG_LOG(" vkMapMemory FAILED");
16335  FreeVulkanMemory(memTypeIndex, size, hMemory);
16336  return res;
16337  }
16338  }
16339 
16340  *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16341  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16342  (*pAllocation)->SetUserData(this, pUserData);
16343  m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16344  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16345  {
16346  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16347  }
16348 
16349  return VK_SUCCESS;
16350 }
16351 
16352 void VmaAllocator_T::GetBufferMemoryRequirements(
16353  VkBuffer hBuffer,
16354  VkMemoryRequirements& memReq,
16355  bool& requiresDedicatedAllocation,
16356  bool& prefersDedicatedAllocation) const
16357 {
16358 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16359  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16360  {
16361  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16362  memReqInfo.buffer = hBuffer;
16363 
16364  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16365 
16366  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16367  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16368 
16369  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16370 
16371  memReq = memReq2.memoryRequirements;
16372  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16373  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16374  }
16375  else
16376 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16377  {
16378  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16379  requiresDedicatedAllocation = false;
16380  prefersDedicatedAllocation = false;
16381  }
16382 }
16383 
16384 void VmaAllocator_T::GetImageMemoryRequirements(
16385  VkImage hImage,
16386  VkMemoryRequirements& memReq,
16387  bool& requiresDedicatedAllocation,
16388  bool& prefersDedicatedAllocation) const
16389 {
16390 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16391  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16392  {
16393  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16394  memReqInfo.image = hImage;
16395 
16396  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16397 
16398  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16399  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16400 
16401  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16402 
16403  memReq = memReq2.memoryRequirements;
16404  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16405  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16406  }
16407  else
16408 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16409  {
16410  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16411  requiresDedicatedAllocation = false;
16412  prefersDedicatedAllocation = false;
16413  }
16414 }
16415 
16416 VkResult VmaAllocator_T::AllocateMemory(
16417  const VkMemoryRequirements& vkMemReq,
16418  bool requiresDedicatedAllocation,
16419  bool prefersDedicatedAllocation,
16420  VkBuffer dedicatedBuffer,
16421  VkBufferUsageFlags dedicatedBufferUsage,
16422  VkImage dedicatedImage,
16423  const VmaAllocationCreateInfo& createInfo,
16424  VmaSuballocationType suballocType,
16425  size_t allocationCount,
16426  VmaAllocation* pAllocations)
16427 {
16428  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16429 
16430  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16431 
16432  if(vkMemReq.size == 0)
16433  {
16434  return VK_ERROR_VALIDATION_FAILED_EXT;
16435  }
16436  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16437  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16438  {
16439  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16440  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16441  }
16442  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16444  {
16445  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16446  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16447  }
16448  if(requiresDedicatedAllocation)
16449  {
16450  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16451  {
16452  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16453  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16454  }
16455  if(createInfo.pool != VK_NULL_HANDLE)
16456  {
16457  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16458  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16459  }
16460  }
16461  if((createInfo.pool != VK_NULL_HANDLE) &&
16462  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16463  {
16464  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16465  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16466  }
16467 
16468  if(createInfo.pool != VK_NULL_HANDLE)
16469  {
16470  const VkDeviceSize alignmentForPool = VMA_MAX(
16471  vkMemReq.alignment,
16472  GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16473 
16474  VmaAllocationCreateInfo createInfoForPool = createInfo;
16475  // If memory type is not HOST_VISIBLE, disable MAPPED.
16476  if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16477  (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16478  {
16479  createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16480  }
16481 
16482  return createInfo.pool->m_BlockVector.Allocate(
16483  m_CurrentFrameIndex.load(),
16484  vkMemReq.size,
16485  alignmentForPool,
16486  createInfoForPool,
16487  suballocType,
16488  allocationCount,
16489  pAllocations);
16490  }
16491  else
16492  {
16493  // Bit mask of memory Vulkan types acceptable for this allocation.
16494  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16495  uint32_t memTypeIndex = UINT32_MAX;
16496  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16497  if(res == VK_SUCCESS)
16498  {
16499  VkDeviceSize alignmentForMemType = VMA_MAX(
16500  vkMemReq.alignment,
16501  GetMemoryTypeMinAlignment(memTypeIndex));
16502 
16503  res = AllocateMemoryOfType(
16504  vkMemReq.size,
16505  alignmentForMemType,
16506  requiresDedicatedAllocation || prefersDedicatedAllocation,
16507  dedicatedBuffer,
16508  dedicatedBufferUsage,
16509  dedicatedImage,
16510  createInfo,
16511  memTypeIndex,
16512  suballocType,
16513  allocationCount,
16514  pAllocations);
16515  // Succeeded on first try.
16516  if(res == VK_SUCCESS)
16517  {
16518  return res;
16519  }
16520  // Allocation from this memory type failed. Try other compatible memory types.
16521  else
16522  {
16523  for(;;)
16524  {
16525  // Remove old memTypeIndex from list of possibilities.
16526  memoryTypeBits &= ~(1u << memTypeIndex);
16527  // Find alternative memTypeIndex.
16528  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16529  if(res == VK_SUCCESS)
16530  {
16531  alignmentForMemType = VMA_MAX(
16532  vkMemReq.alignment,
16533  GetMemoryTypeMinAlignment(memTypeIndex));
16534 
16535  res = AllocateMemoryOfType(
16536  vkMemReq.size,
16537  alignmentForMemType,
16538  requiresDedicatedAllocation || prefersDedicatedAllocation,
16539  dedicatedBuffer,
16540  dedicatedBufferUsage,
16541  dedicatedImage,
16542  createInfo,
16543  memTypeIndex,
16544  suballocType,
16545  allocationCount,
16546  pAllocations);
16547  // Allocation from this alternative memory type succeeded.
16548  if(res == VK_SUCCESS)
16549  {
16550  return res;
16551  }
16552  // else: Allocation from this memory type failed. Try next one - next loop iteration.
16553  }
16554  // No other matching memory type index could be found.
16555  else
16556  {
16557  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16558  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16559  }
16560  }
16561  }
16562  }
16563  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16564  else
16565  return res;
16566  }
16567 }
16568 
16569 void VmaAllocator_T::FreeMemory(
16570  size_t allocationCount,
16571  const VmaAllocation* pAllocations)
16572 {
16573  VMA_ASSERT(pAllocations);
16574 
16575  for(size_t allocIndex = allocationCount; allocIndex--; )
16576  {
16577  VmaAllocation allocation = pAllocations[allocIndex];
16578 
16579  if(allocation != VK_NULL_HANDLE)
16580  {
16581  if(TouchAllocation(allocation))
16582  {
16583  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16584  {
16585  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16586  }
16587 
16588  switch(allocation->GetType())
16589  {
16590  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16591  {
16592  VmaBlockVector* pBlockVector = VMA_NULL;
16593  VmaPool hPool = allocation->GetBlock()->GetParentPool();
16594  if(hPool != VK_NULL_HANDLE)
16595  {
16596  pBlockVector = &hPool->m_BlockVector;
16597  }
16598  else
16599  {
16600  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16601  pBlockVector = m_pBlockVectors[memTypeIndex];
16602  }
16603  pBlockVector->Free(allocation);
16604  }
16605  break;
16606  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16607  FreeDedicatedMemory(allocation);
16608  break;
16609  default:
16610  VMA_ASSERT(0);
16611  }
16612  }
16613 
16614  // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16615  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16616  allocation->SetUserData(this, VMA_NULL);
16617  m_AllocationObjectAllocator.Free(allocation);
16618  }
16619  }
16620 }
16621 
16622 VkResult VmaAllocator_T::ResizeAllocation(
16623  const VmaAllocation alloc,
16624  VkDeviceSize newSize)
16625 {
16626  // This function is deprecated and so it does nothing. It's left for backward compatibility.
16627  if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16628  {
16629  return VK_ERROR_VALIDATION_FAILED_EXT;
16630  }
16631  if(newSize == alloc->GetSize())
16632  {
16633  return VK_SUCCESS;
16634  }
16635  return VK_ERROR_OUT_OF_POOL_MEMORY;
16636 }
16637 
16638 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16639 {
16640  // Initialize.
16641  InitStatInfo(pStats->total);
16642  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16643  InitStatInfo(pStats->memoryType[i]);
16644  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16645  InitStatInfo(pStats->memoryHeap[i]);
16646 
16647  // Process default pools.
16648  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16649  {
16650  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16651  VMA_ASSERT(pBlockVector);
16652  pBlockVector->AddStats(pStats);
16653  }
16654 
16655  // Process custom pools.
16656  {
16657  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16658  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16659  {
16660  m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16661  }
16662  }
16663 
16664  // Process dedicated allocations.
16665  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16666  {
16667  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16668  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16669  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16670  VMA_ASSERT(pDedicatedAllocVector);
16671  for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16672  {
16673  VmaStatInfo allocationStatInfo;
16674  (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16675  VmaAddStatInfo(pStats->total, allocationStatInfo);
16676  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16677  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16678  }
16679  }
16680 
16681  // Postprocess.
16682  VmaPostprocessCalcStatInfo(pStats->total);
16683  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16684  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16685  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16686  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16687 }
16688 
16689 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16690 {
16691 #if VMA_MEMORY_BUDGET
16692  if(m_UseExtMemoryBudget)
16693  {
16694  if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16695  {
16696  VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16697  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16698  {
16699  const uint32_t heapIndex = firstHeap + i;
16700 
16701  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16702  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16703 
16704  if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16705  {
16706  outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16707  outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16708  }
16709  else
16710  {
16711  outBudget->usage = 0;
16712  }
16713 
16714  // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16715  outBudget->budget = VMA_MIN(
16716  m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16717  }
16718  }
16719  else
16720  {
16721  UpdateVulkanBudget(); // Outside of mutex lock
16722  GetBudget(outBudget, firstHeap, heapCount); // Recursion
16723  }
16724  }
16725  else
16726 #endif
16727  {
16728  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16729  {
16730  const uint32_t heapIndex = firstHeap + i;
16731 
16732  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16733  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16734 
16735  outBudget->usage = outBudget->blockBytes;
16736  outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16737  }
16738  }
16739 }
16740 
16741 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16742 
16743 VkResult VmaAllocator_T::DefragmentationBegin(
16744  const VmaDefragmentationInfo2& info,
16745  VmaDefragmentationStats* pStats,
16746  VmaDefragmentationContext* pContext)
16747 {
16748  if(info.pAllocationsChanged != VMA_NULL)
16749  {
16750  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16751  }
16752 
16753  *pContext = vma_new(this, VmaDefragmentationContext_T)(
16754  this, m_CurrentFrameIndex.load(), info.flags, pStats);
16755 
16756  (*pContext)->AddPools(info.poolCount, info.pPools);
16757  (*pContext)->AddAllocations(
16759 
16760  VkResult res = (*pContext)->Defragment(
16763  info.commandBuffer, pStats, info.flags);
16764 
16765  if(res != VK_NOT_READY)
16766  {
16767  vma_delete(this, *pContext);
16768  *pContext = VMA_NULL;
16769  }
16770 
16771  return res;
16772 }
16773 
16774 VkResult VmaAllocator_T::DefragmentationEnd(
16775  VmaDefragmentationContext context)
16776 {
16777  vma_delete(this, context);
16778  return VK_SUCCESS;
16779 }
16780 
16781 VkResult VmaAllocator_T::DefragmentationPassBegin(
16783  VmaDefragmentationContext context)
16784 {
16785  return context->DefragmentPassBegin(pInfo);
16786 }
16787 VkResult VmaAllocator_T::DefragmentationPassEnd(
16788  VmaDefragmentationContext context)
16789 {
16790  return context->DefragmentPassEnd();
16791 
16792 }
16793 
16794 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16795 {
16796  if(hAllocation->CanBecomeLost())
16797  {
16798  /*
16799  Warning: This is a carefully designed algorithm.
16800  Do not modify unless you really know what you're doing :)
16801  */
16802  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16803  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16804  for(;;)
16805  {
16806  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16807  {
16808  pAllocationInfo->memoryType = UINT32_MAX;
16809  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
16810  pAllocationInfo->offset = 0;
16811  pAllocationInfo->size = hAllocation->GetSize();
16812  pAllocationInfo->pMappedData = VMA_NULL;
16813  pAllocationInfo->pUserData = hAllocation->GetUserData();
16814  return;
16815  }
16816  else if(localLastUseFrameIndex == localCurrFrameIndex)
16817  {
16818  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16819  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16820  pAllocationInfo->offset = hAllocation->GetOffset();
16821  pAllocationInfo->size = hAllocation->GetSize();
16822  pAllocationInfo->pMappedData = VMA_NULL;
16823  pAllocationInfo->pUserData = hAllocation->GetUserData();
16824  return;
16825  }
16826  else // Last use time earlier than current time.
16827  {
16828  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16829  {
16830  localLastUseFrameIndex = localCurrFrameIndex;
16831  }
16832  }
16833  }
16834  }
16835  else
16836  {
16837 #if VMA_STATS_STRING_ENABLED
16838  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16839  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16840  for(;;)
16841  {
16842  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16843  if(localLastUseFrameIndex == localCurrFrameIndex)
16844  {
16845  break;
16846  }
16847  else // Last use time earlier than current time.
16848  {
16849  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16850  {
16851  localLastUseFrameIndex = localCurrFrameIndex;
16852  }
16853  }
16854  }
16855 #endif
16856 
16857  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16858  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16859  pAllocationInfo->offset = hAllocation->GetOffset();
16860  pAllocationInfo->size = hAllocation->GetSize();
16861  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
16862  pAllocationInfo->pUserData = hAllocation->GetUserData();
16863  }
16864 }
16865 
16866 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
16867 {
16868  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
16869  if(hAllocation->CanBecomeLost())
16870  {
16871  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16872  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16873  for(;;)
16874  {
16875  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16876  {
16877  return false;
16878  }
16879  else if(localLastUseFrameIndex == localCurrFrameIndex)
16880  {
16881  return true;
16882  }
16883  else // Last use time earlier than current time.
16884  {
16885  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16886  {
16887  localLastUseFrameIndex = localCurrFrameIndex;
16888  }
16889  }
16890  }
16891  }
16892  else
16893  {
16894 #if VMA_STATS_STRING_ENABLED
16895  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16896  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16897  for(;;)
16898  {
16899  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16900  if(localLastUseFrameIndex == localCurrFrameIndex)
16901  {
16902  break;
16903  }
16904  else // Last use time earlier than current time.
16905  {
16906  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16907  {
16908  localLastUseFrameIndex = localCurrFrameIndex;
16909  }
16910  }
16911  }
16912 #endif
16913 
16914  return true;
16915  }
16916 }
16917 
16918 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
16919 {
16920  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
16921 
16922  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
16923 
16924  if(newCreateInfo.maxBlockCount == 0)
16925  {
16926  newCreateInfo.maxBlockCount = SIZE_MAX;
16927  }
16928  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
16929  {
16930  return VK_ERROR_INITIALIZATION_FAILED;
16931  }
16932  // Memory type index out of range or forbidden.
16933  if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
16934  ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
16935  {
16936  return VK_ERROR_FEATURE_NOT_PRESENT;
16937  }
16938 
16939  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
16940 
16941  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
16942 
16943  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
16944  if(res != VK_SUCCESS)
16945  {
16946  vma_delete(this, *pPool);
16947  *pPool = VMA_NULL;
16948  return res;
16949  }
16950 
16951  // Add to m_Pools.
16952  {
16953  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16954  (*pPool)->SetId(m_NextPoolId++);
16955  VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
16956  }
16957 
16958  return VK_SUCCESS;
16959 }
16960 
16961 void VmaAllocator_T::DestroyPool(VmaPool pool)
16962 {
16963  // Remove from m_Pools.
16964  {
16965  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16966  bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
16967  VMA_ASSERT(success && "Pool not found in Allocator.");
16968  }
16969 
16970  vma_delete(this, pool);
16971 }
16972 
16973 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
16974 {
16975  pool->m_BlockVector.GetPoolStats(pPoolStats);
16976 }
16977 
16978 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
16979 {
16980  m_CurrentFrameIndex.store(frameIndex);
16981 
16982 #if VMA_MEMORY_BUDGET
16983  if(m_UseExtMemoryBudget)
16984  {
16985  UpdateVulkanBudget();
16986  }
16987 #endif // #if VMA_MEMORY_BUDGET
16988 }
16989 
16990 void VmaAllocator_T::MakePoolAllocationsLost(
16991  VmaPool hPool,
16992  size_t* pLostAllocationCount)
16993 {
16994  hPool->m_BlockVector.MakePoolAllocationsLost(
16995  m_CurrentFrameIndex.load(),
16996  pLostAllocationCount);
16997 }
16998 
16999 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17000 {
17001  return hPool->m_BlockVector.CheckCorruption();
17002 }
17003 
17004 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17005 {
17006  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17007 
17008  // Process default pools.
17009  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17010  {
17011  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17012  {
17013  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17014  VMA_ASSERT(pBlockVector);
17015  VkResult localRes = pBlockVector->CheckCorruption();
17016  switch(localRes)
17017  {
17018  case VK_ERROR_FEATURE_NOT_PRESENT:
17019  break;
17020  case VK_SUCCESS:
17021  finalRes = VK_SUCCESS;
17022  break;
17023  default:
17024  return localRes;
17025  }
17026  }
17027  }
17028 
17029  // Process custom pools.
17030  {
17031  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17032  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17033  {
17034  if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17035  {
17036  VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17037  switch(localRes)
17038  {
17039  case VK_ERROR_FEATURE_NOT_PRESENT:
17040  break;
17041  case VK_SUCCESS:
17042  finalRes = VK_SUCCESS;
17043  break;
17044  default:
17045  return localRes;
17046  }
17047  }
17048  }
17049  }
17050 
17051  return finalRes;
17052 }
17053 
17054 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17055 {
17056  *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17057  (*pAllocation)->InitLost();
17058 }
17059 
17060 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17061 {
17062  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17063 
17064  // HeapSizeLimit is in effect for this heap.
17065  if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17066  {
17067  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17068  VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17069  for(;;)
17070  {
17071  const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17072  if(blockBytesAfterAllocation > heapSize)
17073  {
17074  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17075  }
17076  if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17077  {
17078  break;
17079  }
17080  }
17081  }
17082  else
17083  {
17084  m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17085  }
17086 
17087  // VULKAN CALL vkAllocateMemory.
17088  VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17089 
17090  if(res == VK_SUCCESS)
17091  {
17092 #if VMA_MEMORY_BUDGET
17093  ++m_Budget.m_OperationsSinceBudgetFetch;
17094 #endif
17095 
17096  // Informative callback.
17097  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17098  {
17099  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17100  }
17101  }
17102  else
17103  {
17104  m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17105  }
17106 
17107  return res;
17108 }
17109 
17110 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17111 {
17112  // Informative callback.
17113  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17114  {
17115  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17116  }
17117 
17118  // VULKAN CALL vkFreeMemory.
17119  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17120 
17121  m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17122 }
17123 
17124 VkResult VmaAllocator_T::BindVulkanBuffer(
17125  VkDeviceMemory memory,
17126  VkDeviceSize memoryOffset,
17127  VkBuffer buffer,
17128  const void* pNext)
17129 {
17130  if(pNext != VMA_NULL)
17131  {
17132 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17133  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17134  m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17135  {
17136  VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17137  bindBufferMemoryInfo.pNext = pNext;
17138  bindBufferMemoryInfo.buffer = buffer;
17139  bindBufferMemoryInfo.memory = memory;
17140  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17141  return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17142  }
17143  else
17144 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17145  {
17146  return VK_ERROR_EXTENSION_NOT_PRESENT;
17147  }
17148  }
17149  else
17150  {
17151  return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17152  }
17153 }
17154 
17155 VkResult VmaAllocator_T::BindVulkanImage(
17156  VkDeviceMemory memory,
17157  VkDeviceSize memoryOffset,
17158  VkImage image,
17159  const void* pNext)
17160 {
17161  if(pNext != VMA_NULL)
17162  {
17163 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17164  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17165  m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17166  {
17167  VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17168  bindBufferMemoryInfo.pNext = pNext;
17169  bindBufferMemoryInfo.image = image;
17170  bindBufferMemoryInfo.memory = memory;
17171  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17172  return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17173  }
17174  else
17175 #endif // #if VMA_BIND_MEMORY2
17176  {
17177  return VK_ERROR_EXTENSION_NOT_PRESENT;
17178  }
17179  }
17180  else
17181  {
17182  return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17183  }
17184 }
17185 
17186 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17187 {
17188  if(hAllocation->CanBecomeLost())
17189  {
17190  return VK_ERROR_MEMORY_MAP_FAILED;
17191  }
17192 
17193  switch(hAllocation->GetType())
17194  {
17195  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17196  {
17197  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17198  char *pBytes = VMA_NULL;
17199  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17200  if(res == VK_SUCCESS)
17201  {
17202  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17203  hAllocation->BlockAllocMap();
17204  }
17205  return res;
17206  }
17207  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17208  return hAllocation->DedicatedAllocMap(this, ppData);
17209  default:
17210  VMA_ASSERT(0);
17211  return VK_ERROR_MEMORY_MAP_FAILED;
17212  }
17213 }
17214 
17215 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17216 {
17217  switch(hAllocation->GetType())
17218  {
17219  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17220  {
17221  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17222  hAllocation->BlockAllocUnmap();
17223  pBlock->Unmap(this, 1);
17224  }
17225  break;
17226  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17227  hAllocation->DedicatedAllocUnmap(this);
17228  break;
17229  default:
17230  VMA_ASSERT(0);
17231  }
17232 }
17233 
17234 VkResult VmaAllocator_T::BindBufferMemory(
17235  VmaAllocation hAllocation,
17236  VkDeviceSize allocationLocalOffset,
17237  VkBuffer hBuffer,
17238  const void* pNext)
17239 {
17240  VkResult res = VK_SUCCESS;
17241  switch(hAllocation->GetType())
17242  {
17243  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17244  res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17245  break;
17246  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17247  {
17248  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17249  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17250  res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17251  break;
17252  }
17253  default:
17254  VMA_ASSERT(0);
17255  }
17256  return res;
17257 }
17258 
17259 VkResult VmaAllocator_T::BindImageMemory(
17260  VmaAllocation hAllocation,
17261  VkDeviceSize allocationLocalOffset,
17262  VkImage hImage,
17263  const void* pNext)
17264 {
17265  VkResult res = VK_SUCCESS;
17266  switch(hAllocation->GetType())
17267  {
17268  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17269  res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17270  break;
17271  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17272  {
17273  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17274  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17275  res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17276  break;
17277  }
17278  default:
17279  VMA_ASSERT(0);
17280  }
17281  return res;
17282 }
17283 
17284 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17285  VmaAllocation hAllocation,
17286  VkDeviceSize offset, VkDeviceSize size,
17287  VMA_CACHE_OPERATION op)
17288 {
17289  VkResult res = VK_SUCCESS;
17290 
17291  VkMappedMemoryRange memRange = {};
17292  if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17293  {
17294  switch(op)
17295  {
17296  case VMA_CACHE_FLUSH:
17297  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17298  break;
17299  case VMA_CACHE_INVALIDATE:
17300  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17301  break;
17302  default:
17303  VMA_ASSERT(0);
17304  }
17305  }
17306  // else: Just ignore this call.
17307  return res;
17308 }
17309 
17310 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17311  uint32_t allocationCount,
17312  const VmaAllocation* allocations,
17313  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17314  VMA_CACHE_OPERATION op)
17315 {
17316  typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17317  typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17318  RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17319 
17320  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17321  {
17322  const VmaAllocation alloc = allocations[allocIndex];
17323  const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17324  const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17325  VkMappedMemoryRange newRange;
17326  if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17327  {
17328  ranges.push_back(newRange);
17329  }
17330  }
17331 
17332  VkResult res = VK_SUCCESS;
17333  if(!ranges.empty())
17334  {
17335  switch(op)
17336  {
17337  case VMA_CACHE_FLUSH:
17338  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17339  break;
17340  case VMA_CACHE_INVALIDATE:
17341  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17342  break;
17343  default:
17344  VMA_ASSERT(0);
17345  }
17346  }
17347  // else: Just ignore this call.
17348  return res;
17349 }
17350 
17351 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17352 {
17353  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17354 
17355  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17356  {
17357  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17358  AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17359  VMA_ASSERT(pDedicatedAllocations);
17360  bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17361  VMA_ASSERT(success);
17362  }
17363 
17364  VkDeviceMemory hMemory = allocation->GetMemory();
17365 
17366  /*
17367  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17368  before vkFreeMemory.
17369 
17370  if(allocation->GetMappedData() != VMA_NULL)
17371  {
17372  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17373  }
17374  */
17375 
17376  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17377 
17378  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17379 }
17380 
17381 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17382 {
17383  VkBufferCreateInfo dummyBufCreateInfo;
17384  VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17385 
17386  uint32_t memoryTypeBits = 0;
17387 
17388  // Create buffer.
17389  VkBuffer buf = VK_NULL_HANDLE;
17390  VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17391  m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17392  if(res == VK_SUCCESS)
17393  {
17394  // Query for supported memory types.
17395  VkMemoryRequirements memReq;
17396  (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17397  memoryTypeBits = memReq.memoryTypeBits;
17398 
17399  // Destroy buffer.
17400  (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17401  }
17402 
17403  return memoryTypeBits;
17404 }
17405 
17406 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17407 {
17408  // Make sure memory information is already fetched.
17409  VMA_ASSERT(GetMemoryTypeCount() > 0);
17410 
17411  uint32_t memoryTypeBits = UINT32_MAX;
17412 
17413  if(!m_UseAmdDeviceCoherentMemory)
17414  {
17415  // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17416  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17417  {
17418  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17419  {
17420  memoryTypeBits &= ~(1u << memTypeIndex);
17421  }
17422  }
17423  }
17424 
17425  return memoryTypeBits;
17426 }
17427 
17428 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17429  VmaAllocation allocation,
17430  VkDeviceSize offset, VkDeviceSize size,
17431  VkMappedMemoryRange& outRange) const
17432 {
17433  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17434  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17435  {
17436  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17437  const VkDeviceSize allocationSize = allocation->GetSize();
17438  VMA_ASSERT(offset <= allocationSize);
17439 
17440  outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17441  outRange.pNext = VMA_NULL;
17442  outRange.memory = allocation->GetMemory();
17443 
17444  switch(allocation->GetType())
17445  {
17446  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17447  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17448  if(size == VK_WHOLE_SIZE)
17449  {
17450  outRange.size = allocationSize - outRange.offset;
17451  }
17452  else
17453  {
17454  VMA_ASSERT(offset + size <= allocationSize);
17455  outRange.size = VMA_MIN(
17456  VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17457  allocationSize - outRange.offset);
17458  }
17459  break;
17460  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17461  {
17462  // 1. Still within this allocation.
17463  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17464  if(size == VK_WHOLE_SIZE)
17465  {
17466  size = allocationSize - offset;
17467  }
17468  else
17469  {
17470  VMA_ASSERT(offset + size <= allocationSize);
17471  }
17472  outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17473 
17474  // 2. Adjust to whole block.
17475  const VkDeviceSize allocationOffset = allocation->GetOffset();
17476  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17477  const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17478  outRange.offset += allocationOffset;
17479  outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17480 
17481  break;
17482  }
17483  default:
17484  VMA_ASSERT(0);
17485  }
17486  return true;
17487  }
17488  return false;
17489 }
17490 
17491 #if VMA_MEMORY_BUDGET
17492 
17493 void VmaAllocator_T::UpdateVulkanBudget()
17494 {
17495  VMA_ASSERT(m_UseExtMemoryBudget);
17496 
17497  VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17498 
17499  VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17500  VmaPnextChainPushFront(&memProps, &budgetProps);
17501 
17502  GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17503 
17504  {
17505  VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17506 
17507  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17508  {
17509  m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17510  m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17511  m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17512 
17513  // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17514  if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17515  {
17516  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17517  }
17518  else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17519  {
17520  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17521  }
17522  if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17523  {
17524  m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17525  }
17526  }
17527  m_Budget.m_OperationsSinceBudgetFetch = 0;
17528  }
17529 }
17530 
17531 #endif // #if VMA_MEMORY_BUDGET
17532 
17533 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17534 {
17535  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17536  !hAllocation->CanBecomeLost() &&
17537  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17538  {
17539  void* pData = VMA_NULL;
17540  VkResult res = Map(hAllocation, &pData);
17541  if(res == VK_SUCCESS)
17542  {
17543  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17544  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17545  Unmap(hAllocation);
17546  }
17547  else
17548  {
17549  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17550  }
17551  }
17552 }
17553 
17554 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17555 {
17556  uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17557  if(memoryTypeBits == UINT32_MAX)
17558  {
17559  memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17560  m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17561  }
17562  return memoryTypeBits;
17563 }
17564 
17565 #if VMA_STATS_STRING_ENABLED
17566 
17567 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17568 {
17569  bool dedicatedAllocationsStarted = false;
17570  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17571  {
17572  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17573  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17574  VMA_ASSERT(pDedicatedAllocVector);
17575  if(pDedicatedAllocVector->empty() == false)
17576  {
17577  if(dedicatedAllocationsStarted == false)
17578  {
17579  dedicatedAllocationsStarted = true;
17580  json.WriteString("DedicatedAllocations");
17581  json.BeginObject();
17582  }
17583 
17584  json.BeginString("Type ");
17585  json.ContinueString(memTypeIndex);
17586  json.EndString();
17587 
17588  json.BeginArray();
17589 
17590  for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17591  {
17592  json.BeginObject(true);
17593  const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17594  hAlloc->PrintParameters(json);
17595  json.EndObject();
17596  }
17597 
17598  json.EndArray();
17599  }
17600  }
17601  if(dedicatedAllocationsStarted)
17602  {
17603  json.EndObject();
17604  }
17605 
17606  {
17607  bool allocationsStarted = false;
17608  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17609  {
17610  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17611  {
17612  if(allocationsStarted == false)
17613  {
17614  allocationsStarted = true;
17615  json.WriteString("DefaultPools");
17616  json.BeginObject();
17617  }
17618 
17619  json.BeginString("Type ");
17620  json.ContinueString(memTypeIndex);
17621  json.EndString();
17622 
17623  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17624  }
17625  }
17626  if(allocationsStarted)
17627  {
17628  json.EndObject();
17629  }
17630  }
17631 
17632  // Custom pools
17633  {
17634  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17635  const size_t poolCount = m_Pools.size();
17636  if(poolCount > 0)
17637  {
17638  json.WriteString("Pools");
17639  json.BeginObject();
17640  for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17641  {
17642  json.BeginString();
17643  json.ContinueString(m_Pools[poolIndex]->GetId());
17644  json.EndString();
17645 
17646  m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17647  }
17648  json.EndObject();
17649  }
17650  }
17651 }
17652 
17653 #endif // #if VMA_STATS_STRING_ENABLED
17654 
17656 // Public interface
17657 
17658 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17659  const VmaAllocatorCreateInfo* pCreateInfo,
17660  VmaAllocator* pAllocator)
17661 {
17662  VMA_ASSERT(pCreateInfo && pAllocator);
17663  VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17664  (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17665  VMA_DEBUG_LOG("vmaCreateAllocator");
17666  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17667  return (*pAllocator)->Init(pCreateInfo);
17668 }
17669 
17670 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17671  VmaAllocator allocator)
17672 {
17673  if(allocator != VK_NULL_HANDLE)
17674  {
17675  VMA_DEBUG_LOG("vmaDestroyAllocator");
17676  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17677  vma_delete(&allocationCallbacks, allocator);
17678  }
17679 }
17680 
17681 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17682 {
17683  VMA_ASSERT(allocator && pAllocatorInfo);
17684  pAllocatorInfo->instance = allocator->m_hInstance;
17685  pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17686  pAllocatorInfo->device = allocator->m_hDevice;
17687 }
17688 
17689 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17690  VmaAllocator allocator,
17691  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17692 {
17693  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17694  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17695 }
17696 
17697 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17698  VmaAllocator allocator,
17699  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17700 {
17701  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17702  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17703 }
17704 
17705 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17706  VmaAllocator allocator,
17707  uint32_t memoryTypeIndex,
17708  VkMemoryPropertyFlags* pFlags)
17709 {
17710  VMA_ASSERT(allocator && pFlags);
17711  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17712  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17713 }
17714 
17715 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17716  VmaAllocator allocator,
17717  uint32_t frameIndex)
17718 {
17719  VMA_ASSERT(allocator);
17720  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17721 
17722  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17723 
17724  allocator->SetCurrentFrameIndex(frameIndex);
17725 }
17726 
17727 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17728  VmaAllocator allocator,
17729  VmaStats* pStats)
17730 {
17731  VMA_ASSERT(allocator && pStats);
17732  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17733  allocator->CalculateStats(pStats);
17734 }
17735 
17736 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17737  VmaAllocator allocator,
17738  VmaBudget* pBudget)
17739 {
17740  VMA_ASSERT(allocator && pBudget);
17741  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17742  allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17743 }
17744 
17745 #if VMA_STATS_STRING_ENABLED
17746 
17747 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17748  VmaAllocator allocator,
17749  char** ppStatsString,
17750  VkBool32 detailedMap)
17751 {
17752  VMA_ASSERT(allocator && ppStatsString);
17753  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17754 
17755  VmaStringBuilder sb(allocator);
17756  {
17757  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17758  json.BeginObject();
17759 
17760  VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17761  allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17762 
17763  VmaStats stats;
17764  allocator->CalculateStats(&stats);
17765 
17766  json.WriteString("Total");
17767  VmaPrintStatInfo(json, stats.total);
17768 
17769  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17770  {
17771  json.BeginString("Heap ");
17772  json.ContinueString(heapIndex);
17773  json.EndString();
17774  json.BeginObject();
17775 
17776  json.WriteString("Size");
17777  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17778 
17779  json.WriteString("Flags");
17780  json.BeginArray(true);
17781  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17782  {
17783  json.WriteString("DEVICE_LOCAL");
17784  }
17785  json.EndArray();
17786 
17787  json.WriteString("Budget");
17788  json.BeginObject();
17789  {
17790  json.WriteString("BlockBytes");
17791  json.WriteNumber(budget[heapIndex].blockBytes);
17792  json.WriteString("AllocationBytes");
17793  json.WriteNumber(budget[heapIndex].allocationBytes);
17794  json.WriteString("Usage");
17795  json.WriteNumber(budget[heapIndex].usage);
17796  json.WriteString("Budget");
17797  json.WriteNumber(budget[heapIndex].budget);
17798  }
17799  json.EndObject();
17800 
17801  if(stats.memoryHeap[heapIndex].blockCount > 0)
17802  {
17803  json.WriteString("Stats");
17804  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
17805  }
17806 
17807  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
17808  {
17809  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
17810  {
17811  json.BeginString("Type ");
17812  json.ContinueString(typeIndex);
17813  json.EndString();
17814 
17815  json.BeginObject();
17816 
17817  json.WriteString("Flags");
17818  json.BeginArray(true);
17819  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
17820  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
17821  {
17822  json.WriteString("DEVICE_LOCAL");
17823  }
17824  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17825  {
17826  json.WriteString("HOST_VISIBLE");
17827  }
17828  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
17829  {
17830  json.WriteString("HOST_COHERENT");
17831  }
17832  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
17833  {
17834  json.WriteString("HOST_CACHED");
17835  }
17836  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
17837  {
17838  json.WriteString("LAZILY_ALLOCATED");
17839  }
17840  if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
17841  {
17842  json.WriteString(" PROTECTED");
17843  }
17844  if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17845  {
17846  json.WriteString(" DEVICE_COHERENT");
17847  }
17848  if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
17849  {
17850  json.WriteString(" DEVICE_UNCACHED");
17851  }
17852  json.EndArray();
17853 
17854  if(stats.memoryType[typeIndex].blockCount > 0)
17855  {
17856  json.WriteString("Stats");
17857  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17858  }
17859 
17860  json.EndObject();
17861  }
17862  }
17863 
17864  json.EndObject();
17865  }
17866  if(detailedMap == VK_TRUE)
17867  {
17868  allocator->PrintDetailedMap(json);
17869  }
17870 
17871  json.EndObject();
17872  }
17873 
17874  const size_t len = sb.GetLength();
17875  char* const pChars = vma_new_array(allocator, char, len + 1);
17876  if(len > 0)
17877  {
17878  memcpy(pChars, sb.GetData(), len);
17879  }
17880  pChars[len] = '\0';
17881  *ppStatsString = pChars;
17882 }
17883 
17884 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17885  VmaAllocator allocator,
17886  char* pStatsString)
17887 {
17888  if(pStatsString != VMA_NULL)
17889  {
17890  VMA_ASSERT(allocator);
17891  size_t len = strlen(pStatsString);
17892  vma_delete_array(allocator, pStatsString, len + 1);
17893  }
17894 }
17895 
17896 #endif // #if VMA_STATS_STRING_ENABLED
17897 
17898 /*
17899 This function is not protected by any mutex because it just reads immutable data.
17900 */
17901 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17902  VmaAllocator allocator,
17903  uint32_t memoryTypeBits,
17904  const VmaAllocationCreateInfo* pAllocationCreateInfo,
17905  uint32_t* pMemoryTypeIndex)
17906 {
17907  VMA_ASSERT(allocator != VK_NULL_HANDLE);
17908  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17909  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17910 
17911  memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17912 
17913  if(pAllocationCreateInfo->memoryTypeBits != 0)
17914  {
17915  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17916  }
17917 
17918  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17919  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17920  uint32_t notPreferredFlags = 0;
17921 
17922  // Convert usage to requiredFlags and preferredFlags.
17923  switch(pAllocationCreateInfo->usage)
17924  {
17926  break;
17928  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17929  {
17930  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17931  }
17932  break;
17934  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17935  break;
17937  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17938  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17939  {
17940  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17941  }
17942  break;
17944  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17945  preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17946  break;
17948  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17949  break;
17951  requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17952  break;
17953  default:
17954  VMA_ASSERT(0);
17955  break;
17956  }
17957 
17958  // Avoid DEVICE_COHERENT unless explicitly requested.
17959  if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17960  (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17961  {
17962  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17963  }
17964 
17965  *pMemoryTypeIndex = UINT32_MAX;
17966  uint32_t minCost = UINT32_MAX;
17967  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17968  memTypeIndex < allocator->GetMemoryTypeCount();
17969  ++memTypeIndex, memTypeBit <<= 1)
17970  {
17971  // This memory type is acceptable according to memoryTypeBits bitmask.
17972  if((memTypeBit & memoryTypeBits) != 0)
17973  {
17974  const VkMemoryPropertyFlags currFlags =
17975  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17976  // This memory type contains requiredFlags.
17977  if((requiredFlags & ~currFlags) == 0)
17978  {
17979  // Calculate cost as number of bits from preferredFlags not present in this memory type.
17980  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17981  VmaCountBitsSet(currFlags & notPreferredFlags);
17982  // Remember memory type with lowest cost.
17983  if(currCost < minCost)
17984  {
17985  *pMemoryTypeIndex = memTypeIndex;
17986  if(currCost == 0)
17987  {
17988  return VK_SUCCESS;
17989  }
17990  minCost = currCost;
17991  }
17992  }
17993  }
17994  }
17995  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17996 }
17997 
17998 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17999  VmaAllocator allocator,
18000  const VkBufferCreateInfo* pBufferCreateInfo,
18001  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18002  uint32_t* pMemoryTypeIndex)
18003 {
18004  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18005  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18006  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18007  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18008 
18009  const VkDevice hDev = allocator->m_hDevice;
18010  VkBuffer hBuffer = VK_NULL_HANDLE;
18011  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18012  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18013  if(res == VK_SUCCESS)
18014  {
18015  VkMemoryRequirements memReq = {};
18016  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18017  hDev, hBuffer, &memReq);
18018 
18019  res = vmaFindMemoryTypeIndex(
18020  allocator,
18021  memReq.memoryTypeBits,
18022  pAllocationCreateInfo,
18023  pMemoryTypeIndex);
18024 
18025  allocator->GetVulkanFunctions().vkDestroyBuffer(
18026  hDev, hBuffer, allocator->GetAllocationCallbacks());
18027  }
18028  return res;
18029 }
18030 
18031 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18032  VmaAllocator allocator,
18033  const VkImageCreateInfo* pImageCreateInfo,
18034  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18035  uint32_t* pMemoryTypeIndex)
18036 {
18037  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18038  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18039  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18040  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18041 
18042  const VkDevice hDev = allocator->m_hDevice;
18043  VkImage hImage = VK_NULL_HANDLE;
18044  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18045  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18046  if(res == VK_SUCCESS)
18047  {
18048  VkMemoryRequirements memReq = {};
18049  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18050  hDev, hImage, &memReq);
18051 
18052  res = vmaFindMemoryTypeIndex(
18053  allocator,
18054  memReq.memoryTypeBits,
18055  pAllocationCreateInfo,
18056  pMemoryTypeIndex);
18057 
18058  allocator->GetVulkanFunctions().vkDestroyImage(
18059  hDev, hImage, allocator->GetAllocationCallbacks());
18060  }
18061  return res;
18062 }
18063 
18064 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18065  VmaAllocator allocator,
18066  const VmaPoolCreateInfo* pCreateInfo,
18067  VmaPool* pPool)
18068 {
18069  VMA_ASSERT(allocator && pCreateInfo && pPool);
18070 
18071  VMA_DEBUG_LOG("vmaCreatePool");
18072 
18073  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18074 
18075  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18076 
18077 #if VMA_RECORDING_ENABLED
18078  if(allocator->GetRecorder() != VMA_NULL)
18079  {
18080  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18081  }
18082 #endif
18083 
18084  return res;
18085 }
18086 
18087 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18088  VmaAllocator allocator,
18089  VmaPool pool)
18090 {
18091  VMA_ASSERT(allocator);
18092 
18093  if(pool == VK_NULL_HANDLE)
18094  {
18095  return;
18096  }
18097 
18098  VMA_DEBUG_LOG("vmaDestroyPool");
18099 
18100  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18101 
18102 #if VMA_RECORDING_ENABLED
18103  if(allocator->GetRecorder() != VMA_NULL)
18104  {
18105  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18106  }
18107 #endif
18108 
18109  allocator->DestroyPool(pool);
18110 }
18111 
18112 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18113  VmaAllocator allocator,
18114  VmaPool pool,
18115  VmaPoolStats* pPoolStats)
18116 {
18117  VMA_ASSERT(allocator && pool && pPoolStats);
18118 
18119  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18120 
18121  allocator->GetPoolStats(pool, pPoolStats);
18122 }
18123 
18124 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18125  VmaAllocator allocator,
18126  VmaPool pool,
18127  size_t* pLostAllocationCount)
18128 {
18129  VMA_ASSERT(allocator && pool);
18130 
18131  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18132 
18133 #if VMA_RECORDING_ENABLED
18134  if(allocator->GetRecorder() != VMA_NULL)
18135  {
18136  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18137  }
18138 #endif
18139 
18140  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18141 }
18142 
18143 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18144 {
18145  VMA_ASSERT(allocator && pool);
18146 
18147  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18148 
18149  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18150 
18151  return allocator->CheckPoolCorruption(pool);
18152 }
18153 
18154 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18155  VmaAllocator allocator,
18156  VmaPool pool,
18157  const char** ppName)
18158 {
18159  VMA_ASSERT(allocator && pool && ppName);
18160 
18161  VMA_DEBUG_LOG("vmaGetPoolName");
18162 
18163  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18164 
18165  *ppName = pool->GetName();
18166 }
18167 
18168 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18169  VmaAllocator allocator,
18170  VmaPool pool,
18171  const char* pName)
18172 {
18173  VMA_ASSERT(allocator && pool);
18174 
18175  VMA_DEBUG_LOG("vmaSetPoolName");
18176 
18177  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18178 
18179  pool->SetName(pName);
18180 
18181 #if VMA_RECORDING_ENABLED
18182  if(allocator->GetRecorder() != VMA_NULL)
18183  {
18184  allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18185  }
18186 #endif
18187 }
18188 
18189 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18190  VmaAllocator allocator,
18191  const VkMemoryRequirements* pVkMemoryRequirements,
18192  const VmaAllocationCreateInfo* pCreateInfo,
18193  VmaAllocation* pAllocation,
18194  VmaAllocationInfo* pAllocationInfo)
18195 {
18196  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18197 
18198  VMA_DEBUG_LOG("vmaAllocateMemory");
18199 
18200  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18201 
18202  VkResult result = allocator->AllocateMemory(
18203  *pVkMemoryRequirements,
18204  false, // requiresDedicatedAllocation
18205  false, // prefersDedicatedAllocation
18206  VK_NULL_HANDLE, // dedicatedBuffer
18207  UINT32_MAX, // dedicatedBufferUsage
18208  VK_NULL_HANDLE, // dedicatedImage
18209  *pCreateInfo,
18210  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18211  1, // allocationCount
18212  pAllocation);
18213 
18214 #if VMA_RECORDING_ENABLED
18215  if(allocator->GetRecorder() != VMA_NULL)
18216  {
18217  allocator->GetRecorder()->RecordAllocateMemory(
18218  allocator->GetCurrentFrameIndex(),
18219  *pVkMemoryRequirements,
18220  *pCreateInfo,
18221  *pAllocation);
18222  }
18223 #endif
18224 
18225  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18226  {
18227  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18228  }
18229 
18230  return result;
18231 }
18232 
18233 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18234  VmaAllocator allocator,
18235  const VkMemoryRequirements* pVkMemoryRequirements,
18236  const VmaAllocationCreateInfo* pCreateInfo,
18237  size_t allocationCount,
18238  VmaAllocation* pAllocations,
18239  VmaAllocationInfo* pAllocationInfo)
18240 {
18241  if(allocationCount == 0)
18242  {
18243  return VK_SUCCESS;
18244  }
18245 
18246  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18247 
18248  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18249 
18250  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18251 
18252  VkResult result = allocator->AllocateMemory(
18253  *pVkMemoryRequirements,
18254  false, // requiresDedicatedAllocation
18255  false, // prefersDedicatedAllocation
18256  VK_NULL_HANDLE, // dedicatedBuffer
18257  UINT32_MAX, // dedicatedBufferUsage
18258  VK_NULL_HANDLE, // dedicatedImage
18259  *pCreateInfo,
18260  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18261  allocationCount,
18262  pAllocations);
18263 
18264 #if VMA_RECORDING_ENABLED
18265  if(allocator->GetRecorder() != VMA_NULL)
18266  {
18267  allocator->GetRecorder()->RecordAllocateMemoryPages(
18268  allocator->GetCurrentFrameIndex(),
18269  *pVkMemoryRequirements,
18270  *pCreateInfo,
18271  (uint64_t)allocationCount,
18272  pAllocations);
18273  }
18274 #endif
18275 
18276  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18277  {
18278  for(size_t i = 0; i < allocationCount; ++i)
18279  {
18280  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18281  }
18282  }
18283 
18284  return result;
18285 }
18286 
18287 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18288  VmaAllocator allocator,
18289  VkBuffer buffer,
18290  const VmaAllocationCreateInfo* pCreateInfo,
18291  VmaAllocation* pAllocation,
18292  VmaAllocationInfo* pAllocationInfo)
18293 {
18294  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18295 
18296  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18297 
18298  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18299 
18300  VkMemoryRequirements vkMemReq = {};
18301  bool requiresDedicatedAllocation = false;
18302  bool prefersDedicatedAllocation = false;
18303  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18304  requiresDedicatedAllocation,
18305  prefersDedicatedAllocation);
18306 
18307  VkResult result = allocator->AllocateMemory(
18308  vkMemReq,
18309  requiresDedicatedAllocation,
18310  prefersDedicatedAllocation,
18311  buffer, // dedicatedBuffer
18312  UINT32_MAX, // dedicatedBufferUsage
18313  VK_NULL_HANDLE, // dedicatedImage
18314  *pCreateInfo,
18315  VMA_SUBALLOCATION_TYPE_BUFFER,
18316  1, // allocationCount
18317  pAllocation);
18318 
18319 #if VMA_RECORDING_ENABLED
18320  if(allocator->GetRecorder() != VMA_NULL)
18321  {
18322  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18323  allocator->GetCurrentFrameIndex(),
18324  vkMemReq,
18325  requiresDedicatedAllocation,
18326  prefersDedicatedAllocation,
18327  *pCreateInfo,
18328  *pAllocation);
18329  }
18330 #endif
18331 
18332  if(pAllocationInfo && result == VK_SUCCESS)
18333  {
18334  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18335  }
18336 
18337  return result;
18338 }
18339 
18340 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18341  VmaAllocator allocator,
18342  VkImage image,
18343  const VmaAllocationCreateInfo* pCreateInfo,
18344  VmaAllocation* pAllocation,
18345  VmaAllocationInfo* pAllocationInfo)
18346 {
18347  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18348 
18349  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18350 
18351  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18352 
18353  VkMemoryRequirements vkMemReq = {};
18354  bool requiresDedicatedAllocation = false;
18355  bool prefersDedicatedAllocation = false;
18356  allocator->GetImageMemoryRequirements(image, vkMemReq,
18357  requiresDedicatedAllocation, prefersDedicatedAllocation);
18358 
18359  VkResult result = allocator->AllocateMemory(
18360  vkMemReq,
18361  requiresDedicatedAllocation,
18362  prefersDedicatedAllocation,
18363  VK_NULL_HANDLE, // dedicatedBuffer
18364  UINT32_MAX, // dedicatedBufferUsage
18365  image, // dedicatedImage
18366  *pCreateInfo,
18367  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18368  1, // allocationCount
18369  pAllocation);
18370 
18371 #if VMA_RECORDING_ENABLED
18372  if(allocator->GetRecorder() != VMA_NULL)
18373  {
18374  allocator->GetRecorder()->RecordAllocateMemoryForImage(
18375  allocator->GetCurrentFrameIndex(),
18376  vkMemReq,
18377  requiresDedicatedAllocation,
18378  prefersDedicatedAllocation,
18379  *pCreateInfo,
18380  *pAllocation);
18381  }
18382 #endif
18383 
18384  if(pAllocationInfo && result == VK_SUCCESS)
18385  {
18386  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18387  }
18388 
18389  return result;
18390 }
18391 
18392 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18393  VmaAllocator allocator,
18394  VmaAllocation allocation)
18395 {
18396  VMA_ASSERT(allocator);
18397 
18398  if(allocation == VK_NULL_HANDLE)
18399  {
18400  return;
18401  }
18402 
18403  VMA_DEBUG_LOG("vmaFreeMemory");
18404 
18405  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18406 
18407 #if VMA_RECORDING_ENABLED
18408  if(allocator->GetRecorder() != VMA_NULL)
18409  {
18410  allocator->GetRecorder()->RecordFreeMemory(
18411  allocator->GetCurrentFrameIndex(),
18412  allocation);
18413  }
18414 #endif
18415 
18416  allocator->FreeMemory(
18417  1, // allocationCount
18418  &allocation);
18419 }
18420 
18421 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18422  VmaAllocator allocator,
18423  size_t allocationCount,
18424  const VmaAllocation* pAllocations)
18425 {
18426  if(allocationCount == 0)
18427  {
18428  return;
18429  }
18430 
18431  VMA_ASSERT(allocator);
18432 
18433  VMA_DEBUG_LOG("vmaFreeMemoryPages");
18434 
18435  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18436 
18437 #if VMA_RECORDING_ENABLED
18438  if(allocator->GetRecorder() != VMA_NULL)
18439  {
18440  allocator->GetRecorder()->RecordFreeMemoryPages(
18441  allocator->GetCurrentFrameIndex(),
18442  (uint64_t)allocationCount,
18443  pAllocations);
18444  }
18445 #endif
18446 
18447  allocator->FreeMemory(allocationCount, pAllocations);
18448 }
18449 
18450 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18451  VmaAllocator allocator,
18452  VmaAllocation allocation,
18453  VkDeviceSize newSize)
18454 {
18455  VMA_ASSERT(allocator && allocation);
18456 
18457  VMA_DEBUG_LOG("vmaResizeAllocation");
18458 
18459  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18460 
18461  return allocator->ResizeAllocation(allocation, newSize);
18462 }
18463 
18464 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18465  VmaAllocator allocator,
18466  VmaAllocation allocation,
18467  VmaAllocationInfo* pAllocationInfo)
18468 {
18469  VMA_ASSERT(allocator && allocation && pAllocationInfo);
18470 
18471  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18472 
18473 #if VMA_RECORDING_ENABLED
18474  if(allocator->GetRecorder() != VMA_NULL)
18475  {
18476  allocator->GetRecorder()->RecordGetAllocationInfo(
18477  allocator->GetCurrentFrameIndex(),
18478  allocation);
18479  }
18480 #endif
18481 
18482  allocator->GetAllocationInfo(allocation, pAllocationInfo);
18483 }
18484 
18485 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18486  VmaAllocator allocator,
18487  VmaAllocation allocation)
18488 {
18489  VMA_ASSERT(allocator && allocation);
18490 
18491  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18492 
18493 #if VMA_RECORDING_ENABLED
18494  if(allocator->GetRecorder() != VMA_NULL)
18495  {
18496  allocator->GetRecorder()->RecordTouchAllocation(
18497  allocator->GetCurrentFrameIndex(),
18498  allocation);
18499  }
18500 #endif
18501 
18502  return allocator->TouchAllocation(allocation);
18503 }
18504 
18505 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18506  VmaAllocator allocator,
18507  VmaAllocation allocation,
18508  void* pUserData)
18509 {
18510  VMA_ASSERT(allocator && allocation);
18511 
18512  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18513 
18514  allocation->SetUserData(allocator, pUserData);
18515 
18516 #if VMA_RECORDING_ENABLED
18517  if(allocator->GetRecorder() != VMA_NULL)
18518  {
18519  allocator->GetRecorder()->RecordSetAllocationUserData(
18520  allocator->GetCurrentFrameIndex(),
18521  allocation,
18522  pUserData);
18523  }
18524 #endif
18525 }
18526 
18527 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18528  VmaAllocator allocator,
18529  VmaAllocation* pAllocation)
18530 {
18531  VMA_ASSERT(allocator && pAllocation);
18532 
18533  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18534 
18535  allocator->CreateLostAllocation(pAllocation);
18536 
18537 #if VMA_RECORDING_ENABLED
18538  if(allocator->GetRecorder() != VMA_NULL)
18539  {
18540  allocator->GetRecorder()->RecordCreateLostAllocation(
18541  allocator->GetCurrentFrameIndex(),
18542  *pAllocation);
18543  }
18544 #endif
18545 }
18546 
18547 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18548  VmaAllocator allocator,
18549  VmaAllocation allocation,
18550  void** ppData)
18551 {
18552  VMA_ASSERT(allocator && allocation && ppData);
18553 
18554  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18555 
18556  VkResult res = allocator->Map(allocation, ppData);
18557 
18558 #if VMA_RECORDING_ENABLED
18559  if(allocator->GetRecorder() != VMA_NULL)
18560  {
18561  allocator->GetRecorder()->RecordMapMemory(
18562  allocator->GetCurrentFrameIndex(),
18563  allocation);
18564  }
18565 #endif
18566 
18567  return res;
18568 }
18569 
18570 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18571  VmaAllocator allocator,
18572  VmaAllocation allocation)
18573 {
18574  VMA_ASSERT(allocator && allocation);
18575 
18576  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18577 
18578 #if VMA_RECORDING_ENABLED
18579  if(allocator->GetRecorder() != VMA_NULL)
18580  {
18581  allocator->GetRecorder()->RecordUnmapMemory(
18582  allocator->GetCurrentFrameIndex(),
18583  allocation);
18584  }
18585 #endif
18586 
18587  allocator->Unmap(allocation);
18588 }
18589 
18590 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18591 {
18592  VMA_ASSERT(allocator && allocation);
18593 
18594  VMA_DEBUG_LOG("vmaFlushAllocation");
18595 
18596  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18597 
18598  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18599 
18600 #if VMA_RECORDING_ENABLED
18601  if(allocator->GetRecorder() != VMA_NULL)
18602  {
18603  allocator->GetRecorder()->RecordFlushAllocation(
18604  allocator->GetCurrentFrameIndex(),
18605  allocation, offset, size);
18606  }
18607 #endif
18608 
18609  return res;
18610 }
18611 
18612 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18613 {
18614  VMA_ASSERT(allocator && allocation);
18615 
18616  VMA_DEBUG_LOG("vmaInvalidateAllocation");
18617 
18618  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18619 
18620  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18621 
18622 #if VMA_RECORDING_ENABLED
18623  if(allocator->GetRecorder() != VMA_NULL)
18624  {
18625  allocator->GetRecorder()->RecordInvalidateAllocation(
18626  allocator->GetCurrentFrameIndex(),
18627  allocation, offset, size);
18628  }
18629 #endif
18630 
18631  return res;
18632 }
18633 
18634 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18635  VmaAllocator allocator,
18636  uint32_t allocationCount,
18637  const VmaAllocation* allocations,
18638  const VkDeviceSize* offsets,
18639  const VkDeviceSize* sizes)
18640 {
18641  VMA_ASSERT(allocator);
18642 
18643  if(allocationCount == 0)
18644  {
18645  return VK_SUCCESS;
18646  }
18647 
18648  VMA_ASSERT(allocations);
18649 
18650  VMA_DEBUG_LOG("vmaFlushAllocations");
18651 
18652  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18653 
18654  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18655 
18656 #if VMA_RECORDING_ENABLED
18657  if(allocator->GetRecorder() != VMA_NULL)
18658  {
18659  //TODO
18660  }
18661 #endif
18662 
18663  return res;
18664 }
18665 
18666 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18667  VmaAllocator allocator,
18668  uint32_t allocationCount,
18669  const VmaAllocation* allocations,
18670  const VkDeviceSize* offsets,
18671  const VkDeviceSize* sizes)
18672 {
18673  VMA_ASSERT(allocator);
18674 
18675  if(allocationCount == 0)
18676  {
18677  return VK_SUCCESS;
18678  }
18679 
18680  VMA_ASSERT(allocations);
18681 
18682  VMA_DEBUG_LOG("vmaInvalidateAllocations");
18683 
18684  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18685 
18686  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18687 
18688 #if VMA_RECORDING_ENABLED
18689  if(allocator->GetRecorder() != VMA_NULL)
18690  {
18691  //TODO
18692  }
18693 #endif
18694 
18695  return res;
18696 }
18697 
18698 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
18699 {
18700  VMA_ASSERT(allocator);
18701 
18702  VMA_DEBUG_LOG("vmaCheckCorruption");
18703 
18704  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18705 
18706  return allocator->CheckCorruption(memoryTypeBits);
18707 }
18708 
18709 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
18710  VmaAllocator allocator,
18711  const VmaAllocation* pAllocations,
18712  size_t allocationCount,
18713  VkBool32* pAllocationsChanged,
18714  const VmaDefragmentationInfo *pDefragmentationInfo,
18715  VmaDefragmentationStats* pDefragmentationStats)
18716 {
18717  // Deprecated interface, reimplemented using new one.
18718 
18719  VmaDefragmentationInfo2 info2 = {};
18720  info2.allocationCount = (uint32_t)allocationCount;
18721  info2.pAllocations = pAllocations;
18722  info2.pAllocationsChanged = pAllocationsChanged;
18723  if(pDefragmentationInfo != VMA_NULL)
18724  {
18725  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
18726  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
18727  }
18728  else
18729  {
18730  info2.maxCpuAllocationsToMove = UINT32_MAX;
18731  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
18732  }
18733  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
18734 
18736  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
18737  if(res == VK_NOT_READY)
18738  {
18739  res = vmaDefragmentationEnd( allocator, ctx);
18740  }
18741  return res;
18742 }
18743 
18744 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
18745  VmaAllocator allocator,
18746  const VmaDefragmentationInfo2* pInfo,
18747  VmaDefragmentationStats* pStats,
18748  VmaDefragmentationContext *pContext)
18749 {
18750  VMA_ASSERT(allocator && pInfo && pContext);
18751 
18752  // Degenerate case: Nothing to defragment.
18753  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
18754  {
18755  return VK_SUCCESS;
18756  }
18757 
18758  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
18759  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
18760  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
18761  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
18762 
18763  VMA_DEBUG_LOG("vmaDefragmentationBegin");
18764 
18765  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18766 
18767  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
18768 
18769 #if VMA_RECORDING_ENABLED
18770  if(allocator->GetRecorder() != VMA_NULL)
18771  {
18772  allocator->GetRecorder()->RecordDefragmentationBegin(
18773  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
18774  }
18775 #endif
18776 
18777  return res;
18778 }
18779 
18780 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
18781  VmaAllocator allocator,
18782  VmaDefragmentationContext context)
18783 {
18784  VMA_ASSERT(allocator);
18785 
18786  VMA_DEBUG_LOG("vmaDefragmentationEnd");
18787 
18788  if(context != VK_NULL_HANDLE)
18789  {
18790  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18791 
18792 #if VMA_RECORDING_ENABLED
18793  if(allocator->GetRecorder() != VMA_NULL)
18794  {
18795  allocator->GetRecorder()->RecordDefragmentationEnd(
18796  allocator->GetCurrentFrameIndex(), context);
18797  }
18798 #endif
18799 
18800  return allocator->DefragmentationEnd(context);
18801  }
18802  else
18803  {
18804  return VK_SUCCESS;
18805  }
18806 }
18807 
18808 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
18809  VmaAllocator allocator,
18810  VmaDefragmentationContext context,
18812  )
18813 {
18814  VMA_ASSERT(allocator);
18815  VMA_ASSERT(pInfo);
18816 
18817  VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
18818 
18819  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18820 
18821  if(context == VK_NULL_HANDLE)
18822  {
18823  pInfo->moveCount = 0;
18824  return VK_SUCCESS;
18825  }
18826 
18827  return allocator->DefragmentationPassBegin(pInfo, context);
18828 }
18829 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
18830  VmaAllocator allocator,
18831  VmaDefragmentationContext context)
18832 {
18833  VMA_ASSERT(allocator);
18834 
18835  VMA_DEBUG_LOG("vmaEndDefragmentationPass");
18836  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18837 
18838  if(context == VK_NULL_HANDLE)
18839  return VK_SUCCESS;
18840 
18841  return allocator->DefragmentationPassEnd(context);
18842 }
18843 
18844 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
18845  VmaAllocator allocator,
18846  VmaAllocation allocation,
18847  VkBuffer buffer)
18848 {
18849  VMA_ASSERT(allocator && allocation && buffer);
18850 
18851  VMA_DEBUG_LOG("vmaBindBufferMemory");
18852 
18853  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18854 
18855  return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
18856 }
18857 
18858 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
18859  VmaAllocator allocator,
18860  VmaAllocation allocation,
18861  VkDeviceSize allocationLocalOffset,
18862  VkBuffer buffer,
18863  const void* pNext)
18864 {
18865  VMA_ASSERT(allocator && allocation && buffer);
18866 
18867  VMA_DEBUG_LOG("vmaBindBufferMemory2");
18868 
18869  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18870 
18871  return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
18872 }
18873 
18874 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
18875  VmaAllocator allocator,
18876  VmaAllocation allocation,
18877  VkImage image)
18878 {
18879  VMA_ASSERT(allocator && allocation && image);
18880 
18881  VMA_DEBUG_LOG("vmaBindImageMemory");
18882 
18883  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18884 
18885  return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18886 }
18887 
18888 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18889  VmaAllocator allocator,
18890  VmaAllocation allocation,
18891  VkDeviceSize allocationLocalOffset,
18892  VkImage image,
18893  const void* pNext)
18894 {
18895  VMA_ASSERT(allocator && allocation && image);
18896 
18897  VMA_DEBUG_LOG("vmaBindImageMemory2");
18898 
18899  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18900 
18901  return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18902 }
18903 
18904 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18905  VmaAllocator allocator,
18906  const VkBufferCreateInfo* pBufferCreateInfo,
18907  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18908  VkBuffer* pBuffer,
18909  VmaAllocation* pAllocation,
18910  VmaAllocationInfo* pAllocationInfo)
18911 {
18912  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18913 
18914  if(pBufferCreateInfo->size == 0)
18915  {
18916  return VK_ERROR_VALIDATION_FAILED_EXT;
18917  }
18918  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18919  !allocator->m_UseKhrBufferDeviceAddress)
18920  {
18921  VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18922  return VK_ERROR_VALIDATION_FAILED_EXT;
18923  }
18924 
18925  VMA_DEBUG_LOG("vmaCreateBuffer");
18926 
18927  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18928 
18929  *pBuffer = VK_NULL_HANDLE;
18930  *pAllocation = VK_NULL_HANDLE;
18931 
18932  // 1. Create VkBuffer.
18933  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18934  allocator->m_hDevice,
18935  pBufferCreateInfo,
18936  allocator->GetAllocationCallbacks(),
18937  pBuffer);
18938  if(res >= 0)
18939  {
18940  // 2. vkGetBufferMemoryRequirements.
18941  VkMemoryRequirements vkMemReq = {};
18942  bool requiresDedicatedAllocation = false;
18943  bool prefersDedicatedAllocation = false;
18944  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18945  requiresDedicatedAllocation, prefersDedicatedAllocation);
18946 
18947  // 3. Allocate memory using allocator.
18948  res = allocator->AllocateMemory(
18949  vkMemReq,
18950  requiresDedicatedAllocation,
18951  prefersDedicatedAllocation,
18952  *pBuffer, // dedicatedBuffer
18953  pBufferCreateInfo->usage, // dedicatedBufferUsage
18954  VK_NULL_HANDLE, // dedicatedImage
18955  *pAllocationCreateInfo,
18956  VMA_SUBALLOCATION_TYPE_BUFFER,
18957  1, // allocationCount
18958  pAllocation);
18959 
18960 #if VMA_RECORDING_ENABLED
18961  if(allocator->GetRecorder() != VMA_NULL)
18962  {
18963  allocator->GetRecorder()->RecordCreateBuffer(
18964  allocator->GetCurrentFrameIndex(),
18965  *pBufferCreateInfo,
18966  *pAllocationCreateInfo,
18967  *pAllocation);
18968  }
18969 #endif
18970 
18971  if(res >= 0)
18972  {
18973  // 3. Bind buffer with memory.
18974  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18975  {
18976  res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18977  }
18978  if(res >= 0)
18979  {
18980  // All steps succeeded.
18981  #if VMA_STATS_STRING_ENABLED
18982  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18983  #endif
18984  if(pAllocationInfo != VMA_NULL)
18985  {
18986  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18987  }
18988 
18989  return VK_SUCCESS;
18990  }
18991  allocator->FreeMemory(
18992  1, // allocationCount
18993  pAllocation);
18994  *pAllocation = VK_NULL_HANDLE;
18995  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18996  *pBuffer = VK_NULL_HANDLE;
18997  return res;
18998  }
18999  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19000  *pBuffer = VK_NULL_HANDLE;
19001  return res;
19002  }
19003  return res;
19004 }
19005 
19006 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19007  VmaAllocator allocator,
19008  VkBuffer buffer,
19009  VmaAllocation allocation)
19010 {
19011  VMA_ASSERT(allocator);
19012 
19013  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19014  {
19015  return;
19016  }
19017 
19018  VMA_DEBUG_LOG("vmaDestroyBuffer");
19019 
19020  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19021 
19022 #if VMA_RECORDING_ENABLED
19023  if(allocator->GetRecorder() != VMA_NULL)
19024  {
19025  allocator->GetRecorder()->RecordDestroyBuffer(
19026  allocator->GetCurrentFrameIndex(),
19027  allocation);
19028  }
19029 #endif
19030 
19031  if(buffer != VK_NULL_HANDLE)
19032  {
19033  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19034  }
19035 
19036  if(allocation != VK_NULL_HANDLE)
19037  {
19038  allocator->FreeMemory(
19039  1, // allocationCount
19040  &allocation);
19041  }
19042 }
19043 
19044 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19045  VmaAllocator allocator,
19046  const VkImageCreateInfo* pImageCreateInfo,
19047  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19048  VkImage* pImage,
19049  VmaAllocation* pAllocation,
19050  VmaAllocationInfo* pAllocationInfo)
19051 {
19052  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19053 
19054  if(pImageCreateInfo->extent.width == 0 ||
19055  pImageCreateInfo->extent.height == 0 ||
19056  pImageCreateInfo->extent.depth == 0 ||
19057  pImageCreateInfo->mipLevels == 0 ||
19058  pImageCreateInfo->arrayLayers == 0)
19059  {
19060  return VK_ERROR_VALIDATION_FAILED_EXT;
19061  }
19062 
19063  VMA_DEBUG_LOG("vmaCreateImage");
19064 
19065  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19066 
19067  *pImage = VK_NULL_HANDLE;
19068  *pAllocation = VK_NULL_HANDLE;
19069 
19070  // 1. Create VkImage.
19071  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19072  allocator->m_hDevice,
19073  pImageCreateInfo,
19074  allocator->GetAllocationCallbacks(),
19075  pImage);
19076  if(res >= 0)
19077  {
19078  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19079  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19080  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19081 
19082  // 2. Allocate memory using allocator.
19083  VkMemoryRequirements vkMemReq = {};
19084  bool requiresDedicatedAllocation = false;
19085  bool prefersDedicatedAllocation = false;
19086  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19087  requiresDedicatedAllocation, prefersDedicatedAllocation);
19088 
19089  res = allocator->AllocateMemory(
19090  vkMemReq,
19091  requiresDedicatedAllocation,
19092  prefersDedicatedAllocation,
19093  VK_NULL_HANDLE, // dedicatedBuffer
19094  UINT32_MAX, // dedicatedBufferUsage
19095  *pImage, // dedicatedImage
19096  *pAllocationCreateInfo,
19097  suballocType,
19098  1, // allocationCount
19099  pAllocation);
19100 
19101 #if VMA_RECORDING_ENABLED
19102  if(allocator->GetRecorder() != VMA_NULL)
19103  {
19104  allocator->GetRecorder()->RecordCreateImage(
19105  allocator->GetCurrentFrameIndex(),
19106  *pImageCreateInfo,
19107  *pAllocationCreateInfo,
19108  *pAllocation);
19109  }
19110 #endif
19111 
19112  if(res >= 0)
19113  {
19114  // 3. Bind image with memory.
19115  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19116  {
19117  res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19118  }
19119  if(res >= 0)
19120  {
19121  // All steps succeeded.
19122  #if VMA_STATS_STRING_ENABLED
19123  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19124  #endif
19125  if(pAllocationInfo != VMA_NULL)
19126  {
19127  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19128  }
19129 
19130  return VK_SUCCESS;
19131  }
19132  allocator->FreeMemory(
19133  1, // allocationCount
19134  pAllocation);
19135  *pAllocation = VK_NULL_HANDLE;
19136  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19137  *pImage = VK_NULL_HANDLE;
19138  return res;
19139  }
19140  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19141  *pImage = VK_NULL_HANDLE;
19142  return res;
19143  }
19144  return res;
19145 }
19146 
19147 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19148  VmaAllocator allocator,
19149  VkImage image,
19150  VmaAllocation allocation)
19151 {
19152  VMA_ASSERT(allocator);
19153 
19154  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19155  {
19156  return;
19157  }
19158 
19159  VMA_DEBUG_LOG("vmaDestroyImage");
19160 
19161  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19162 
19163 #if VMA_RECORDING_ENABLED
19164  if(allocator->GetRecorder() != VMA_NULL)
19165  {
19166  allocator->GetRecorder()->RecordDestroyImage(
19167  allocator->GetCurrentFrameIndex(),
19168  allocation);
19169  }
19170 #endif
19171 
19172  if(image != VK_NULL_HANDLE)
19173  {
19174  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19175  }
19176  if(allocation != VK_NULL_HANDLE)
19177  {
19178  allocator->FreeMemory(
19179  1, // allocationCount
19180  &allocation);
19181  }
19182 }
19183 
19184 #endif // #ifdef VMA_IMPLEMENTATION
VmaStats
struct VmaStats VmaStats
General statistics from current state of Allocator.
VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
Definition: vk_mem_alloc.h:2365
VmaVulkanFunctions::vkAllocateMemory
PFN_vkAllocateMemory vkAllocateMemory
Definition: vk_mem_alloc.h:2322
VmaDeviceMemoryCallbacks::pfnFree
PFN_vmaFreeDeviceMemoryFunction pfnFree
Optional, can be null.
Definition: vk_mem_alloc.h:2210
VMA_RECORD_FLAG_BITS_MAX_ENUM
@ VMA_RECORD_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2359
VmaVulkanFunctions::vkGetPhysicalDeviceProperties
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties
Definition: vk_mem_alloc.h:2320
VmaAllocatorCreateInfo::physicalDevice
VkPhysicalDevice physicalDevice
Vulkan physical device.
Definition: vk_mem_alloc.h:2385
VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
@ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
Enables alternative, linear allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2982
VmaDefragmentationInfo2::allocationCount
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:3595
VmaAllocatorCreateInfo::frameInUseCount
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:2411
VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
Definition: vk_mem_alloc.h:2273
VmaBudget
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
Definition: vk_mem_alloc.h:2584
VmaDefragmentationPassMoveInfo::memory
VkDeviceMemory memory
Definition: vk_mem_alloc.h:3663
VmaDefragmentationInfo2::pPools
const VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:3629
VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
@ VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
Definition: vk_mem_alloc.h:2728
VmaDefragmentationInfo
struct VmaDefragmentationInfo VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
VmaPoolStats
Describes parameter of existing VmaPool.
Definition: vk_mem_alloc.h:3054
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
Definition: vk_mem_alloc.h:2811
VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
@ VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
Allocator and all objects created from it will not be synchronized internally, so you must guarantee ...
Definition: vk_mem_alloc.h:2221
VmaPoolStats::unusedSize
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3060
VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
@ VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
Definition: vk_mem_alloc.h:2791
VmaRecordFlagBits
VmaRecordFlagBits
Flags to be used in VmaRecordSettings::flags.
Definition: vk_mem_alloc.h:2351
vmaSetPoolName
void vmaSetPoolName(VmaAllocator allocator, VmaPool pool, const char *pName)
Sets name of a custom pool.
VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
Definition: vk_mem_alloc.h:2206
vmaTouchAllocation
VkBool32 vmaTouchAllocation(VmaAllocator allocator, VmaAllocation allocation)
Returns VK_TRUE if allocation is not lost and atomically marks it as used in current frame.
VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
@ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
Definition: vk_mem_alloc.h:2778
VmaAllocatorCreateInfo::preferredLargeHeapBlockSize
VkDeviceSize preferredLargeHeapBlockSize
Preferred size of a single VkDeviceMemory block to be allocated from large heaps > 1 GiB....
Definition: vk_mem_alloc.h:2391
VMA_RECORD_FLUSH_AFTER_CALL_BIT
@ VMA_RECORD_FLUSH_AFTER_CALL_BIT
Enables flush after recording every function call.
Definition: vk_mem_alloc.h:2357
VmaAllocationCreateInfo
struct VmaAllocationCreateInfo VmaAllocationCreateInfo
vmaResizeAllocation
VkResult vmaResizeAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize newSize)
Deprecated.
VmaVulkanFunctions::vkUnmapMemory
PFN_vkUnmapMemory vkUnmapMemory
Definition: vk_mem_alloc.h:2325
VmaAllocationInfo::deviceMemory
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:3197
VmaStatInfo::unusedRangeCount
uint32_t unusedRangeCount
Number of free ranges of memory between allocations.
Definition: vk_mem_alloc.h:2551
VmaAllocationCreateInfo::pUserData
void * pUserData
Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
Definition: vk_mem_alloc.h:2885
VmaStatInfo::unusedRangeSizeMax
VkDeviceSize unusedRangeSizeMax
Definition: vk_mem_alloc.h:2557
VmaVulkanFunctions::vkMapMemory
PFN_vkMapMemory vkMapMemory
Definition: vk_mem_alloc.h:2324
VMA_RECORDING_ENABLED
#define VMA_RECORDING_ENABLED
Definition: vk_mem_alloc.h:2022
VmaDefragmentationPassMoveInfo::offset
VkDeviceSize offset
Definition: vk_mem_alloc.h:3664
VmaDefragmentationPassInfo::pMoves
VmaDefragmentationPassMoveInfo * pMoves
Definition: vk_mem_alloc.h:3673
VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
Definition: vk_mem_alloc.h:2822
vmaUnmapMemory
void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
VmaAllocatorInfo::instance
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2483
VmaBudget::usage
VkDeviceSize usage
Estimated current memory usage of the program, in bytes.
Definition: vk_mem_alloc.h:2608
VmaAllocator
Represents main object of this library initialized.
VmaVulkanFunctions::vkCmdCopyBuffer
PFN_vkCmdCopyBuffer vkCmdCopyBuffer
Definition: vk_mem_alloc.h:2336
VmaAllocatorCreateInfo
Description of a Allocator to be created.
Definition: vk_mem_alloc.h:2380
VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT
@ VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT
Set this flag to only try to allocate from existing VkDeviceMemory blocks and never create new such b...
Definition: vk_mem_alloc.h:2752
VmaAllocatorInfo::device
VkDevice device
Handle to Vulkan device object.
Definition: vk_mem_alloc.h:2493
VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
@ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3581
VmaPoolStats::unusedRangeSizeMax
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:3073
VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
Definition: vk_mem_alloc.h:2815
VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
@ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
Enables usage of VK_KHR_dedicated_allocation extension.
Definition: vk_mem_alloc.h:2246
vmaSetCurrentFrameIndex
void vmaSetCurrentFrameIndex(VmaAllocator allocator, uint32_t frameIndex)
Sets index of the current frame.
VmaDefragmentationInfo::maxAllocationsToMove
uint32_t maxAllocationsToMove
Maximum number of allocations that can be moved to different place.
Definition: vk_mem_alloc.h:3690
VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
@ VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
Definition: vk_mem_alloc.h:2806
VmaMemoryUsage
VmaMemoryUsage
Definition: vk_mem_alloc.h:2667
vmaFreeMemoryPages
void vmaFreeMemoryPages(VmaAllocator allocator, size_t allocationCount, const VmaAllocation *pAllocations)
Frees memory and destroys multiple allocations.
vmaGetMemoryTypeProperties
void vmaGetMemoryTypeProperties(VmaAllocator allocator, uint32_t memoryTypeIndex, VkMemoryPropertyFlags *pFlags)
Given Memory Type Index, returns Property Flags of this memory type.
VmaStatInfo::blockCount
uint32_t blockCount
Number of VkDeviceMemory Vulkan memory blocks allocated.
Definition: vk_mem_alloc.h:2547
VmaPoolCreateInfo::memoryTypeIndex
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition: vk_mem_alloc.h:3010
VmaPoolCreateInfo::blockSize
VkDeviceSize blockSize
Size of a single VkDeviceMemory block to be allocated as part of this pool, in bytes....
Definition: vk_mem_alloc.h:3022
VmaDefragmentationInfo2::poolCount
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:3613
VmaDefragmentationPassMoveInfo
Definition: vk_mem_alloc.h:3661
vmaBuildStatsString
void vmaBuildStatsString(VmaAllocator allocator, char **ppStatsString, VkBool32 detailedMap)
Builds and returns statistics as string in JSON format.
vmaGetAllocationInfo
void vmaGetAllocationInfo(VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
Returns current information about specified allocation and atomically marks it as used in current fra...
VmaPoolStats::allocationCount
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost.
Definition: vk_mem_alloc.h:3063
VmaAllocatorCreateFlags
VkFlags VmaAllocatorCreateFlags
Definition: vk_mem_alloc.h:2313
vmaFreeStatsString
void vmaFreeStatsString(VmaAllocator allocator, char *pStatsString)
vmaAllocateMemoryForBuffer
VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
VmaVulkanFunctions
struct VmaVulkanFunctions VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2311
VmaDefragmentationFlagBits
VmaDefragmentationFlagBits
Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
Definition: vk_mem_alloc.h:3579
VmaAllocationInfo::offset
VkDeviceSize offset
Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory,...
Definition: vk_mem_alloc.h:3202
VmaAllocationCreateFlagBits
VmaAllocationCreateFlagBits
Flags to be passed as VmaAllocationCreateInfo::flags.
Definition: vk_mem_alloc.h:2734
VmaVulkanFunctions::vkGetPhysicalDeviceMemoryProperties
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties
Definition: vk_mem_alloc.h:2321
VmaPoolCreateFlags
VkFlags VmaPoolCreateFlags
Definition: vk_mem_alloc.h:3003
vmaCreateLostAllocation
void vmaCreateLostAllocation(VmaAllocator allocator, VmaAllocation *pAllocation)
Creates new allocation that is in lost state from the beginning.
vmaInvalidateAllocations
VkResult vmaInvalidateAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Invalidates memory of given set of allocations.
VmaDeviceMemoryCallbacks
struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
vmaGetPhysicalDeviceProperties
void vmaGetPhysicalDeviceProperties(VmaAllocator allocator, const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
VmaAllocationCreateInfo::pool
VmaPool pool
Pool that this allocation should be created in.
Definition: vk_mem_alloc.h:2878
vmaGetMemoryProperties
void vmaGetMemoryProperties(VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
VmaStats::total
VmaStatInfo total
Definition: vk_mem_alloc.h:2565
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
@ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2741
vmaDefragmentationEnd
VkResult vmaDefragmentationEnd(VmaAllocator allocator, VmaDefragmentationContext context)
Ends defragmentation process.
VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
@ VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
Definition: vk_mem_alloc.h:2261
VmaDefragmentationInfo2::flags
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:3592
VmaVulkanFunctions::vkBindImageMemory
PFN_vkBindImageMemory vkBindImageMemory
Definition: vk_mem_alloc.h:2329
VmaDefragmentationInfo2::maxGpuBytesToMove
VkDeviceSize maxGpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3644
VmaDefragmentationStats
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:3694
vmaDestroyPool
void vmaDestroyPool(VmaAllocator allocator, VmaPool pool)
Destroys VmaPool object and frees Vulkan device memory.
VmaPoolStats::size
VkDeviceSize size
Total amount of VkDeviceMemory allocated from Vulkan for this pool, in bytes.
Definition: vk_mem_alloc.h:3057
VmaVulkanFunctions::vkFreeMemory
PFN_vkFreeMemory vkFreeMemory
Definition: vk_mem_alloc.h:2323
VmaRecordFlags
VkFlags VmaRecordFlags
Definition: vk_mem_alloc.h:2361
vmaFlushAllocation
VkResult vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Flushes memory of given allocation.
VMA_MEMORY_USAGE_CPU_ONLY
@ VMA_MEMORY_USAGE_CPU_ONLY
Definition: vk_mem_alloc.h:2698
VmaAllocation
Represents single memory allocation.
VMA_MEMORY_USAGE_CPU_COPY
@ VMA_MEMORY_USAGE_CPU_COPY
Definition: vk_mem_alloc.h:2720
vmaSetAllocationUserData
void vmaSetAllocationUserData(VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
Sets pUserData in given allocation to new value.
VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
@ VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
Definition: vk_mem_alloc.h:3580
VmaAllocatorCreateInfo::pRecordSettings
const VmaRecordSettings * pRecordSettings
Parameters for recording of VMA calls. Can be null.
Definition: vk_mem_alloc.h:2449
VmaVulkanFunctions::vkBindBufferMemory
PFN_vkBindBufferMemory vkBindBufferMemory
Definition: vk_mem_alloc.h:2328
VmaVulkanFunctions::vkGetBufferMemoryRequirements
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements
Definition: vk_mem_alloc.h:2330
VmaDefragmentationInfo2::commandBuffer
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:3658
VmaStats
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:2562
VmaPoolCreateInfo::minBlockCount
size_t minBlockCount
Minimum number of blocks to be always allocated in this pool, even if they stay empty.
Definition: vk_mem_alloc.h:3027
VmaAllocatorCreateInfo::vulkanApiVersion
uint32_t vulkanApiVersion
Optional. The highest version of Vulkan that the application is designed to use.
Definition: vk_mem_alloc.h:2463
VmaStatInfo
Calculated statistics of memory usage in entire allocator.
Definition: vk_mem_alloc.h:2545
VmaDefragmentationStats::bytesFreed
VkDeviceSize bytesFreed
Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects.
Definition: vk_mem_alloc.h:3698
vmaDefragment
VkResult vmaDefragment(VmaAllocator allocator, const VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
Deprecated. Compacts memory by moving allocations.
VmaDefragmentationPassInfo::moveCount
uint32_t moveCount
Definition: vk_mem_alloc.h:3672
VMA_MEMORY_USAGE_GPU_ONLY
@ VMA_MEMORY_USAGE_GPU_ONLY
Definition: vk_mem_alloc.h:2688
vmaBeginDefragmentationPass
VkResult vmaBeginDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationPassInfo *pInfo)
vmaFindMemoryTypeIndex
VkResult vmaFindMemoryTypeIndex(VmaAllocator allocator, uint32_t memoryTypeBits, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
vmaFlushAllocations
VkResult vmaFlushAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Flushes memory of given set of allocations.
vmaCreatePool
VkResult vmaCreatePool(VmaAllocator allocator, const VmaPoolCreateInfo *pCreateInfo, VmaPool *pPool)
Allocates Vulkan device memory and creates VmaPool object.
VmaStatInfo::unusedBytes
VkDeviceSize unusedBytes
Total number of bytes occupied by unused ranges.
Definition: vk_mem_alloc.h:2555
VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
@ VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
Definition: vk_mem_alloc.h:2309
vmaAllocateMemoryPages
VkResult vmaAllocateMemoryPages(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaAllocationCreateInfo *pCreateInfo, size_t allocationCount, VmaAllocation *pAllocations, VmaAllocationInfo *pAllocationInfo)
General purpose memory allocation for multiple allocation objects at once.
VmaStatInfo::usedBytes
VkDeviceSize usedBytes
Total number of bytes occupied by all allocations.
Definition: vk_mem_alloc.h:2553
VmaAllocatorCreateInfo::pAllocationCallbacks
const VkAllocationCallbacks * pAllocationCallbacks
Custom CPU memory allocation callbacks. Optional.
Definition: vk_mem_alloc.h:2394
VmaAllocatorCreateFlagBits
VmaAllocatorCreateFlagBits
Flags for created VmaAllocator.
Definition: vk_mem_alloc.h:2216
vmaAllocateMemoryForImage
VkResult vmaAllocateMemoryForImage(VmaAllocator allocator, VkImage image, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaAllocateMemoryForBuffer().
VmaPoolCreateInfo::maxBlockCount
size_t maxBlockCount
Maximum number of blocks that can be allocated in this pool. Optional.
Definition: vk_mem_alloc.h:3035
VmaPoolCreateInfo
Describes parameter of created VmaPool.
Definition: vk_mem_alloc.h:3007
VmaDeviceMemoryCallbacks::pfnAllocate
PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
Optional, can be null.
Definition: vk_mem_alloc.h:2208
VmaPool
Represents custom memory pool.
VMA_MEMORY_USAGE_GPU_TO_CPU
@ VMA_MEMORY_USAGE_GPU_TO_CPU
Definition: vk_mem_alloc.h:2714
VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
@ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
Definition: vk_mem_alloc.h:2785
VmaPoolCreateInfo::flags
VmaPoolCreateFlags flags
Use combination of VmaPoolCreateFlagBits.
Definition: vk_mem_alloc.h:3013
VMA_MEMORY_USAGE_MAX_ENUM
@ VMA_MEMORY_USAGE_MAX_ENUM
Definition: vk_mem_alloc.h:2730
VmaStatInfo::allocationCount
uint32_t allocationCount
Number of VmaAllocation allocation objects allocated.
Definition: vk_mem_alloc.h:2549
VmaVulkanFunctions::vkInvalidateMappedMemoryRanges
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges
Definition: vk_mem_alloc.h:2327
vmaAllocateMemory
VkResult vmaAllocateMemory(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
General purpose memory allocation.
VmaDefragmentationInfo2
Parameters for defragmentation.
Definition: vk_mem_alloc.h:3589
VmaDefragmentationInfo::maxBytesToMove
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3685
VmaBudget::blockBytes
VkDeviceSize blockBytes
Sum size of all VkDeviceMemory blocks allocated from particular heap, in bytes.
Definition: vk_mem_alloc.h:2587
VmaAllocatorInfo
Information about existing VmaAllocator object.
Definition: vk_mem_alloc.h:2478
VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3001
VmaAllocationCreateInfo::requiredFlags
VkMemoryPropertyFlags requiredFlags
Flags that must be set in a Memory Type chosen for an allocation.
Definition: vk_mem_alloc.h:2859
VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
Definition: vk_mem_alloc.h:2832
VmaStatInfo
struct VmaStatInfo VmaStatInfo
Calculated statistics of memory usage in entire allocator.
VmaStatInfo::allocationSizeAvg
VkDeviceSize allocationSizeAvg
Definition: vk_mem_alloc.h:2556
vmaDestroyAllocator
void vmaDestroyAllocator(VmaAllocator allocator)
Destroys allocator object.
VmaAllocatorCreateInfo::pDeviceMemoryCallbacks
const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
Informative callbacks for vkAllocateMemory, vkFreeMemory. Optional.
Definition: vk_mem_alloc.h:2397
VMA_ALLOCATION_CREATE_STRATEGY_MASK
@ VMA_ALLOCATION_CREATE_STRATEGY_MASK
Definition: vk_mem_alloc.h:2836
VmaAllocatorCreateInfo::device
VkDevice device
Vulkan device.
Definition: vk_mem_alloc.h:2388
vmaFindMemoryTypeIndexForImageInfo
VkResult vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
vmaMapMemory
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void **ppData)
Maps memory represented by given allocation and returns pointer to it.
vmaBindBufferMemory
VkResult vmaBindBufferMemory(VmaAllocator allocator, VmaAllocation allocation, VkBuffer buffer)
Binds buffer to allocation.
VmaAllocatorCreateInfo::pHeapSizeLimit
const VkDeviceSize * pHeapSizeLimit
Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out o...
Definition: vk_mem_alloc.h:2436
VmaDefragmentationPassMoveInfo::allocation
VmaAllocation allocation
Definition: vk_mem_alloc.h:3662
vmaCreateImage
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaCreateBuffer().
vmaFindMemoryTypeIndexForBufferInfo
VkResult vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
VmaBudget::budget
VkDeviceSize budget
Estimated amount of memory available to the program, in bytes.
Definition: vk_mem_alloc.h:2619
VmaPoolStats
struct VmaPoolStats VmaPoolStats
Describes parameter of existing VmaPool.
VmaDefragmentationPassInfo
struct VmaDefragmentationPassInfo VmaDefragmentationPassInfo
Parameters for incremental defragmentation steps.
VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:2319
VmaAllocationInfo::pMappedData
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:3222
VmaAllocatorCreateInfo::flags
VmaAllocatorCreateFlags flags
Flags for created allocator. Use VmaAllocatorCreateFlagBits enum.
Definition: vk_mem_alloc.h:2382
VmaDefragmentationFlags
VkFlags VmaDefragmentationFlags
Definition: vk_mem_alloc.h:3583
VmaDefragmentationInfo2::pAllocations
const VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:3604
vmaGetPoolStats
void vmaGetPoolStats(VmaAllocator allocator, VmaPool pool, VmaPoolStats *pPoolStats)
Retrieves statistics of existing VmaPool object.
VmaVulkanFunctions::vkCreateImage
PFN_vkCreateImage vkCreateImage
Definition: vk_mem_alloc.h:2334
VmaDeviceMemoryCallbacks::pUserData
void * pUserData
Optional, can be null.
Definition: vk_mem_alloc.h:2212
VmaRecordSettings
struct VmaRecordSettings VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
VmaStatInfo::unusedRangeSizeAvg
VkDeviceSize unusedRangeSizeAvg
Definition: vk_mem_alloc.h:2557
VMA_MEMORY_USAGE_CPU_TO_GPU
@ VMA_MEMORY_USAGE_CPU_TO_GPU
Definition: vk_mem_alloc.h:2705
VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
Definition: vk_mem_alloc.h:2829
VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
Definition: vk_mem_alloc.h:2826
VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
@ VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
Definition: vk_mem_alloc.h:2291
VmaDefragmentationStats
struct VmaDefragmentationStats VmaDefragmentationStats
Statistics returned by function vmaDefragment().
VmaAllocationCreateInfo::usage
VmaMemoryUsage usage
Intended usage of memory.
Definition: vk_mem_alloc.h:2854
VmaStatInfo::allocationSizeMin
VkDeviceSize allocationSizeMin
Definition: vk_mem_alloc.h:2556
vmaBindBufferMemory2
VkResult vmaBindBufferMemory2(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize allocationLocalOffset, VkBuffer buffer, const void *pNext)
Binds buffer to allocation with additional parameters.
VmaAllocationInfo::size
VkDeviceSize size
Size of this allocation, in bytes.
Definition: vk_mem_alloc.h:3213
VmaRecordSettings::flags
VmaRecordFlags flags
Flags for recording. Use VmaRecordFlagBits enum.
Definition: vk_mem_alloc.h:2367
VmaVulkanFunctions::vkFlushMappedMemoryRanges
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges
Definition: vk_mem_alloc.h:2326
VmaAllocationInfo::pUserData
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:3227
vmaMakePoolAllocationsLost
void vmaMakePoolAllocationsLost(VmaAllocator allocator, VmaPool pool, size_t *pLostAllocationCount)
Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInf...
VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT
@ VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT
Use this flag if you always allocate only buffers and linear images or only optimal images out of thi...
Definition: vk_mem_alloc.h:2965
vmaInvalidateAllocation
VkResult vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Invalidates memory of given allocation.
vmaCreateBuffer
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
VmaStats::memoryHeap
VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
Definition: vk_mem_alloc.h:2564
VmaAllocatorCreateInfo::pVulkanFunctions
const VmaVulkanFunctions * pVulkanFunctions
Pointers to Vulkan functions. Can be null.
Definition: vk_mem_alloc.h:2442
VmaPoolStats::blockCount
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:3076
vmaCreateAllocator
VkResult vmaCreateAllocator(const VmaAllocatorCreateInfo *pCreateInfo, VmaAllocator *pAllocator)
Creates Allocator object.
vmaCheckCorruption
VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
Checks magic number in margins around all allocations in given memory types (in both default and cust...
VmaDefragmentationPassInfo
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:3671
VmaStats::memoryType
VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
Definition: vk_mem_alloc.h:2563
VmaAllocationCreateFlags
VkFlags VmaAllocationCreateFlags
Definition: vk_mem_alloc.h:2843
VmaAllocatorCreateInfo::instance
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2454
VMA_MEMORY_USAGE_UNKNOWN
@ VMA_MEMORY_USAGE_UNKNOWN
Definition: vk_mem_alloc.h:2671
VmaDefragmentationInfo2::maxGpuAllocationsToMove
uint32_t maxGpuAllocationsToMove
Maximum number of allocations that can be moved to a different place using transfers on GPU side,...
Definition: vk_mem_alloc.h:3649
VmaVulkanFunctions::vkDestroyBuffer
PFN_vkDestroyBuffer vkDestroyBuffer
Definition: vk_mem_alloc.h:2333
VmaPoolCreateInfo::frameInUseCount
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:3049
VmaVulkanFunctions::vkDestroyImage
PFN_vkDestroyImage vkDestroyImage
Definition: vk_mem_alloc.h:2335
VmaDefragmentationInfo2::maxCpuBytesToMove
VkDeviceSize maxCpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3634
VmaPoolCreateInfo
struct VmaPoolCreateInfo VmaPoolCreateInfo
Describes parameter of created VmaPool.
vmaGetPoolName
void vmaGetPoolName(VmaAllocator allocator, VmaPool pool, const char **ppName)
Retrieves name of a custom pool.
VmaAllocationInfo::memoryType
uint32_t memoryType
Memory type index that this allocation was allocated from.
Definition: vk_mem_alloc.h:3188
vmaDestroyImage
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation)
Destroys Vulkan image and frees allocated memory.
VMA_ALLOCATION_CREATE_MAPPED_BIT
@ VMA_ALLOCATION_CREATE_MAPPED_BIT
Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
Definition: vk_mem_alloc.h:2765
vmaCalculateStats
void vmaCalculateStats(VmaAllocator allocator, VmaStats *pStats)
Retrieves statistics from current state of the Allocator.
vmaDestroyBuffer
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
VmaVulkanFunctions::vkCreateBuffer
PFN_vkCreateBuffer vkCreateBuffer
Definition: vk_mem_alloc.h:2332
PFN_vmaAllocateDeviceMemoryFunction
void(VKAPI_PTR * PFN_vmaAllocateDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size, void *pUserData)
Callback function called after successful vkAllocateMemory.
Definition: vk_mem_alloc.h:2185
vmaGetAllocatorInfo
void vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo *pAllocatorInfo)
Returns information about existing VmaAllocator object - handle to Vulkan device etc.
VmaPoolStats::unusedRangeCount
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3066
VmaPoolCreateFlagBits
VmaPoolCreateFlagBits
Flags to be passed as VmaPoolCreateInfo::flags.
Definition: vk_mem_alloc.h:2947
VmaAllocationInfo
struct VmaAllocationInfo VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
VmaDefragmentationStats::bytesMoved
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3696
VmaStatInfo::unusedRangeSizeMin
VkDeviceSize unusedRangeSizeMin
Definition: vk_mem_alloc.h:2557
VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
@ VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
Definition: vk_mem_alloc.h:2796
vmaCheckPoolCorruption
VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
Checks magic number in margins around all allocations in given memory pool in search for corruptions.
vmaBindImageMemory
VkResult vmaBindImageMemory(VmaAllocator allocator, VmaAllocation allocation, VkImage image)
Binds image to allocation.
PFN_vmaFreeDeviceMemoryFunction
void(VKAPI_PTR * PFN_vmaFreeDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size, void *pUserData)
Callback function called before vkFreeMemory.
Definition: vk_mem_alloc.h:2192
VmaDefragmentationPassMoveInfo
struct VmaDefragmentationPassMoveInfo VmaDefragmentationPassMoveInfo
VmaAllocationCreateInfo::flags
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2848
VmaVulkanFunctions::vkGetImageMemoryRequirements
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements
Definition: vk_mem_alloc.h:2331
vmaGetBudget
void vmaGetBudget(VmaAllocator allocator, VmaBudget *pBudget)
Retrieves information about current memory budget for all memory heaps.
VmaAllocationCreateInfo
Definition: vk_mem_alloc.h:2846
VmaAllocationCreateInfo::preferredFlags
VkMemoryPropertyFlags preferredFlags
Flags that preferably should be set in a memory type chosen for an allocation.
Definition: vk_mem_alloc.h:2864
vmaDefragmentationBegin
VkResult vmaDefragmentationBegin(VmaAllocator allocator, const VmaDefragmentationInfo2 *pInfo, VmaDefragmentationStats *pStats, VmaDefragmentationContext *pContext)
Begins defragmentation process.
vmaBindImageMemory2
VkResult vmaBindImageMemory2(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize allocationLocalOffset, VkImage image, const void *pNext)
Binds image to allocation with additional parameters.
VmaBudget
struct VmaBudget VmaBudget
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
vmaEndDefragmentationPass
VkResult vmaEndDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context)
VmaDefragmentationInfo2::pAllocationsChanged
VkBool32 * pAllocationsChanged
Optional, output. Pointer to array that will be filled with information whether the allocation at cer...
Definition: vk_mem_alloc.h:3610
VmaDefragmentationStats::allocationsMoved
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:3700
VmaAllocationCreateInfo::memoryTypeBits
uint32_t memoryTypeBits
Bitmask containing one bit set for every memory type acceptable for this allocation.
Definition: vk_mem_alloc.h:2872
VmaAllocatorInfo::physicalDevice
VkPhysicalDevice physicalDevice
Handle to Vulkan physical device object.
Definition: vk_mem_alloc.h:2488
VmaDefragmentationStats::deviceMemoryBlocksFreed
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:3702
VmaRecordSettings::pFilePath
const char * pFilePath
Path to the file that should be written by the recording.
Definition: vk_mem_alloc.h:2375
VmaStatInfo::allocationSizeMax
VkDeviceSize allocationSizeMax
Definition: vk_mem_alloc.h:2556
VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
Definition: vk_mem_alloc.h:3183
VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
@ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
Enables alternative, buddy allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2993
VmaAllocatorInfo
struct VmaAllocatorInfo VmaAllocatorInfo
Information about existing VmaAllocator object.
VmaBudget::allocationBytes
VkDeviceSize allocationBytes
Sum size of all allocations created in particular heap, in bytes.
Definition: vk_mem_alloc.h:2598
VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2841
VmaDefragmentationContext
Represents Opaque object that represents started defragmentation process.
VMA_POOL_CREATE_ALGORITHM_MASK
@ VMA_POOL_CREATE_ALGORITHM_MASK
Definition: vk_mem_alloc.h:2997
VmaDefragmentationInfo2::maxCpuAllocationsToMove
uint32_t maxCpuAllocationsToMove
Maximum number of allocations that can be moved to a different place using transfers on CPU side,...
Definition: vk_mem_alloc.h:3639
vmaFreeMemory
void vmaFreeMemory(VmaAllocator allocator, const VmaAllocation allocation)
Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(),...
VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:3680
VMA_ALLOCATION_CREATE_DONT_BIND_BIT
@ VMA_ALLOCATION_CREATE_DONT_BIND_BIT
Definition: vk_mem_alloc.h:2802
VmaDefragmentationInfo2
struct VmaDefragmentationInfo2 VmaDefragmentationInfo2
Parameters for defragmentation.