Vulkan Memory Allocator
vk_mem_alloc.h
Go to the documentation of this file.
1 //
2 // Copyright (c) 2017-2021 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 
2020 #ifdef __cplusplus
2021 extern "C" {
2022 #endif
2023 
2024 /*
2025 Define this macro to 0/1 to disable/enable support for recording functionality,
2026 available through VmaAllocatorCreateInfo::pRecordSettings.
2027 */
2028 #ifndef VMA_RECORDING_ENABLED
2029  #define VMA_RECORDING_ENABLED 0
2030 #endif
2031 
2032 #if !defined(NOMINMAX) && defined(VMA_IMPLEMENTATION)
2033  #define NOMINMAX // For windows.h
2034 #endif
2035 
2036 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
2037  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
2038  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
2039  extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
2040  extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
2041  extern PFN_vkAllocateMemory vkAllocateMemory;
2042  extern PFN_vkFreeMemory vkFreeMemory;
2043  extern PFN_vkMapMemory vkMapMemory;
2044  extern PFN_vkUnmapMemory vkUnmapMemory;
2045  extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
2046  extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
2047  extern PFN_vkBindBufferMemory vkBindBufferMemory;
2048  extern PFN_vkBindImageMemory vkBindImageMemory;
2049  extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
2050  extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
2051  extern PFN_vkCreateBuffer vkCreateBuffer;
2052  extern PFN_vkDestroyBuffer vkDestroyBuffer;
2053  extern PFN_vkCreateImage vkCreateImage;
2054  extern PFN_vkDestroyImage vkDestroyImage;
2055  extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
2056  #if VMA_VULKAN_VERSION >= 1001000
2057  extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
2058  extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
2059  extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
2060  extern PFN_vkBindImageMemory2 vkBindImageMemory2;
2061  extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
2062  #endif // #if VMA_VULKAN_VERSION >= 1001000
2063 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
2064 
2065 #ifndef VULKAN_H_
2066  #include <vulkan/vulkan.h>
2067 #endif
2068 
2069 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
2070 // where AAA = major, BBB = minor, CCC = patch.
2071 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
2072 #if !defined(VMA_VULKAN_VERSION)
2073  #if defined(VK_VERSION_1_2)
2074  #define VMA_VULKAN_VERSION 1002000
2075  #elif defined(VK_VERSION_1_1)
2076  #define VMA_VULKAN_VERSION 1001000
2077  #else
2078  #define VMA_VULKAN_VERSION 1000000
2079  #endif
2080 #endif
2081 
2082 #if !defined(VMA_DEDICATED_ALLOCATION)
2083  #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
2084  #define VMA_DEDICATED_ALLOCATION 1
2085  #else
2086  #define VMA_DEDICATED_ALLOCATION 0
2087  #endif
2088 #endif
2089 
2090 #if !defined(VMA_BIND_MEMORY2)
2091  #if VK_KHR_bind_memory2
2092  #define VMA_BIND_MEMORY2 1
2093  #else
2094  #define VMA_BIND_MEMORY2 0
2095  #endif
2096 #endif
2097 
2098 #if !defined(VMA_MEMORY_BUDGET)
2099  #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
2100  #define VMA_MEMORY_BUDGET 1
2101  #else
2102  #define VMA_MEMORY_BUDGET 0
2103  #endif
2104 #endif
2105 
2106 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
2107 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
2108  #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
2109  #define VMA_BUFFER_DEVICE_ADDRESS 1
2110  #else
2111  #define VMA_BUFFER_DEVICE_ADDRESS 0
2112  #endif
2113 #endif
2114 
2115 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
2116 #if !defined(VMA_MEMORY_PRIORITY)
2117  #if VK_EXT_memory_priority
2118  #define VMA_MEMORY_PRIORITY 1
2119  #else
2120  #define VMA_MEMORY_PRIORITY 0
2121  #endif
2122 #endif
2123 
2124 // Define these macros to decorate all public functions with additional code,
2125 // before and after returned type, appropriately. This may be useful for
2126 // exporting the functions when compiling VMA as a separate library. Example:
2127 // #define VMA_CALL_PRE __declspec(dllexport)
2128 // #define VMA_CALL_POST __cdecl
2129 #ifndef VMA_CALL_PRE
2130  #define VMA_CALL_PRE
2131 #endif
2132 #ifndef VMA_CALL_POST
2133  #define VMA_CALL_POST
2134 #endif
2135 
2136 // Define this macro to decorate pointers with an attribute specifying the
2137 // length of the array they point to if they are not null.
2138 //
2139 // The length may be one of
2140 // - The name of another parameter in the argument list where the pointer is declared
2141 // - The name of another member in the struct where the pointer is declared
2142 // - The name of a member of a struct type, meaning the value of that member in
2143 // the context of the call. For example
2144 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2145 // this means the number of memory heaps available in the device associated
2146 // with the VmaAllocator being dealt with.
2147 #ifndef VMA_LEN_IF_NOT_NULL
2148  #define VMA_LEN_IF_NOT_NULL(len)
2149 #endif
2150 
2151 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2152 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2153 #ifndef VMA_NULLABLE
2154  #ifdef __clang__
2155  #define VMA_NULLABLE _Nullable
2156  #else
2157  #define VMA_NULLABLE
2158  #endif
2159 #endif
2160 
2161 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2162 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2163 #ifndef VMA_NOT_NULL
2164  #ifdef __clang__
2165  #define VMA_NOT_NULL _Nonnull
2166  #else
2167  #define VMA_NOT_NULL
2168  #endif
2169 #endif
2170 
2171 // If non-dispatchable handles are represented as pointers then we can give
2172 // then nullability annotations
2173 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2174  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2175  #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2176  #else
2177  #define VMA_NOT_NULL_NON_DISPATCHABLE
2178  #endif
2179 #endif
2180 
2181 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2182  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2183  #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2184  #else
2185  #define VMA_NULLABLE_NON_DISPATCHABLE
2186  #endif
2187 #endif
2188 
2198 VK_DEFINE_HANDLE(VmaAllocator)
2199 
2200 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2202  VmaAllocator VMA_NOT_NULL allocator,
2203  uint32_t memoryType,
2204  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2205  VkDeviceSize size,
2206  void* VMA_NULLABLE pUserData);
2208 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2209  VmaAllocator VMA_NOT_NULL allocator,
2210  uint32_t memoryType,
2211  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2212  VkDeviceSize size,
2213  void* VMA_NULLABLE pUserData);
2214 
2228  void* VMA_NULLABLE pUserData;
2230 
2343 
2346 typedef VkFlags VmaAllocatorCreateFlags;
2347 
2352 typedef struct VmaVulkanFunctions {
2353  PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2354  PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2355  PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2356  PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2357  PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2358  PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2359  PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2360  PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2361  PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2362  PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2363  PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2364  PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2365  PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2366  PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2367  PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2368  PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2369  PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2370 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2371  PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2372  PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2373 #endif
2374 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2375  PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2376  PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2377 #endif
2378 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2379  PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2380 #endif
2382 
2384 typedef enum VmaRecordFlagBits {
2391 
2392  VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2394 typedef VkFlags VmaRecordFlags;
2395 
2397 typedef struct VmaRecordSettings
2398 {
2408  const char* VMA_NOT_NULL pFilePath;
2410 
2413 {
2417 
2418  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2420 
2421  VkDevice VMA_NOT_NULL device;
2423 
2426 
2427  const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2429 
2469  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2470 
2482  const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2487  VkInstance VMA_NOT_NULL instance;
2498 
2500 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2501  const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2502  VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2503 
2505 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2506  VmaAllocator VMA_NULLABLE allocator);
2507 
2510 typedef struct VmaAllocatorInfo
2511 {
2516  VkInstance VMA_NOT_NULL instance;
2521  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2526  VkDevice VMA_NOT_NULL device;
2528 
2534 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2535 
2540 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2541  VmaAllocator VMA_NOT_NULL allocator,
2542  const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2543 
2548 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2549  VmaAllocator VMA_NOT_NULL allocator,
2550  const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2551 
2558 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2559  VmaAllocator VMA_NOT_NULL allocator,
2560  uint32_t memoryTypeIndex,
2561  VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2562 
2571 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2572  VmaAllocator VMA_NOT_NULL allocator,
2573  uint32_t frameIndex);
2574 
2577 typedef struct VmaStatInfo
2578 {
2580  uint32_t blockCount;
2586  VkDeviceSize usedBytes;
2588  VkDeviceSize unusedBytes;
2589  VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2590  VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2592 
2594 typedef struct VmaStats
2595 {
2596  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2597  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2600 
2610 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2611  VmaAllocator VMA_NOT_NULL allocator,
2612  VmaStats* VMA_NOT_NULL pStats);
2613 
2616 typedef struct VmaBudget
2617 {
2620  VkDeviceSize blockBytes;
2621 
2631  VkDeviceSize allocationBytes;
2632 
2641  VkDeviceSize usage;
2642 
2652  VkDeviceSize budget;
2654 
2665 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2666  VmaAllocator VMA_NOT_NULL allocator,
2667  VmaBudget* VMA_NOT_NULL pBudget);
2668 
2669 #ifndef VMA_STATS_STRING_ENABLED
2670 #define VMA_STATS_STRING_ENABLED 1
2671 #endif
2672 
2673 #if VMA_STATS_STRING_ENABLED
2674 
2676 
2678 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2679  VmaAllocator VMA_NOT_NULL allocator,
2680  char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2681  VkBool32 detailedMap);
2682 
2683 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2684  VmaAllocator VMA_NOT_NULL allocator,
2685  char* VMA_NULLABLE pStatsString);
2686 
2687 #endif // #if VMA_STATS_STRING_ENABLED
2688 
2697 VK_DEFINE_HANDLE(VmaPool)
2698 
2699 typedef enum VmaMemoryUsage
2700 {
2762 
2763  VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2765 
2775 
2840 
2856 
2866 
2873 
2877 
2879 {
2892  VkMemoryPropertyFlags requiredFlags;
2897  VkMemoryPropertyFlags preferredFlags;
2905  uint32_t memoryTypeBits;
2911  VmaPool VMA_NULLABLE pool;
2918  void* VMA_NULLABLE pUserData;
2925  float priority;
2927 
2944 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2945  VmaAllocator VMA_NOT_NULL allocator,
2946  uint32_t memoryTypeBits,
2947  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2948  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2949 
2962 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2963  VmaAllocator VMA_NOT_NULL allocator,
2964  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2965  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2966  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2967 
2980 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2981  VmaAllocator VMA_NOT_NULL allocator,
2982  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2983  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2984  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2985 
3006 
3023 
3034 
3040 
3043 typedef VkFlags VmaPoolCreateFlags;
3044 
3047 typedef struct VmaPoolCreateInfo {
3062  VkDeviceSize blockSize;
3095  float priority;
3097 
3100 typedef struct VmaPoolStats {
3103  VkDeviceSize size;
3106  VkDeviceSize unusedSize;
3119  VkDeviceSize unusedRangeSizeMax;
3122  size_t blockCount;
3124 
3131 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
3132  VmaAllocator VMA_NOT_NULL allocator,
3133  const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
3134  VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
3135 
3138 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
3139  VmaAllocator VMA_NOT_NULL allocator,
3140  VmaPool VMA_NULLABLE pool);
3141 
3148 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3149  VmaAllocator VMA_NOT_NULL allocator,
3150  VmaPool VMA_NOT_NULL pool,
3151  VmaPoolStats* VMA_NOT_NULL pPoolStats);
3152 
3159 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3160  VmaAllocator VMA_NOT_NULL allocator,
3161  VmaPool VMA_NOT_NULL pool,
3162  size_t* VMA_NULLABLE pLostAllocationCount);
3163 
3178 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3179 
3186 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3187  VmaAllocator VMA_NOT_NULL allocator,
3188  VmaPool VMA_NOT_NULL pool,
3189  const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3190 
3196 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3197  VmaAllocator VMA_NOT_NULL allocator,
3198  VmaPool VMA_NOT_NULL pool,
3199  const char* VMA_NULLABLE pName);
3200 
3225 VK_DEFINE_HANDLE(VmaAllocation)
3226 
3227 
3229 typedef struct VmaAllocationInfo {
3234  uint32_t memoryType;
3243  VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3253  VkDeviceSize offset;
3264  VkDeviceSize size;
3273  void* VMA_NULLABLE pMappedData;
3278  void* VMA_NULLABLE pUserData;
3280 
3291 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3292  VmaAllocator VMA_NOT_NULL allocator,
3293  const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3294  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3295  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3296  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3297 
3317 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3318  VmaAllocator VMA_NOT_NULL allocator,
3319  const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3320  const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3321  size_t allocationCount,
3322  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3323  VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3324 
3331 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3332  VmaAllocator VMA_NOT_NULL allocator,
3333  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3334  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3335  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3336  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3337 
3339 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3340  VmaAllocator VMA_NOT_NULL allocator,
3341  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3342  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3343  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3344  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3345 
3350 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3351  VmaAllocator VMA_NOT_NULL allocator,
3352  const VmaAllocation VMA_NULLABLE allocation);
3353 
3364 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3365  VmaAllocator VMA_NOT_NULL allocator,
3366  size_t allocationCount,
3367  const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3368 
3385 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3386  VmaAllocator VMA_NOT_NULL allocator,
3387  VmaAllocation VMA_NOT_NULL allocation,
3388  VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3389 
3404 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3405  VmaAllocator VMA_NOT_NULL allocator,
3406  VmaAllocation VMA_NOT_NULL allocation);
3407 
3421 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3422  VmaAllocator VMA_NOT_NULL allocator,
3423  VmaAllocation VMA_NOT_NULL allocation,
3424  void* VMA_NULLABLE pUserData);
3425 
3436 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3437  VmaAllocator VMA_NOT_NULL allocator,
3438  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3439 
3478 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3479  VmaAllocator VMA_NOT_NULL allocator,
3480  VmaAllocation VMA_NOT_NULL allocation,
3481  void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3482 
3491 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3492  VmaAllocator VMA_NOT_NULL allocator,
3493  VmaAllocation VMA_NOT_NULL allocation);
3494 
3516 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3517  VmaAllocator VMA_NOT_NULL allocator,
3518  VmaAllocation VMA_NOT_NULL allocation,
3519  VkDeviceSize offset,
3520  VkDeviceSize size);
3521 
3543 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3544  VmaAllocator VMA_NOT_NULL allocator,
3545  VmaAllocation VMA_NOT_NULL allocation,
3546  VkDeviceSize offset,
3547  VkDeviceSize size);
3548 
3563 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3564  VmaAllocator VMA_NOT_NULL allocator,
3565  uint32_t allocationCount,
3566  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3567  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3568  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3569 
3584 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3585  VmaAllocator VMA_NOT_NULL allocator,
3586  uint32_t allocationCount,
3587  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3588  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3589  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3590 
3607 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3608 
3615 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3616 
3617 typedef enum VmaDefragmentationFlagBits {
3622 typedef VkFlags VmaDefragmentationFlags;
3623 
3628 typedef struct VmaDefragmentationInfo2 {
3643  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3649  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3652  uint32_t poolCount;
3668  const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3673  VkDeviceSize maxCpuBytesToMove;
3683  VkDeviceSize maxGpuBytesToMove;
3697  VkCommandBuffer VMA_NULLABLE commandBuffer;
3699 
3702  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3703  VkDeviceSize offset;
3705 
3711  uint32_t moveCount;
3712  VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3714 
3719 typedef struct VmaDefragmentationInfo {
3724  VkDeviceSize maxBytesToMove;
3731 
3733 typedef struct VmaDefragmentationStats {
3735  VkDeviceSize bytesMoved;
3737  VkDeviceSize bytesFreed;
3743 
3773 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3774  VmaAllocator VMA_NOT_NULL allocator,
3775  const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3776  VmaDefragmentationStats* VMA_NULLABLE pStats,
3777  VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3778 
3784 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3785  VmaAllocator VMA_NOT_NULL allocator,
3786  VmaDefragmentationContext VMA_NULLABLE context);
3787 
3788 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3789  VmaAllocator VMA_NOT_NULL allocator,
3790  VmaDefragmentationContext VMA_NULLABLE context,
3791  VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3792 );
3793 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3794  VmaAllocator VMA_NOT_NULL allocator,
3795  VmaDefragmentationContext VMA_NULLABLE context
3796 );
3797 
3838 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3839  VmaAllocator VMA_NOT_NULL allocator,
3840  const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3841  size_t allocationCount,
3842  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3843  const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3844  VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3845 
3858 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3859  VmaAllocator VMA_NOT_NULL allocator,
3860  VmaAllocation VMA_NOT_NULL allocation,
3861  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3862 
3873 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3874  VmaAllocator VMA_NOT_NULL allocator,
3875  VmaAllocation VMA_NOT_NULL allocation,
3876  VkDeviceSize allocationLocalOffset,
3877  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3878  const void* VMA_NULLABLE pNext);
3879 
3892 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3893  VmaAllocator VMA_NOT_NULL allocator,
3894  VmaAllocation VMA_NOT_NULL allocation,
3895  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3896 
3907 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3908  VmaAllocator VMA_NOT_NULL allocator,
3909  VmaAllocation VMA_NOT_NULL allocation,
3910  VkDeviceSize allocationLocalOffset,
3911  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3912  const void* VMA_NULLABLE pNext);
3913 
3944 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3945  VmaAllocator VMA_NOT_NULL allocator,
3946  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3947  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3948  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3949  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3950  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3951 
3963 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3964  VmaAllocator VMA_NOT_NULL allocator,
3965  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3966  VmaAllocation VMA_NULLABLE allocation);
3967 
3969 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3970  VmaAllocator VMA_NOT_NULL allocator,
3971  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3972  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3973  VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3974  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3975  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3976 
3988 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3989  VmaAllocator VMA_NOT_NULL allocator,
3990  VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3991  VmaAllocation VMA_NULLABLE allocation);
3992 
3993 #ifdef __cplusplus
3994 }
3995 #endif
3996 
3997 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3998 
3999 // For Visual Studio IntelliSense.
4000 #if defined(__cplusplus) && defined(__INTELLISENSE__)
4001 #define VMA_IMPLEMENTATION
4002 #endif
4003 
4004 #ifdef VMA_IMPLEMENTATION
4005 #undef VMA_IMPLEMENTATION
4006 
4007 #include <cstdint>
4008 #include <cstdlib>
4009 #include <cstring>
4010 #include <utility>
4011 
4012 #if VMA_RECORDING_ENABLED
4013  #include <chrono>
4014  #if defined(_WIN32)
4015  #include <windows.h>
4016  #else
4017  #include <sstream>
4018  #include <thread>
4019  #endif
4020 #endif
4021 
4022 /*******************************************************************************
4023 CONFIGURATION SECTION
4024 
4025 Define some of these macros before each #include of this header or change them
4026 here if you need other then default behavior depending on your environment.
4027 */
4028 
4029 /*
4030 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4031 internally, like:
4032 
4033  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
4034 */
4035 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
4036  #define VMA_STATIC_VULKAN_FUNCTIONS 1
4037 #endif
4038 
4039 /*
4040 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4041 internally, like:
4042 
4043  vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
4044 */
4045 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
4046  #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
4047  #if defined(VK_NO_PROTOTYPES)
4048  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
4049  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
4050  #endif
4051 #endif
4052 
4053 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
4054 //#define VMA_USE_STL_CONTAINERS 1
4055 
4056 /* Set this macro to 1 to make the library including and using STL containers:
4057 std::pair, std::vector, std::list, std::unordered_map.
4058 
4059 Set it to 0 or undefined to make the library using its own implementation of
4060 the containers.
4061 */
4062 #if VMA_USE_STL_CONTAINERS
4063  #define VMA_USE_STL_VECTOR 1
4064  #define VMA_USE_STL_UNORDERED_MAP 1
4065  #define VMA_USE_STL_LIST 1
4066 #endif
4067 
4068 #ifndef VMA_USE_STL_SHARED_MUTEX
4069  // Compiler conforms to C++17.
4070  #if __cplusplus >= 201703L
4071  #define VMA_USE_STL_SHARED_MUTEX 1
4072  // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
4073  // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
4074  // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
4075  #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
4076  #define VMA_USE_STL_SHARED_MUTEX 1
4077  #else
4078  #define VMA_USE_STL_SHARED_MUTEX 0
4079  #endif
4080 #endif
4081 
4082 /*
4083 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
4084 Library has its own container implementation.
4085 */
4086 #if VMA_USE_STL_VECTOR
4087  #include <vector>
4088 #endif
4089 
4090 #if VMA_USE_STL_UNORDERED_MAP
4091  #include <unordered_map>
4092 #endif
4093 
4094 #if VMA_USE_STL_LIST
4095  #include <list>
4096 #endif
4097 
4098 /*
4099 Following headers are used in this CONFIGURATION section only, so feel free to
4100 remove them if not needed.
4101 */
4102 #include <cassert> // for assert
4103 #include <algorithm> // for min, max
4104 #include <mutex>
4105 
4106 #ifndef VMA_NULL
4107  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
4108  #define VMA_NULL nullptr
4109 #endif
4110 
4111 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
4112 #include <cstdlib>
4113 static void* vma_aligned_alloc(size_t alignment, size_t size)
4114 {
4115  // alignment must be >= sizeof(void*)
4116  if(alignment < sizeof(void*))
4117  {
4118  alignment = sizeof(void*);
4119  }
4120 
4121  return memalign(alignment, size);
4122 }
4123 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
4124 #include <cstdlib>
4125 
4126 #if defined(__APPLE__)
4127 #include <AvailabilityMacros.h>
4128 #endif
4129 
4130 static void* vma_aligned_alloc(size_t alignment, size_t size)
4131 {
4132 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
4133 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
4134  // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
4135  // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
4136  // MAC_OS_X_VERSION_10_16), even though the function is marked
4137  // availabe for 10.15. That's why the preprocessor checks for 10.16 but
4138  // the __builtin_available checks for 10.15.
4139  // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
4140  if (__builtin_available(macOS 10.15, iOS 13, *))
4141  return aligned_alloc(alignment, size);
4142 #endif
4143 #endif
4144  // alignment must be >= sizeof(void*)
4145  if(alignment < sizeof(void*))
4146  {
4147  alignment = sizeof(void*);
4148  }
4149 
4150  void *pointer;
4151  if(posix_memalign(&pointer, alignment, size) == 0)
4152  return pointer;
4153  return VMA_NULL;
4154 }
4155 #elif defined(_WIN32)
4156 static void* vma_aligned_alloc(size_t alignment, size_t size)
4157 {
4158  return _aligned_malloc(size, alignment);
4159 }
4160 #else
4161 static void* vma_aligned_alloc(size_t alignment, size_t size)
4162 {
4163  return aligned_alloc(alignment, size);
4164 }
4165 #endif
4166 
4167 #if defined(_WIN32)
4168 static void vma_aligned_free(void* ptr)
4169 {
4170  _aligned_free(ptr);
4171 }
4172 #else
4173 static void vma_aligned_free(void* VMA_NULLABLE ptr)
4174 {
4175  free(ptr);
4176 }
4177 #endif
4178 
4179 // If your compiler is not compatible with C++11 and definition of
4180 // aligned_alloc() function is missing, uncommeting following line may help:
4181 
4182 //#include <malloc.h>
4183 
4184 // Normal assert to check for programmer's errors, especially in Debug configuration.
4185 #ifndef VMA_ASSERT
4186  #ifdef NDEBUG
4187  #define VMA_ASSERT(expr)
4188  #else
4189  #define VMA_ASSERT(expr) assert(expr)
4190  #endif
4191 #endif
4192 
4193 // Assert that will be called very often, like inside data structures e.g. operator[].
4194 // Making it non-empty can make program slow.
4195 #ifndef VMA_HEAVY_ASSERT
4196  #ifdef NDEBUG
4197  #define VMA_HEAVY_ASSERT(expr)
4198  #else
4199  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
4200  #endif
4201 #endif
4202 
4203 #ifndef VMA_ALIGN_OF
4204  #define VMA_ALIGN_OF(type) (__alignof(type))
4205 #endif
4206 
4207 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4208  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
4209 #endif
4210 
4211 #ifndef VMA_SYSTEM_ALIGNED_FREE
4212  // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
4213  #if defined(VMA_SYSTEM_FREE)
4214  #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
4215  #else
4216  #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
4217  #endif
4218 #endif
4219 
4220 #ifndef VMA_MIN
4221  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4222 #endif
4223 
4224 #ifndef VMA_MAX
4225  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4226 #endif
4227 
4228 #ifndef VMA_SWAP
4229  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4230 #endif
4231 
4232 #ifndef VMA_SORT
4233  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4234 #endif
4235 
4236 #ifndef VMA_DEBUG_LOG
4237  #define VMA_DEBUG_LOG(format, ...)
4238  /*
4239  #define VMA_DEBUG_LOG(format, ...) do { \
4240  printf(format, __VA_ARGS__); \
4241  printf("\n"); \
4242  } while(false)
4243  */
4244 #endif
4245 
4246 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4247 #if VMA_STATS_STRING_ENABLED
4248  static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
4249  {
4250  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4251  }
4252  static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
4253  {
4254  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4255  }
4256  static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
4257  {
4258  snprintf(outStr, strLen, "%p", ptr);
4259  }
4260 #endif
4261 
4262 #ifndef VMA_MUTEX
4263  class VmaMutex
4264  {
4265  public:
4266  void Lock() { m_Mutex.lock(); }
4267  void Unlock() { m_Mutex.unlock(); }
4268  bool TryLock() { return m_Mutex.try_lock(); }
4269  private:
4270  std::mutex m_Mutex;
4271  };
4272  #define VMA_MUTEX VmaMutex
4273 #endif
4274 
4275 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4276 #ifndef VMA_RW_MUTEX
4277  #if VMA_USE_STL_SHARED_MUTEX
4278  // Use std::shared_mutex from C++17.
4279  #include <shared_mutex>
4280  class VmaRWMutex
4281  {
4282  public:
4283  void LockRead() { m_Mutex.lock_shared(); }
4284  void UnlockRead() { m_Mutex.unlock_shared(); }
4285  bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4286  void LockWrite() { m_Mutex.lock(); }
4287  void UnlockWrite() { m_Mutex.unlock(); }
4288  bool TryLockWrite() { return m_Mutex.try_lock(); }
4289  private:
4290  std::shared_mutex m_Mutex;
4291  };
4292  #define VMA_RW_MUTEX VmaRWMutex
4293  #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4294  // Use SRWLOCK from WinAPI.
4295  // Minimum supported client = Windows Vista, server = Windows Server 2008.
4296  class VmaRWMutex
4297  {
4298  public:
4299  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4300  void LockRead() { AcquireSRWLockShared(&m_Lock); }
4301  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4302  bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4303  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4304  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4305  bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4306  private:
4307  SRWLOCK m_Lock;
4308  };
4309  #define VMA_RW_MUTEX VmaRWMutex
4310  #else
4311  // Less efficient fallback: Use normal mutex.
4312  class VmaRWMutex
4313  {
4314  public:
4315  void LockRead() { m_Mutex.Lock(); }
4316  void UnlockRead() { m_Mutex.Unlock(); }
4317  bool TryLockRead() { return m_Mutex.TryLock(); }
4318  void LockWrite() { m_Mutex.Lock(); }
4319  void UnlockWrite() { m_Mutex.Unlock(); }
4320  bool TryLockWrite() { return m_Mutex.TryLock(); }
4321  private:
4322  VMA_MUTEX m_Mutex;
4323  };
4324  #define VMA_RW_MUTEX VmaRWMutex
4325  #endif // #if VMA_USE_STL_SHARED_MUTEX
4326 #endif // #ifndef VMA_RW_MUTEX
4327 
4328 /*
4329 If providing your own implementation, you need to implement a subset of std::atomic.
4330 */
4331 #ifndef VMA_ATOMIC_UINT32
4332  #include <atomic>
4333  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4334 #endif
4335 
4336 #ifndef VMA_ATOMIC_UINT64
4337  #include <atomic>
4338  #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4339 #endif
4340 
4341 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4346  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4347 #endif
4348 
4349 #ifndef VMA_DEBUG_ALIGNMENT
4354  #define VMA_DEBUG_ALIGNMENT (1)
4355 #endif
4356 
4357 #ifndef VMA_DEBUG_MARGIN
4362  #define VMA_DEBUG_MARGIN (0)
4363 #endif
4364 
4365 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4370  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4371 #endif
4372 
4373 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4379  #define VMA_DEBUG_DETECT_CORRUPTION (0)
4380 #endif
4381 
4382 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4387  #define VMA_DEBUG_GLOBAL_MUTEX (0)
4388 #endif
4389 
4390 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4395  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4396 #endif
4397 
4398 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
4399  /*
4400  Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
4401  and return error instead of leaving up to Vulkan implementation what to do in such cases.
4402  */
4403  #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
4404 #endif
4405 
4406 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4408  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4409 #endif
4410 
4411 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4413  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4414 #endif
4415 
4416 #ifndef VMA_CLASS_NO_COPY
4417  #define VMA_CLASS_NO_COPY(className) \
4418  private: \
4419  className(const className&) = delete; \
4420  className& operator=(const className&) = delete;
4421 #endif
4422 
4423 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4424 
4425 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4426 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4427 
4428 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4429 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4430 
4431 /*******************************************************************************
4432 END OF CONFIGURATION
4433 */
4434 
4435 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4436 
4437 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4438 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4439 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4440 
4441 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4442 
4443 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4444  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4445 
4446 // Returns number of bits set to 1 in (v).
4447 static inline uint32_t VmaCountBitsSet(uint32_t v)
4448 {
4449  uint32_t c = v - ((v >> 1) & 0x55555555);
4450  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4451  c = ((c >> 4) + c) & 0x0F0F0F0F;
4452  c = ((c >> 8) + c) & 0x00FF00FF;
4453  c = ((c >> 16) + c) & 0x0000FFFF;
4454  return c;
4455 }
4456 
4457 /*
4458 Returns true if given number is a power of two.
4459 T must be unsigned integer number or signed integer but always nonnegative.
4460 For 0 returns true.
4461 */
4462 template <typename T>
4463 inline bool VmaIsPow2(T x)
4464 {
4465  return (x & (x-1)) == 0;
4466 }
4467 
4468 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4469 // Use types like uint32_t, uint64_t as T.
4470 template <typename T>
4471 static inline T VmaAlignUp(T val, T alignment)
4472 {
4473  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4474  return (val + alignment - 1) & ~(alignment - 1);
4475 }
4476 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4477 // Use types like uint32_t, uint64_t as T.
4478 template <typename T>
4479 static inline T VmaAlignDown(T val, T alignment)
4480 {
4481  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4482  return val & ~(alignment - 1);
4483 }
4484 
4485 // Division with mathematical rounding to nearest number.
4486 template <typename T>
4487 static inline T VmaRoundDiv(T x, T y)
4488 {
4489  return (x + (y / (T)2)) / y;
4490 }
4491 
4492 // Returns smallest power of 2 greater or equal to v.
4493 static inline uint32_t VmaNextPow2(uint32_t v)
4494 {
4495  v--;
4496  v |= v >> 1;
4497  v |= v >> 2;
4498  v |= v >> 4;
4499  v |= v >> 8;
4500  v |= v >> 16;
4501  v++;
4502  return v;
4503 }
4504 static inline uint64_t VmaNextPow2(uint64_t v)
4505 {
4506  v--;
4507  v |= v >> 1;
4508  v |= v >> 2;
4509  v |= v >> 4;
4510  v |= v >> 8;
4511  v |= v >> 16;
4512  v |= v >> 32;
4513  v++;
4514  return v;
4515 }
4516 
4517 // Returns largest power of 2 less or equal to v.
4518 static inline uint32_t VmaPrevPow2(uint32_t v)
4519 {
4520  v |= v >> 1;
4521  v |= v >> 2;
4522  v |= v >> 4;
4523  v |= v >> 8;
4524  v |= v >> 16;
4525  v = v ^ (v >> 1);
4526  return v;
4527 }
4528 static inline uint64_t VmaPrevPow2(uint64_t v)
4529 {
4530  v |= v >> 1;
4531  v |= v >> 2;
4532  v |= v >> 4;
4533  v |= v >> 8;
4534  v |= v >> 16;
4535  v |= v >> 32;
4536  v = v ^ (v >> 1);
4537  return v;
4538 }
4539 
4540 static inline bool VmaStrIsEmpty(const char* pStr)
4541 {
4542  return pStr == VMA_NULL || *pStr == '\0';
4543 }
4544 
4545 #if VMA_STATS_STRING_ENABLED
4546 
4547 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4548 {
4549  switch(algorithm)
4550  {
4552  return "Linear";
4554  return "Buddy";
4555  case 0:
4556  return "Default";
4557  default:
4558  VMA_ASSERT(0);
4559  return "";
4560  }
4561 }
4562 
4563 #endif // #if VMA_STATS_STRING_ENABLED
4564 
4565 #ifndef VMA_SORT
4566 
4567 template<typename Iterator, typename Compare>
4568 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4569 {
4570  Iterator centerValue = end; --centerValue;
4571  Iterator insertIndex = beg;
4572  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4573  {
4574  if(cmp(*memTypeIndex, *centerValue))
4575  {
4576  if(insertIndex != memTypeIndex)
4577  {
4578  VMA_SWAP(*memTypeIndex, *insertIndex);
4579  }
4580  ++insertIndex;
4581  }
4582  }
4583  if(insertIndex != centerValue)
4584  {
4585  VMA_SWAP(*insertIndex, *centerValue);
4586  }
4587  return insertIndex;
4588 }
4589 
4590 template<typename Iterator, typename Compare>
4591 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4592 {
4593  if(beg < end)
4594  {
4595  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4596  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4597  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4598  }
4599 }
4600 
4601 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4602 
4603 #endif // #ifndef VMA_SORT
4604 
4605 /*
4606 Returns true if two memory blocks occupy overlapping pages.
4607 ResourceA must be in less memory offset than ResourceB.
4608 
4609 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4610 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4611 */
4612 static inline bool VmaBlocksOnSamePage(
4613  VkDeviceSize resourceAOffset,
4614  VkDeviceSize resourceASize,
4615  VkDeviceSize resourceBOffset,
4616  VkDeviceSize pageSize)
4617 {
4618  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4619  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4620  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4621  VkDeviceSize resourceBStart = resourceBOffset;
4622  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4623  return resourceAEndPage == resourceBStartPage;
4624 }
4625 
4626 enum VmaSuballocationType
4627 {
4628  VMA_SUBALLOCATION_TYPE_FREE = 0,
4629  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4630  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4631  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4632  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4633  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4634  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4635 };
4636 
4637 /*
4638 Returns true if given suballocation types could conflict and must respect
4639 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4640 or linear image and another one is optimal image. If type is unknown, behave
4641 conservatively.
4642 */
4643 static inline bool VmaIsBufferImageGranularityConflict(
4644  VmaSuballocationType suballocType1,
4645  VmaSuballocationType suballocType2)
4646 {
4647  if(suballocType1 > suballocType2)
4648  {
4649  VMA_SWAP(suballocType1, suballocType2);
4650  }
4651 
4652  switch(suballocType1)
4653  {
4654  case VMA_SUBALLOCATION_TYPE_FREE:
4655  return false;
4656  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4657  return true;
4658  case VMA_SUBALLOCATION_TYPE_BUFFER:
4659  return
4660  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4661  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4662  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4663  return
4664  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4665  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4666  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4667  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4668  return
4669  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4670  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4671  return false;
4672  default:
4673  VMA_ASSERT(0);
4674  return true;
4675  }
4676 }
4677 
4678 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4679 {
4680 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4681  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4682  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4683  for(size_t i = 0; i < numberCount; ++i, ++pDst)
4684  {
4685  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4686  }
4687 #else
4688  // no-op
4689 #endif
4690 }
4691 
4692 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4693 {
4694 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4695  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4696  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4697  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4698  {
4699  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4700  {
4701  return false;
4702  }
4703  }
4704 #endif
4705  return true;
4706 }
4707 
4708 /*
4709 Fills structure with parameters of an example buffer to be used for transfers
4710 during GPU memory defragmentation.
4711 */
4712 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4713 {
4714  memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4715  outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4716  outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4717  outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4718 }
4719 
4720 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4721 struct VmaMutexLock
4722 {
4723  VMA_CLASS_NO_COPY(VmaMutexLock)
4724 public:
4725  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4726  m_pMutex(useMutex ? &mutex : VMA_NULL)
4727  { if(m_pMutex) { m_pMutex->Lock(); } }
4728  ~VmaMutexLock()
4729  { if(m_pMutex) { m_pMutex->Unlock(); } }
4730 private:
4731  VMA_MUTEX* m_pMutex;
4732 };
4733 
4734 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4735 struct VmaMutexLockRead
4736 {
4737  VMA_CLASS_NO_COPY(VmaMutexLockRead)
4738 public:
4739  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4740  m_pMutex(useMutex ? &mutex : VMA_NULL)
4741  { if(m_pMutex) { m_pMutex->LockRead(); } }
4742  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4743 private:
4744  VMA_RW_MUTEX* m_pMutex;
4745 };
4746 
4747 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4748 struct VmaMutexLockWrite
4749 {
4750  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4751 public:
4752  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4753  m_pMutex(useMutex ? &mutex : VMA_NULL)
4754  { if(m_pMutex) { m_pMutex->LockWrite(); } }
4755  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4756 private:
4757  VMA_RW_MUTEX* m_pMutex;
4758 };
4759 
4760 #if VMA_DEBUG_GLOBAL_MUTEX
4761  static VMA_MUTEX gDebugGlobalMutex;
4762  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4763 #else
4764  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4765 #endif
4766 
4767 // Minimum size of a free suballocation to register it in the free suballocation collection.
4768 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4769 
4770 /*
4771 Performs binary search and returns iterator to first element that is greater or
4772 equal to (key), according to comparison (cmp).
4773 
4774 Cmp should return true if first argument is less than second argument.
4775 
4776 Returned value is the found element, if present in the collection or place where
4777 new element with value (key) should be inserted.
4778 */
4779 template <typename CmpLess, typename IterT, typename KeyT>
4780 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4781 {
4782  size_t down = 0, up = (end - beg);
4783  while(down < up)
4784  {
4785  const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
4786  if(cmp(*(beg+mid), key))
4787  {
4788  down = mid + 1;
4789  }
4790  else
4791  {
4792  up = mid;
4793  }
4794  }
4795  return beg + down;
4796 }
4797 
4798 template<typename CmpLess, typename IterT, typename KeyT>
4799 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4800 {
4801  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4802  beg, end, value, cmp);
4803  if(it == end ||
4804  (!cmp(*it, value) && !cmp(value, *it)))
4805  {
4806  return it;
4807  }
4808  return end;
4809 }
4810 
4811 /*
4812 Returns true if all pointers in the array are not-null and unique.
4813 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4814 T must be pointer type, e.g. VmaAllocation, VmaPool.
4815 */
4816 template<typename T>
4817 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4818 {
4819  for(uint32_t i = 0; i < count; ++i)
4820  {
4821  const T iPtr = arr[i];
4822  if(iPtr == VMA_NULL)
4823  {
4824  return false;
4825  }
4826  for(uint32_t j = i + 1; j < count; ++j)
4827  {
4828  if(iPtr == arr[j])
4829  {
4830  return false;
4831  }
4832  }
4833  }
4834  return true;
4835 }
4836 
4837 template<typename MainT, typename NewT>
4838 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4839 {
4840  newStruct->pNext = mainStruct->pNext;
4841  mainStruct->pNext = newStruct;
4842 }
4843 
4845 // Memory allocation
4846 
4847 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4848 {
4849  void* result = VMA_NULL;
4850  if((pAllocationCallbacks != VMA_NULL) &&
4851  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4852  {
4853  result = (*pAllocationCallbacks->pfnAllocation)(
4854  pAllocationCallbacks->pUserData,
4855  size,
4856  alignment,
4857  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4858  }
4859  else
4860  {
4861  result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4862  }
4863  VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4864  return result;
4865 }
4866 
4867 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4868 {
4869  if((pAllocationCallbacks != VMA_NULL) &&
4870  (pAllocationCallbacks->pfnFree != VMA_NULL))
4871  {
4872  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4873  }
4874  else
4875  {
4876  VMA_SYSTEM_ALIGNED_FREE(ptr);
4877  }
4878 }
4879 
4880 template<typename T>
4881 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4882 {
4883  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4884 }
4885 
4886 template<typename T>
4887 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4888 {
4889  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4890 }
4891 
4892 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4893 
4894 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4895 
4896 template<typename T>
4897 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4898 {
4899  ptr->~T();
4900  VmaFree(pAllocationCallbacks, ptr);
4901 }
4902 
4903 template<typename T>
4904 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4905 {
4906  if(ptr != VMA_NULL)
4907  {
4908  for(size_t i = count; i--; )
4909  {
4910  ptr[i].~T();
4911  }
4912  VmaFree(pAllocationCallbacks, ptr);
4913  }
4914 }
4915 
4916 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4917 {
4918  if(srcStr != VMA_NULL)
4919  {
4920  const size_t len = strlen(srcStr);
4921  char* const result = vma_new_array(allocs, char, len + 1);
4922  memcpy(result, srcStr, len + 1);
4923  return result;
4924  }
4925  else
4926  {
4927  return VMA_NULL;
4928  }
4929 }
4930 
4931 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4932 {
4933  if(str != VMA_NULL)
4934  {
4935  const size_t len = strlen(str);
4936  vma_delete_array(allocs, str, len + 1);
4937  }
4938 }
4939 
4940 // STL-compatible allocator.
4941 template<typename T>
4942 class VmaStlAllocator
4943 {
4944 public:
4945  const VkAllocationCallbacks* const m_pCallbacks;
4946  typedef T value_type;
4947 
4948  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4949  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4950 
4951  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4952  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4953 
4954  template<typename U>
4955  bool operator==(const VmaStlAllocator<U>& rhs) const
4956  {
4957  return m_pCallbacks == rhs.m_pCallbacks;
4958  }
4959  template<typename U>
4960  bool operator!=(const VmaStlAllocator<U>& rhs) const
4961  {
4962  return m_pCallbacks != rhs.m_pCallbacks;
4963  }
4964 
4965  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4966  VmaStlAllocator(const VmaStlAllocator&) = default;
4967 };
4968 
4969 #if VMA_USE_STL_VECTOR
4970 
4971 #define VmaVector std::vector
4972 
4973 template<typename T, typename allocatorT>
4974 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4975 {
4976  vec.insert(vec.begin() + index, item);
4977 }
4978 
4979 template<typename T, typename allocatorT>
4980 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4981 {
4982  vec.erase(vec.begin() + index);
4983 }
4984 
4985 #else // #if VMA_USE_STL_VECTOR
4986 
4987 /* Class with interface compatible with subset of std::vector.
4988 T must be POD because constructors and destructors are not called and memcpy is
4989 used for these objects. */
4990 template<typename T, typename AllocatorT>
4991 class VmaVector
4992 {
4993 public:
4994  typedef T value_type;
4995 
4996  VmaVector(const AllocatorT& allocator) :
4997  m_Allocator(allocator),
4998  m_pArray(VMA_NULL),
4999  m_Count(0),
5000  m_Capacity(0)
5001  {
5002  }
5003 
5004  VmaVector(size_t count, const AllocatorT& allocator) :
5005  m_Allocator(allocator),
5006  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
5007  m_Count(count),
5008  m_Capacity(count)
5009  {
5010  }
5011 
5012  // This version of the constructor is here for compatibility with pre-C++14 std::vector.
5013  // value is unused.
5014  VmaVector(size_t count, const T& value, const AllocatorT& allocator)
5015  : VmaVector(count, allocator) {}
5016 
5017  VmaVector(const VmaVector<T, AllocatorT>& src) :
5018  m_Allocator(src.m_Allocator),
5019  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
5020  m_Count(src.m_Count),
5021  m_Capacity(src.m_Count)
5022  {
5023  if(m_Count != 0)
5024  {
5025  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
5026  }
5027  }
5028 
5029  ~VmaVector()
5030  {
5031  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5032  }
5033 
5034  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
5035  {
5036  if(&rhs != this)
5037  {
5038  resize(rhs.m_Count);
5039  if(m_Count != 0)
5040  {
5041  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
5042  }
5043  }
5044  return *this;
5045  }
5046 
5047  bool empty() const { return m_Count == 0; }
5048  size_t size() const { return m_Count; }
5049  T* data() { return m_pArray; }
5050  const T* data() const { return m_pArray; }
5051 
5052  T& operator[](size_t index)
5053  {
5054  VMA_HEAVY_ASSERT(index < m_Count);
5055  return m_pArray[index];
5056  }
5057  const T& operator[](size_t index) const
5058  {
5059  VMA_HEAVY_ASSERT(index < m_Count);
5060  return m_pArray[index];
5061  }
5062 
5063  T& front()
5064  {
5065  VMA_HEAVY_ASSERT(m_Count > 0);
5066  return m_pArray[0];
5067  }
5068  const T& front() const
5069  {
5070  VMA_HEAVY_ASSERT(m_Count > 0);
5071  return m_pArray[0];
5072  }
5073  T& back()
5074  {
5075  VMA_HEAVY_ASSERT(m_Count > 0);
5076  return m_pArray[m_Count - 1];
5077  }
5078  const T& back() const
5079  {
5080  VMA_HEAVY_ASSERT(m_Count > 0);
5081  return m_pArray[m_Count - 1];
5082  }
5083 
5084  void reserve(size_t newCapacity, bool freeMemory = false)
5085  {
5086  newCapacity = VMA_MAX(newCapacity, m_Count);
5087 
5088  if((newCapacity < m_Capacity) && !freeMemory)
5089  {
5090  newCapacity = m_Capacity;
5091  }
5092 
5093  if(newCapacity != m_Capacity)
5094  {
5095  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
5096  if(m_Count != 0)
5097  {
5098  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5099  }
5100  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5101  m_Capacity = newCapacity;
5102  m_pArray = newArray;
5103  }
5104  }
5105 
5106  void resize(size_t newCount)
5107  {
5108  size_t newCapacity = m_Capacity;
5109  if(newCount > m_Capacity)
5110  {
5111  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
5112  }
5113 
5114  if(newCapacity != m_Capacity)
5115  {
5116  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
5117  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
5118  if(elementsToCopy != 0)
5119  {
5120  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
5121  }
5122  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5123  m_Capacity = newCapacity;
5124  m_pArray = newArray;
5125  }
5126 
5127  m_Count = newCount;
5128  }
5129 
5130  void clear()
5131  {
5132  resize(0);
5133  }
5134 
5135  void shrink_to_fit()
5136  {
5137  if(m_Capacity > m_Count)
5138  {
5139  T* newArray = VMA_NULL;
5140  if(m_Count > 0)
5141  {
5142  newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
5143  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5144  }
5145  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5146  m_Capacity = m_Count;
5147  m_pArray = newArray;
5148  }
5149  }
5150 
5151  void insert(size_t index, const T& src)
5152  {
5153  VMA_HEAVY_ASSERT(index <= m_Count);
5154  const size_t oldCount = size();
5155  resize(oldCount + 1);
5156  if(index < oldCount)
5157  {
5158  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
5159  }
5160  m_pArray[index] = src;
5161  }
5162 
5163  void remove(size_t index)
5164  {
5165  VMA_HEAVY_ASSERT(index < m_Count);
5166  const size_t oldCount = size();
5167  if(index < oldCount - 1)
5168  {
5169  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
5170  }
5171  resize(oldCount - 1);
5172  }
5173 
5174  void push_back(const T& src)
5175  {
5176  const size_t newIndex = size();
5177  resize(newIndex + 1);
5178  m_pArray[newIndex] = src;
5179  }
5180 
5181  void pop_back()
5182  {
5183  VMA_HEAVY_ASSERT(m_Count > 0);
5184  resize(size() - 1);
5185  }
5186 
5187  void push_front(const T& src)
5188  {
5189  insert(0, src);
5190  }
5191 
5192  void pop_front()
5193  {
5194  VMA_HEAVY_ASSERT(m_Count > 0);
5195  remove(0);
5196  }
5197 
5198  typedef T* iterator;
5199 
5200  iterator begin() { return m_pArray; }
5201  iterator end() { return m_pArray + m_Count; }
5202 
5203 private:
5204  AllocatorT m_Allocator;
5205  T* m_pArray;
5206  size_t m_Count;
5207  size_t m_Capacity;
5208 };
5209 
5210 template<typename T, typename allocatorT>
5211 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5212 {
5213  vec.insert(index, item);
5214 }
5215 
5216 template<typename T, typename allocatorT>
5217 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5218 {
5219  vec.remove(index);
5220 }
5221 
5222 #endif // #if VMA_USE_STL_VECTOR
5223 
5224 template<typename CmpLess, typename VectorT>
5225 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5226 {
5227  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5228  vector.data(),
5229  vector.data() + vector.size(),
5230  value,
5231  CmpLess()) - vector.data();
5232  VmaVectorInsert(vector, indexToInsert, value);
5233  return indexToInsert;
5234 }
5235 
5236 template<typename CmpLess, typename VectorT>
5237 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5238 {
5239  CmpLess comparator;
5240  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5241  vector.begin(),
5242  vector.end(),
5243  value,
5244  comparator);
5245  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5246  {
5247  size_t indexToRemove = it - vector.begin();
5248  VmaVectorRemove(vector, indexToRemove);
5249  return true;
5250  }
5251  return false;
5252 }
5253 
5255 // class VmaSmallVector
5256 
5257 /*
5258 This is a vector (a variable-sized array), optimized for the case when the array is small.
5259 
5260 It contains some number of elements in-place, which allows it to avoid heap allocation
5261 when the actual number of elements is below that threshold. This allows normal "small"
5262 cases to be fast without losing generality for large inputs.
5263 */
5264 
5265 template<typename T, typename AllocatorT, size_t N>
5266 class VmaSmallVector
5267 {
5268 public:
5269  typedef T value_type;
5270 
5271  VmaSmallVector(const AllocatorT& allocator) :
5272  m_Count(0),
5273  m_DynamicArray(allocator)
5274  {
5275  }
5276  VmaSmallVector(size_t count, const AllocatorT& allocator) :
5277  m_Count(count),
5278  m_DynamicArray(count > N ? count : 0, allocator)
5279  {
5280  }
5281  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5282  VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5283  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5284  VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5285 
5286  bool empty() const { return m_Count == 0; }
5287  size_t size() const { return m_Count; }
5288  T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5289  const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5290 
5291  T& operator[](size_t index)
5292  {
5293  VMA_HEAVY_ASSERT(index < m_Count);
5294  return data()[index];
5295  }
5296  const T& operator[](size_t index) const
5297  {
5298  VMA_HEAVY_ASSERT(index < m_Count);
5299  return data()[index];
5300  }
5301 
5302  T& front()
5303  {
5304  VMA_HEAVY_ASSERT(m_Count > 0);
5305  return data()[0];
5306  }
5307  const T& front() const
5308  {
5309  VMA_HEAVY_ASSERT(m_Count > 0);
5310  return data()[0];
5311  }
5312  T& back()
5313  {
5314  VMA_HEAVY_ASSERT(m_Count > 0);
5315  return data()[m_Count - 1];
5316  }
5317  const T& back() const
5318  {
5319  VMA_HEAVY_ASSERT(m_Count > 0);
5320  return data()[m_Count - 1];
5321  }
5322 
5323  void resize(size_t newCount, bool freeMemory = false)
5324  {
5325  if(newCount > N && m_Count > N)
5326  {
5327  // Any direction, staying in m_DynamicArray
5328  m_DynamicArray.resize(newCount);
5329  if(freeMemory)
5330  {
5331  m_DynamicArray.shrink_to_fit();
5332  }
5333  }
5334  else if(newCount > N && m_Count <= N)
5335  {
5336  // Growing, moving from m_StaticArray to m_DynamicArray
5337  m_DynamicArray.resize(newCount);
5338  if(m_Count > 0)
5339  {
5340  memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5341  }
5342  }
5343  else if(newCount <= N && m_Count > N)
5344  {
5345  // Shrinking, moving from m_DynamicArray to m_StaticArray
5346  if(newCount > 0)
5347  {
5348  memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5349  }
5350  m_DynamicArray.resize(0);
5351  if(freeMemory)
5352  {
5353  m_DynamicArray.shrink_to_fit();
5354  }
5355  }
5356  else
5357  {
5358  // Any direction, staying in m_StaticArray - nothing to do here
5359  }
5360  m_Count = newCount;
5361  }
5362 
5363  void clear(bool freeMemory = false)
5364  {
5365  m_DynamicArray.clear();
5366  if(freeMemory)
5367  {
5368  m_DynamicArray.shrink_to_fit();
5369  }
5370  m_Count = 0;
5371  }
5372 
5373  void insert(size_t index, const T& src)
5374  {
5375  VMA_HEAVY_ASSERT(index <= m_Count);
5376  const size_t oldCount = size();
5377  resize(oldCount + 1);
5378  T* const dataPtr = data();
5379  if(index < oldCount)
5380  {
5381  // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5382  memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5383  }
5384  dataPtr[index] = src;
5385  }
5386 
5387  void remove(size_t index)
5388  {
5389  VMA_HEAVY_ASSERT(index < m_Count);
5390  const size_t oldCount = size();
5391  if(index < oldCount - 1)
5392  {
5393  // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5394  T* const dataPtr = data();
5395  memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5396  }
5397  resize(oldCount - 1);
5398  }
5399 
5400  void push_back(const T& src)
5401  {
5402  const size_t newIndex = size();
5403  resize(newIndex + 1);
5404  data()[newIndex] = src;
5405  }
5406 
5407  void pop_back()
5408  {
5409  VMA_HEAVY_ASSERT(m_Count > 0);
5410  resize(size() - 1);
5411  }
5412 
5413  void push_front(const T& src)
5414  {
5415  insert(0, src);
5416  }
5417 
5418  void pop_front()
5419  {
5420  VMA_HEAVY_ASSERT(m_Count > 0);
5421  remove(0);
5422  }
5423 
5424  typedef T* iterator;
5425 
5426  iterator begin() { return data(); }
5427  iterator end() { return data() + m_Count; }
5428 
5429 private:
5430  size_t m_Count;
5431  T m_StaticArray[N]; // Used when m_Size <= N
5432  VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5433 };
5434 
5436 // class VmaPoolAllocator
5437 
5438 /*
5439 Allocator for objects of type T using a list of arrays (pools) to speed up
5440 allocation. Number of elements that can be allocated is not bounded because
5441 allocator can create multiple blocks.
5442 */
5443 template<typename T>
5444 class VmaPoolAllocator
5445 {
5446  VMA_CLASS_NO_COPY(VmaPoolAllocator)
5447 public:
5448  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5449  ~VmaPoolAllocator();
5450  template<typename... Types> T* Alloc(Types... args);
5451  void Free(T* ptr);
5452 
5453 private:
5454  union Item
5455  {
5456  uint32_t NextFreeIndex;
5457  alignas(T) char Value[sizeof(T)];
5458  };
5459 
5460  struct ItemBlock
5461  {
5462  Item* pItems;
5463  uint32_t Capacity;
5464  uint32_t FirstFreeIndex;
5465  };
5466 
5467  const VkAllocationCallbacks* m_pAllocationCallbacks;
5468  const uint32_t m_FirstBlockCapacity;
5469  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5470 
5471  ItemBlock& CreateNewBlock();
5472 };
5473 
5474 template<typename T>
5475 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5476  m_pAllocationCallbacks(pAllocationCallbacks),
5477  m_FirstBlockCapacity(firstBlockCapacity),
5478  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5479 {
5480  VMA_ASSERT(m_FirstBlockCapacity > 1);
5481 }
5482 
5483 template<typename T>
5484 VmaPoolAllocator<T>::~VmaPoolAllocator()
5485 {
5486  for(size_t i = m_ItemBlocks.size(); i--; )
5487  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5488  m_ItemBlocks.clear();
5489 }
5490 
5491 template<typename T>
5492 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5493 {
5494  for(size_t i = m_ItemBlocks.size(); i--; )
5495  {
5496  ItemBlock& block = m_ItemBlocks[i];
5497  // This block has some free items: Use first one.
5498  if(block.FirstFreeIndex != UINT32_MAX)
5499  {
5500  Item* const pItem = &block.pItems[block.FirstFreeIndex];
5501  block.FirstFreeIndex = pItem->NextFreeIndex;
5502  T* result = (T*)&pItem->Value;
5503  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5504  return result;
5505  }
5506  }
5507 
5508  // No block has free item: Create new one and use it.
5509  ItemBlock& newBlock = CreateNewBlock();
5510  Item* const pItem = &newBlock.pItems[0];
5511  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5512  T* result = (T*)&pItem->Value;
5513  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5514  return result;
5515 }
5516 
5517 template<typename T>
5518 void VmaPoolAllocator<T>::Free(T* ptr)
5519 {
5520  // Search all memory blocks to find ptr.
5521  for(size_t i = m_ItemBlocks.size(); i--; )
5522  {
5523  ItemBlock& block = m_ItemBlocks[i];
5524 
5525  // Casting to union.
5526  Item* pItemPtr;
5527  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5528 
5529  // Check if pItemPtr is in address range of this block.
5530  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5531  {
5532  ptr->~T(); // Explicit destructor call.
5533  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5534  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5535  block.FirstFreeIndex = index;
5536  return;
5537  }
5538  }
5539  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5540 }
5541 
5542 template<typename T>
5543 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5544 {
5545  const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5546  m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5547 
5548  const ItemBlock newBlock = {
5549  vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5550  newBlockCapacity,
5551  0 };
5552 
5553  m_ItemBlocks.push_back(newBlock);
5554 
5555  // Setup singly-linked list of all free items in this block.
5556  for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5557  newBlock.pItems[i].NextFreeIndex = i + 1;
5558  newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5559  return m_ItemBlocks.back();
5560 }
5561 
5563 // class VmaRawList, VmaList
5564 
5565 #if VMA_USE_STL_LIST
5566 
5567 #define VmaList std::list
5568 
5569 #else // #if VMA_USE_STL_LIST
5570 
5571 template<typename T>
5572 struct VmaListItem
5573 {
5574  VmaListItem* pPrev;
5575  VmaListItem* pNext;
5576  T Value;
5577 };
5578 
5579 // Doubly linked list.
5580 template<typename T>
5581 class VmaRawList
5582 {
5583  VMA_CLASS_NO_COPY(VmaRawList)
5584 public:
5585  typedef VmaListItem<T> ItemType;
5586 
5587  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5588  ~VmaRawList();
5589  void Clear();
5590 
5591  size_t GetCount() const { return m_Count; }
5592  bool IsEmpty() const { return m_Count == 0; }
5593 
5594  ItemType* Front() { return m_pFront; }
5595  const ItemType* Front() const { return m_pFront; }
5596  ItemType* Back() { return m_pBack; }
5597  const ItemType* Back() const { return m_pBack; }
5598 
5599  ItemType* PushBack();
5600  ItemType* PushFront();
5601  ItemType* PushBack(const T& value);
5602  ItemType* PushFront(const T& value);
5603  void PopBack();
5604  void PopFront();
5605 
5606  // Item can be null - it means PushBack.
5607  ItemType* InsertBefore(ItemType* pItem);
5608  // Item can be null - it means PushFront.
5609  ItemType* InsertAfter(ItemType* pItem);
5610 
5611  ItemType* InsertBefore(ItemType* pItem, const T& value);
5612  ItemType* InsertAfter(ItemType* pItem, const T& value);
5613 
5614  void Remove(ItemType* pItem);
5615 
5616 private:
5617  const VkAllocationCallbacks* const m_pAllocationCallbacks;
5618  VmaPoolAllocator<ItemType> m_ItemAllocator;
5619  ItemType* m_pFront;
5620  ItemType* m_pBack;
5621  size_t m_Count;
5622 };
5623 
5624 template<typename T>
5625 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5626  m_pAllocationCallbacks(pAllocationCallbacks),
5627  m_ItemAllocator(pAllocationCallbacks, 128),
5628  m_pFront(VMA_NULL),
5629  m_pBack(VMA_NULL),
5630  m_Count(0)
5631 {
5632 }
5633 
5634 template<typename T>
5635 VmaRawList<T>::~VmaRawList()
5636 {
5637  // Intentionally not calling Clear, because that would be unnecessary
5638  // computations to return all items to m_ItemAllocator as free.
5639 }
5640 
5641 template<typename T>
5642 void VmaRawList<T>::Clear()
5643 {
5644  if(IsEmpty() == false)
5645  {
5646  ItemType* pItem = m_pBack;
5647  while(pItem != VMA_NULL)
5648  {
5649  ItemType* const pPrevItem = pItem->pPrev;
5650  m_ItemAllocator.Free(pItem);
5651  pItem = pPrevItem;
5652  }
5653  m_pFront = VMA_NULL;
5654  m_pBack = VMA_NULL;
5655  m_Count = 0;
5656  }
5657 }
5658 
5659 template<typename T>
5660 VmaListItem<T>* VmaRawList<T>::PushBack()
5661 {
5662  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5663  pNewItem->pNext = VMA_NULL;
5664  if(IsEmpty())
5665  {
5666  pNewItem->pPrev = VMA_NULL;
5667  m_pFront = pNewItem;
5668  m_pBack = pNewItem;
5669  m_Count = 1;
5670  }
5671  else
5672  {
5673  pNewItem->pPrev = m_pBack;
5674  m_pBack->pNext = pNewItem;
5675  m_pBack = pNewItem;
5676  ++m_Count;
5677  }
5678  return pNewItem;
5679 }
5680 
5681 template<typename T>
5682 VmaListItem<T>* VmaRawList<T>::PushFront()
5683 {
5684  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5685  pNewItem->pPrev = VMA_NULL;
5686  if(IsEmpty())
5687  {
5688  pNewItem->pNext = VMA_NULL;
5689  m_pFront = pNewItem;
5690  m_pBack = pNewItem;
5691  m_Count = 1;
5692  }
5693  else
5694  {
5695  pNewItem->pNext = m_pFront;
5696  m_pFront->pPrev = pNewItem;
5697  m_pFront = pNewItem;
5698  ++m_Count;
5699  }
5700  return pNewItem;
5701 }
5702 
5703 template<typename T>
5704 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5705 {
5706  ItemType* const pNewItem = PushBack();
5707  pNewItem->Value = value;
5708  return pNewItem;
5709 }
5710 
5711 template<typename T>
5712 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5713 {
5714  ItemType* const pNewItem = PushFront();
5715  pNewItem->Value = value;
5716  return pNewItem;
5717 }
5718 
5719 template<typename T>
5720 void VmaRawList<T>::PopBack()
5721 {
5722  VMA_HEAVY_ASSERT(m_Count > 0);
5723  ItemType* const pBackItem = m_pBack;
5724  ItemType* const pPrevItem = pBackItem->pPrev;
5725  if(pPrevItem != VMA_NULL)
5726  {
5727  pPrevItem->pNext = VMA_NULL;
5728  }
5729  m_pBack = pPrevItem;
5730  m_ItemAllocator.Free(pBackItem);
5731  --m_Count;
5732 }
5733 
5734 template<typename T>
5735 void VmaRawList<T>::PopFront()
5736 {
5737  VMA_HEAVY_ASSERT(m_Count > 0);
5738  ItemType* const pFrontItem = m_pFront;
5739  ItemType* const pNextItem = pFrontItem->pNext;
5740  if(pNextItem != VMA_NULL)
5741  {
5742  pNextItem->pPrev = VMA_NULL;
5743  }
5744  m_pFront = pNextItem;
5745  m_ItemAllocator.Free(pFrontItem);
5746  --m_Count;
5747 }
5748 
5749 template<typename T>
5750 void VmaRawList<T>::Remove(ItemType* pItem)
5751 {
5752  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5753  VMA_HEAVY_ASSERT(m_Count > 0);
5754 
5755  if(pItem->pPrev != VMA_NULL)
5756  {
5757  pItem->pPrev->pNext = pItem->pNext;
5758  }
5759  else
5760  {
5761  VMA_HEAVY_ASSERT(m_pFront == pItem);
5762  m_pFront = pItem->pNext;
5763  }
5764 
5765  if(pItem->pNext != VMA_NULL)
5766  {
5767  pItem->pNext->pPrev = pItem->pPrev;
5768  }
5769  else
5770  {
5771  VMA_HEAVY_ASSERT(m_pBack == pItem);
5772  m_pBack = pItem->pPrev;
5773  }
5774 
5775  m_ItemAllocator.Free(pItem);
5776  --m_Count;
5777 }
5778 
5779 template<typename T>
5780 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5781 {
5782  if(pItem != VMA_NULL)
5783  {
5784  ItemType* const prevItem = pItem->pPrev;
5785  ItemType* const newItem = m_ItemAllocator.Alloc();
5786  newItem->pPrev = prevItem;
5787  newItem->pNext = pItem;
5788  pItem->pPrev = newItem;
5789  if(prevItem != VMA_NULL)
5790  {
5791  prevItem->pNext = newItem;
5792  }
5793  else
5794  {
5795  VMA_HEAVY_ASSERT(m_pFront == pItem);
5796  m_pFront = newItem;
5797  }
5798  ++m_Count;
5799  return newItem;
5800  }
5801  else
5802  return PushBack();
5803 }
5804 
5805 template<typename T>
5806 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5807 {
5808  if(pItem != VMA_NULL)
5809  {
5810  ItemType* const nextItem = pItem->pNext;
5811  ItemType* const newItem = m_ItemAllocator.Alloc();
5812  newItem->pNext = nextItem;
5813  newItem->pPrev = pItem;
5814  pItem->pNext = newItem;
5815  if(nextItem != VMA_NULL)
5816  {
5817  nextItem->pPrev = newItem;
5818  }
5819  else
5820  {
5821  VMA_HEAVY_ASSERT(m_pBack == pItem);
5822  m_pBack = newItem;
5823  }
5824  ++m_Count;
5825  return newItem;
5826  }
5827  else
5828  return PushFront();
5829 }
5830 
5831 template<typename T>
5832 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5833 {
5834  ItemType* const newItem = InsertBefore(pItem);
5835  newItem->Value = value;
5836  return newItem;
5837 }
5838 
5839 template<typename T>
5840 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5841 {
5842  ItemType* const newItem = InsertAfter(pItem);
5843  newItem->Value = value;
5844  return newItem;
5845 }
5846 
5847 template<typename T, typename AllocatorT>
5848 class VmaList
5849 {
5850  VMA_CLASS_NO_COPY(VmaList)
5851 public:
5852  class iterator
5853  {
5854  public:
5855  iterator() :
5856  m_pList(VMA_NULL),
5857  m_pItem(VMA_NULL)
5858  {
5859  }
5860 
5861  T& operator*() const
5862  {
5863  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5864  return m_pItem->Value;
5865  }
5866  T* operator->() const
5867  {
5868  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5869  return &m_pItem->Value;
5870  }
5871 
5872  iterator& operator++()
5873  {
5874  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5875  m_pItem = m_pItem->pNext;
5876  return *this;
5877  }
5878  iterator& operator--()
5879  {
5880  if(m_pItem != VMA_NULL)
5881  {
5882  m_pItem = m_pItem->pPrev;
5883  }
5884  else
5885  {
5886  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5887  m_pItem = m_pList->Back();
5888  }
5889  return *this;
5890  }
5891 
5892  iterator operator++(int)
5893  {
5894  iterator result = *this;
5895  ++*this;
5896  return result;
5897  }
5898  iterator operator--(int)
5899  {
5900  iterator result = *this;
5901  --*this;
5902  return result;
5903  }
5904 
5905  bool operator==(const iterator& rhs) const
5906  {
5907  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5908  return m_pItem == rhs.m_pItem;
5909  }
5910  bool operator!=(const iterator& rhs) const
5911  {
5912  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5913  return m_pItem != rhs.m_pItem;
5914  }
5915 
5916  private:
5917  VmaRawList<T>* m_pList;
5918  VmaListItem<T>* m_pItem;
5919 
5920  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5921  m_pList(pList),
5922  m_pItem(pItem)
5923  {
5924  }
5925 
5926  friend class VmaList<T, AllocatorT>;
5927  };
5928 
5929  class const_iterator
5930  {
5931  public:
5932  const_iterator() :
5933  m_pList(VMA_NULL),
5934  m_pItem(VMA_NULL)
5935  {
5936  }
5937 
5938  const_iterator(const iterator& src) :
5939  m_pList(src.m_pList),
5940  m_pItem(src.m_pItem)
5941  {
5942  }
5943 
5944  const T& operator*() const
5945  {
5946  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5947  return m_pItem->Value;
5948  }
5949  const T* operator->() const
5950  {
5951  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5952  return &m_pItem->Value;
5953  }
5954 
5955  const_iterator& operator++()
5956  {
5957  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5958  m_pItem = m_pItem->pNext;
5959  return *this;
5960  }
5961  const_iterator& operator--()
5962  {
5963  if(m_pItem != VMA_NULL)
5964  {
5965  m_pItem = m_pItem->pPrev;
5966  }
5967  else
5968  {
5969  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5970  m_pItem = m_pList->Back();
5971  }
5972  return *this;
5973  }
5974 
5975  const_iterator operator++(int)
5976  {
5977  const_iterator result = *this;
5978  ++*this;
5979  return result;
5980  }
5981  const_iterator operator--(int)
5982  {
5983  const_iterator result = *this;
5984  --*this;
5985  return result;
5986  }
5987 
5988  bool operator==(const const_iterator& rhs) const
5989  {
5990  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5991  return m_pItem == rhs.m_pItem;
5992  }
5993  bool operator!=(const const_iterator& rhs) const
5994  {
5995  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5996  return m_pItem != rhs.m_pItem;
5997  }
5998 
5999  private:
6000  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
6001  m_pList(pList),
6002  m_pItem(pItem)
6003  {
6004  }
6005 
6006  const VmaRawList<T>* m_pList;
6007  const VmaListItem<T>* m_pItem;
6008 
6009  friend class VmaList<T, AllocatorT>;
6010  };
6011 
6012  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
6013 
6014  bool empty() const { return m_RawList.IsEmpty(); }
6015  size_t size() const { return m_RawList.GetCount(); }
6016 
6017  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
6018  iterator end() { return iterator(&m_RawList, VMA_NULL); }
6019 
6020  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
6021  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
6022 
6023  void clear() { m_RawList.Clear(); }
6024  void push_back(const T& value) { m_RawList.PushBack(value); }
6025  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
6026  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
6027 
6028 private:
6029  VmaRawList<T> m_RawList;
6030 };
6031 
6032 #endif // #if VMA_USE_STL_LIST
6033 
6035 // class VmaIntrusiveLinkedList
6036 
6037 /*
6038 Expected interface of ItemTypeTraits:
6039 struct MyItemTypeTraits
6040 {
6041  typedef MyItem ItemType;
6042  static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
6043  static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
6044  static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
6045  static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
6046 };
6047 */
6048 template<typename ItemTypeTraits>
6049 class VmaIntrusiveLinkedList
6050 {
6051 public:
6052  typedef typename ItemTypeTraits::ItemType ItemType;
6053  static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
6054  static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
6055  // Movable, not copyable.
6056  VmaIntrusiveLinkedList() { }
6057  VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6058  VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) :
6059  m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
6060  {
6061  src.m_Front = src.m_Back = VMA_NULL;
6062  src.m_Count = 0;
6063  }
6064  ~VmaIntrusiveLinkedList()
6065  {
6066  VMA_HEAVY_ASSERT(IsEmpty());
6067  }
6068  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6069  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src)
6070  {
6071  if(&src != this)
6072  {
6073  VMA_HEAVY_ASSERT(IsEmpty());
6074  m_Front = src.m_Front;
6075  m_Back = src.m_Back;
6076  m_Count = src.m_Count;
6077  src.m_Front = src.m_Back = VMA_NULL;
6078  src.m_Count = 0;
6079  }
6080  return *this;
6081  }
6082  void RemoveAll()
6083  {
6084  if(!IsEmpty())
6085  {
6086  ItemType* item = m_Back;
6087  while(item != VMA_NULL)
6088  {
6089  ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
6090  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6091  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6092  item = prevItem;
6093  }
6094  m_Front = VMA_NULL;
6095  m_Back = VMA_NULL;
6096  m_Count = 0;
6097  }
6098  }
6099  size_t GetCount() const { return m_Count; }
6100  bool IsEmpty() const { return m_Count == 0; }
6101  ItemType* Front() { return m_Front; }
6102  const ItemType* Front() const { return m_Front; }
6103  ItemType* Back() { return m_Back; }
6104  const ItemType* Back() const { return m_Back; }
6105  void PushBack(ItemType* item)
6106  {
6107  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6108  if(IsEmpty())
6109  {
6110  m_Front = item;
6111  m_Back = item;
6112  m_Count = 1;
6113  }
6114  else
6115  {
6116  ItemTypeTraits::AccessPrev(item) = m_Back;
6117  ItemTypeTraits::AccessNext(m_Back) = item;
6118  m_Back = item;
6119  ++m_Count;
6120  }
6121  }
6122  void PushFront(ItemType* item)
6123  {
6124  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6125  if(IsEmpty())
6126  {
6127  m_Front = item;
6128  m_Back = item;
6129  m_Count = 1;
6130  }
6131  else
6132  {
6133  ItemTypeTraits::AccessNext(item) = m_Front;
6134  ItemTypeTraits::AccessPrev(m_Front) = item;
6135  m_Front = item;
6136  ++m_Count;
6137  }
6138  }
6139  ItemType* PopBack()
6140  {
6141  VMA_HEAVY_ASSERT(m_Count > 0);
6142  ItemType* const backItem = m_Back;
6143  ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
6144  if(prevItem != VMA_NULL)
6145  {
6146  ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
6147  }
6148  m_Back = prevItem;
6149  --m_Count;
6150  ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
6151  ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
6152  return backItem;
6153  }
6154  ItemType* PopFront()
6155  {
6156  VMA_HEAVY_ASSERT(m_Count > 0);
6157  ItemType* const frontItem = m_Front;
6158  ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
6159  if(nextItem != VMA_NULL)
6160  {
6161  ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
6162  }
6163  m_Front = nextItem;
6164  --m_Count;
6165  ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
6166  ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
6167  return frontItem;
6168  }
6169 
6170  // MyItem can be null - it means PushBack.
6171  void InsertBefore(ItemType* existingItem, ItemType* newItem)
6172  {
6173  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6174  if(existingItem != VMA_NULL)
6175  {
6176  ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
6177  ItemTypeTraits::AccessPrev(newItem) = prevItem;
6178  ItemTypeTraits::AccessNext(newItem) = existingItem;
6179  ItemTypeTraits::AccessPrev(existingItem) = newItem;
6180  if(prevItem != VMA_NULL)
6181  {
6182  ItemTypeTraits::AccessNext(prevItem) = newItem;
6183  }
6184  else
6185  {
6186  VMA_HEAVY_ASSERT(m_Front == existingItem);
6187  m_Front = newItem;
6188  }
6189  ++m_Count;
6190  }
6191  else
6192  PushBack(newItem);
6193  }
6194  // MyItem can be null - it means PushFront.
6195  void InsertAfter(ItemType* existingItem, ItemType* newItem)
6196  {
6197  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6198  if(existingItem != VMA_NULL)
6199  {
6200  ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
6201  ItemTypeTraits::AccessNext(newItem) = nextItem;
6202  ItemTypeTraits::AccessPrev(newItem) = existingItem;
6203  ItemTypeTraits::AccessNext(existingItem) = newItem;
6204  if(nextItem != VMA_NULL)
6205  {
6206  ItemTypeTraits::AccessPrev(nextItem) = newItem;
6207  }
6208  else
6209  {
6210  VMA_HEAVY_ASSERT(m_Back == existingItem);
6211  m_Back = newItem;
6212  }
6213  ++m_Count;
6214  }
6215  else
6216  return PushFront(newItem);
6217  }
6218  void Remove(ItemType* item)
6219  {
6220  VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
6221  if(ItemTypeTraits::GetPrev(item) != VMA_NULL)
6222  {
6223  ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
6224  }
6225  else
6226  {
6227  VMA_HEAVY_ASSERT(m_Front == item);
6228  m_Front = ItemTypeTraits::GetNext(item);
6229  }
6230 
6231  if(ItemTypeTraits::GetNext(item) != VMA_NULL)
6232  {
6233  ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
6234  }
6235  else
6236  {
6237  VMA_HEAVY_ASSERT(m_Back == item);
6238  m_Back = ItemTypeTraits::GetPrev(item);
6239  }
6240  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6241  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6242  --m_Count;
6243  }
6244 private:
6245  ItemType* m_Front = VMA_NULL;
6246  ItemType* m_Back = VMA_NULL;
6247  size_t m_Count = 0;
6248 };
6249 
6251 // class VmaMap
6252 
6253 // Unused in this version.
6254 #if 0
6255 
6256 #if VMA_USE_STL_UNORDERED_MAP
6257 
6258 #define VmaPair std::pair
6259 
6260 #define VMA_MAP_TYPE(KeyT, ValueT) \
6261  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
6262 
6263 #else // #if VMA_USE_STL_UNORDERED_MAP
6264 
6265 template<typename T1, typename T2>
6266 struct VmaPair
6267 {
6268  T1 first;
6269  T2 second;
6270 
6271  VmaPair() : first(), second() { }
6272  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
6273 };
6274 
6275 /* Class compatible with subset of interface of std::unordered_map.
6276 KeyT, ValueT must be POD because they will be stored in VmaVector.
6277 */
6278 template<typename KeyT, typename ValueT>
6279 class VmaMap
6280 {
6281 public:
6282  typedef VmaPair<KeyT, ValueT> PairType;
6283  typedef PairType* iterator;
6284 
6285  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
6286 
6287  iterator begin() { return m_Vector.begin(); }
6288  iterator end() { return m_Vector.end(); }
6289 
6290  void insert(const PairType& pair);
6291  iterator find(const KeyT& key);
6292  void erase(iterator it);
6293 
6294 private:
6295  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
6296 };
6297 
6298 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
6299 
6300 template<typename FirstT, typename SecondT>
6301 struct VmaPairFirstLess
6302 {
6303  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
6304  {
6305  return lhs.first < rhs.first;
6306  }
6307  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
6308  {
6309  return lhs.first < rhsFirst;
6310  }
6311 };
6312 
6313 template<typename KeyT, typename ValueT>
6314 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
6315 {
6316  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
6317  m_Vector.data(),
6318  m_Vector.data() + m_Vector.size(),
6319  pair,
6320  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
6321  VmaVectorInsert(m_Vector, indexToInsert, pair);
6322 }
6323 
6324 template<typename KeyT, typename ValueT>
6325 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
6326 {
6327  PairType* it = VmaBinaryFindFirstNotLess(
6328  m_Vector.data(),
6329  m_Vector.data() + m_Vector.size(),
6330  key,
6331  VmaPairFirstLess<KeyT, ValueT>());
6332  if((it != m_Vector.end()) && (it->first == key))
6333  {
6334  return it;
6335  }
6336  else
6337  {
6338  return m_Vector.end();
6339  }
6340 }
6341 
6342 template<typename KeyT, typename ValueT>
6343 void VmaMap<KeyT, ValueT>::erase(iterator it)
6344 {
6345  VmaVectorRemove(m_Vector, it - m_Vector.begin());
6346 }
6347 
6348 #endif // #if VMA_USE_STL_UNORDERED_MAP
6349 
6350 #endif // #if 0
6351 
6353 
6354 class VmaDeviceMemoryBlock;
6355 
6356 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
6357 
6358 struct VmaAllocation_T
6359 {
6360 private:
6361  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
6362 
6363  enum FLAGS
6364  {
6365  FLAG_USER_DATA_STRING = 0x01,
6366  };
6367 
6368 public:
6369  enum ALLOCATION_TYPE
6370  {
6371  ALLOCATION_TYPE_NONE,
6372  ALLOCATION_TYPE_BLOCK,
6373  ALLOCATION_TYPE_DEDICATED,
6374  };
6375 
6376  /*
6377  This struct is allocated using VmaPoolAllocator.
6378  */
6379 
6380  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
6381  m_Alignment{1},
6382  m_Size{0},
6383  m_pUserData{VMA_NULL},
6384  m_LastUseFrameIndex{currentFrameIndex},
6385  m_MemoryTypeIndex{0},
6386  m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
6387  m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
6388  m_MapCount{0},
6389  m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
6390  {
6391 #if VMA_STATS_STRING_ENABLED
6392  m_CreationFrameIndex = currentFrameIndex;
6393  m_BufferImageUsage = 0;
6394 #endif
6395  }
6396 
6397  ~VmaAllocation_T()
6398  {
6399  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
6400 
6401  // Check if owned string was freed.
6402  VMA_ASSERT(m_pUserData == VMA_NULL);
6403  }
6404 
6405  void InitBlockAllocation(
6406  VmaDeviceMemoryBlock* block,
6407  VkDeviceSize offset,
6408  VkDeviceSize alignment,
6409  VkDeviceSize size,
6410  uint32_t memoryTypeIndex,
6411  VmaSuballocationType suballocationType,
6412  bool mapped,
6413  bool canBecomeLost)
6414  {
6415  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6416  VMA_ASSERT(block != VMA_NULL);
6417  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6418  m_Alignment = alignment;
6419  m_Size = size;
6420  m_MemoryTypeIndex = memoryTypeIndex;
6421  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6422  m_SuballocationType = (uint8_t)suballocationType;
6423  m_BlockAllocation.m_Block = block;
6424  m_BlockAllocation.m_Offset = offset;
6425  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6426  }
6427 
6428  void InitLost()
6429  {
6430  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6431  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6432  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6433  m_MemoryTypeIndex = 0;
6434  m_BlockAllocation.m_Block = VMA_NULL;
6435  m_BlockAllocation.m_Offset = 0;
6436  m_BlockAllocation.m_CanBecomeLost = true;
6437  }
6438 
6439  void ChangeBlockAllocation(
6440  VmaAllocator hAllocator,
6441  VmaDeviceMemoryBlock* block,
6442  VkDeviceSize offset);
6443 
6444  void ChangeOffset(VkDeviceSize newOffset);
6445 
6446  // pMappedData not null means allocation is created with MAPPED flag.
6447  void InitDedicatedAllocation(
6448  uint32_t memoryTypeIndex,
6449  VkDeviceMemory hMemory,
6450  VmaSuballocationType suballocationType,
6451  void* pMappedData,
6452  VkDeviceSize size)
6453  {
6454  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6455  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6456  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6457  m_Alignment = 0;
6458  m_Size = size;
6459  m_MemoryTypeIndex = memoryTypeIndex;
6460  m_SuballocationType = (uint8_t)suballocationType;
6461  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6462  m_DedicatedAllocation.m_hMemory = hMemory;
6463  m_DedicatedAllocation.m_pMappedData = pMappedData;
6464  m_DedicatedAllocation.m_Prev = VMA_NULL;
6465  m_DedicatedAllocation.m_Next = VMA_NULL;
6466  }
6467 
6468  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6469  VkDeviceSize GetAlignment() const { return m_Alignment; }
6470  VkDeviceSize GetSize() const { return m_Size; }
6471  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6472  void* GetUserData() const { return m_pUserData; }
6473  void SetUserData(VmaAllocator hAllocator, void* pUserData);
6474  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6475 
6476  VmaDeviceMemoryBlock* GetBlock() const
6477  {
6478  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6479  return m_BlockAllocation.m_Block;
6480  }
6481  VkDeviceSize GetOffset() const;
6482  VkDeviceMemory GetMemory() const;
6483  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6484  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6485  void* GetMappedData() const;
6486  bool CanBecomeLost() const;
6487 
6488  uint32_t GetLastUseFrameIndex() const
6489  {
6490  return m_LastUseFrameIndex.load();
6491  }
6492  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6493  {
6494  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6495  }
6496  /*
6497  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6498  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6499  - Else, returns false.
6500 
6501  If hAllocation is already lost, assert - you should not call it then.
6502  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6503  */
6504  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6505 
6506  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6507  {
6508  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6509  outInfo.blockCount = 1;
6510  outInfo.allocationCount = 1;
6511  outInfo.unusedRangeCount = 0;
6512  outInfo.usedBytes = m_Size;
6513  outInfo.unusedBytes = 0;
6514  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6515  outInfo.unusedRangeSizeMin = UINT64_MAX;
6516  outInfo.unusedRangeSizeMax = 0;
6517  }
6518 
6519  void BlockAllocMap();
6520  void BlockAllocUnmap();
6521  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6522  void DedicatedAllocUnmap(VmaAllocator hAllocator);
6523 
6524 #if VMA_STATS_STRING_ENABLED
6525  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6526  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6527 
6528  void InitBufferImageUsage(uint32_t bufferImageUsage)
6529  {
6530  VMA_ASSERT(m_BufferImageUsage == 0);
6531  m_BufferImageUsage = bufferImageUsage;
6532  }
6533 
6534  void PrintParameters(class VmaJsonWriter& json) const;
6535 #endif
6536 
6537 private:
6538  VkDeviceSize m_Alignment;
6539  VkDeviceSize m_Size;
6540  void* m_pUserData;
6541  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6542  uint32_t m_MemoryTypeIndex;
6543  uint8_t m_Type; // ALLOCATION_TYPE
6544  uint8_t m_SuballocationType; // VmaSuballocationType
6545  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6546  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6547  uint8_t m_MapCount;
6548  uint8_t m_Flags; // enum FLAGS
6549 
6550  // Allocation out of VmaDeviceMemoryBlock.
6551  struct BlockAllocation
6552  {
6553  VmaDeviceMemoryBlock* m_Block;
6554  VkDeviceSize m_Offset;
6555  bool m_CanBecomeLost;
6556  };
6557 
6558  // Allocation for an object that has its own private VkDeviceMemory.
6559  struct DedicatedAllocation
6560  {
6561  VkDeviceMemory m_hMemory;
6562  void* m_pMappedData; // Not null means memory is mapped.
6563  VmaAllocation_T* m_Prev;
6564  VmaAllocation_T* m_Next;
6565  };
6566 
6567  union
6568  {
6569  // Allocation out of VmaDeviceMemoryBlock.
6570  BlockAllocation m_BlockAllocation;
6571  // Allocation for an object that has its own private VkDeviceMemory.
6572  DedicatedAllocation m_DedicatedAllocation;
6573  };
6574 
6575 #if VMA_STATS_STRING_ENABLED
6576  uint32_t m_CreationFrameIndex;
6577  uint32_t m_BufferImageUsage; // 0 if unknown.
6578 #endif
6579 
6580  void FreeUserDataString(VmaAllocator hAllocator);
6581 
6582  friend struct VmaDedicatedAllocationListItemTraits;
6583 };
6584 
6585 struct VmaDedicatedAllocationListItemTraits
6586 {
6587  typedef VmaAllocation_T ItemType;
6588  static ItemType* GetPrev(const ItemType* item)
6589  {
6590  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6591  return item->m_DedicatedAllocation.m_Prev;
6592  }
6593  static ItemType* GetNext(const ItemType* item)
6594  {
6595  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6596  return item->m_DedicatedAllocation.m_Next;
6597  }
6598  static ItemType*& AccessPrev(ItemType* item)
6599  {
6600  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6601  return item->m_DedicatedAllocation.m_Prev;
6602  }
6603  static ItemType*& AccessNext(ItemType* item){
6604  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6605  return item->m_DedicatedAllocation.m_Next;
6606  }
6607 };
6608 
6609 /*
6610 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6611 allocated memory block or free.
6612 */
6613 struct VmaSuballocation
6614 {
6615  VkDeviceSize offset;
6616  VkDeviceSize size;
6617  VmaAllocation hAllocation;
6618  VmaSuballocationType type;
6619 };
6620 
6621 // Comparator for offsets.
6622 struct VmaSuballocationOffsetLess
6623 {
6624  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6625  {
6626  return lhs.offset < rhs.offset;
6627  }
6628 };
6629 struct VmaSuballocationOffsetGreater
6630 {
6631  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6632  {
6633  return lhs.offset > rhs.offset;
6634  }
6635 };
6636 
6637 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6638 
6639 // Cost of one additional allocation lost, as equivalent in bytes.
6640 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6641 
6642 enum class VmaAllocationRequestType
6643 {
6644  Normal,
6645  // Used by "Linear" algorithm.
6646  UpperAddress,
6647  EndOf1st,
6648  EndOf2nd,
6649 };
6650 
6651 /*
6652 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6653 
6654 If canMakeOtherLost was false:
6655 - item points to a FREE suballocation.
6656 - itemsToMakeLostCount is 0.
6657 
6658 If canMakeOtherLost was true:
6659 - item points to first of sequence of suballocations, which are either FREE,
6660  or point to VmaAllocations that can become lost.
6661 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6662  the requested allocation to succeed.
6663 */
6664 struct VmaAllocationRequest
6665 {
6666  VkDeviceSize offset;
6667  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6668  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6669  VmaSuballocationList::iterator item;
6670  size_t itemsToMakeLostCount;
6671  void* customData;
6672  VmaAllocationRequestType type;
6673 
6674  VkDeviceSize CalcCost() const
6675  {
6676  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6677  }
6678 };
6679 
6680 /*
6681 Data structure used for bookkeeping of allocations and unused ranges of memory
6682 in a single VkDeviceMemory block.
6683 */
6684 class VmaBlockMetadata
6685 {
6686 public:
6687  VmaBlockMetadata(VmaAllocator hAllocator);
6688  virtual ~VmaBlockMetadata() { }
6689  virtual void Init(VkDeviceSize size) { m_Size = size; }
6690 
6691  // Validates all data structures inside this object. If not valid, returns false.
6692  virtual bool Validate() const = 0;
6693  VkDeviceSize GetSize() const { return m_Size; }
6694  virtual size_t GetAllocationCount() const = 0;
6695  virtual VkDeviceSize GetSumFreeSize() const = 0;
6696  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6697  // Returns true if this block is empty - contains only single free suballocation.
6698  virtual bool IsEmpty() const = 0;
6699 
6700  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6701  // Shouldn't modify blockCount.
6702  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6703 
6704 #if VMA_STATS_STRING_ENABLED
6705  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6706 #endif
6707 
6708  // Tries to find a place for suballocation with given parameters inside this block.
6709  // If succeeded, fills pAllocationRequest and returns true.
6710  // If failed, returns false.
6711  virtual bool CreateAllocationRequest(
6712  uint32_t currentFrameIndex,
6713  uint32_t frameInUseCount,
6714  VkDeviceSize bufferImageGranularity,
6715  VkDeviceSize allocSize,
6716  VkDeviceSize allocAlignment,
6717  bool upperAddress,
6718  VmaSuballocationType allocType,
6719  bool canMakeOtherLost,
6720  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6721  uint32_t strategy,
6722  VmaAllocationRequest* pAllocationRequest) = 0;
6723 
6724  virtual bool MakeRequestedAllocationsLost(
6725  uint32_t currentFrameIndex,
6726  uint32_t frameInUseCount,
6727  VmaAllocationRequest* pAllocationRequest) = 0;
6728 
6729  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6730 
6731  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6732 
6733  // Makes actual allocation based on request. Request must already be checked and valid.
6734  virtual void Alloc(
6735  const VmaAllocationRequest& request,
6736  VmaSuballocationType type,
6737  VkDeviceSize allocSize,
6738  VmaAllocation hAllocation) = 0;
6739 
6740  // Frees suballocation assigned to given memory region.
6741  virtual void Free(const VmaAllocation allocation) = 0;
6742  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6743 
6744 protected:
6745  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6746 
6747 #if VMA_STATS_STRING_ENABLED
6748  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6749  VkDeviceSize unusedBytes,
6750  size_t allocationCount,
6751  size_t unusedRangeCount) const;
6752  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6753  VkDeviceSize offset,
6754  VmaAllocation hAllocation) const;
6755  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6756  VkDeviceSize offset,
6757  VkDeviceSize size) const;
6758  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6759 #endif
6760 
6761 private:
6762  VkDeviceSize m_Size;
6763  const VkAllocationCallbacks* m_pAllocationCallbacks;
6764 };
6765 
6766 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6767  VMA_ASSERT(0 && "Validation failed: " #cond); \
6768  return false; \
6769  } } while(false)
6770 
6771 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6772 {
6773  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6774 public:
6775  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6776  virtual ~VmaBlockMetadata_Generic();
6777  virtual void Init(VkDeviceSize size);
6778 
6779  virtual bool Validate() const;
6780  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6781  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6782  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6783  virtual bool IsEmpty() const;
6784 
6785  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6786  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6787 
6788 #if VMA_STATS_STRING_ENABLED
6789  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6790 #endif
6791 
6792  virtual bool CreateAllocationRequest(
6793  uint32_t currentFrameIndex,
6794  uint32_t frameInUseCount,
6795  VkDeviceSize bufferImageGranularity,
6796  VkDeviceSize allocSize,
6797  VkDeviceSize allocAlignment,
6798  bool upperAddress,
6799  VmaSuballocationType allocType,
6800  bool canMakeOtherLost,
6801  uint32_t strategy,
6802  VmaAllocationRequest* pAllocationRequest);
6803 
6804  virtual bool MakeRequestedAllocationsLost(
6805  uint32_t currentFrameIndex,
6806  uint32_t frameInUseCount,
6807  VmaAllocationRequest* pAllocationRequest);
6808 
6809  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6810 
6811  virtual VkResult CheckCorruption(const void* pBlockData);
6812 
6813  virtual void Alloc(
6814  const VmaAllocationRequest& request,
6815  VmaSuballocationType type,
6816  VkDeviceSize allocSize,
6817  VmaAllocation hAllocation);
6818 
6819  virtual void Free(const VmaAllocation allocation);
6820  virtual void FreeAtOffset(VkDeviceSize offset);
6821 
6823  // For defragmentation
6824 
6825  bool IsBufferImageGranularityConflictPossible(
6826  VkDeviceSize bufferImageGranularity,
6827  VmaSuballocationType& inOutPrevSuballocType) const;
6828 
6829 private:
6830  friend class VmaDefragmentationAlgorithm_Generic;
6831  friend class VmaDefragmentationAlgorithm_Fast;
6832 
6833  uint32_t m_FreeCount;
6834  VkDeviceSize m_SumFreeSize;
6835  VmaSuballocationList m_Suballocations;
6836  // Suballocations that are free and have size greater than certain threshold.
6837  // Sorted by size, ascending.
6838  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6839 
6840  bool ValidateFreeSuballocationList() const;
6841 
6842  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6843  // If yes, fills pOffset and returns true. If no, returns false.
6844  bool CheckAllocation(
6845  uint32_t currentFrameIndex,
6846  uint32_t frameInUseCount,
6847  VkDeviceSize bufferImageGranularity,
6848  VkDeviceSize allocSize,
6849  VkDeviceSize allocAlignment,
6850  VmaSuballocationType allocType,
6851  VmaSuballocationList::const_iterator suballocItem,
6852  bool canMakeOtherLost,
6853  VkDeviceSize* pOffset,
6854  size_t* itemsToMakeLostCount,
6855  VkDeviceSize* pSumFreeSize,
6856  VkDeviceSize* pSumItemSize) const;
6857  // Given free suballocation, it merges it with following one, which must also be free.
6858  void MergeFreeWithNext(VmaSuballocationList::iterator item);
6859  // Releases given suballocation, making it free.
6860  // Merges it with adjacent free suballocations if applicable.
6861  // Returns iterator to new free suballocation at this place.
6862  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6863  // Given free suballocation, it inserts it into sorted list of
6864  // m_FreeSuballocationsBySize if it's suitable.
6865  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6866  // Given free suballocation, it removes it from sorted list of
6867  // m_FreeSuballocationsBySize if it's suitable.
6868  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6869 };
6870 
6871 /*
6872 Allocations and their references in internal data structure look like this:
6873 
6874 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6875 
6876  0 +-------+
6877  | |
6878  | |
6879  | |
6880  +-------+
6881  | Alloc | 1st[m_1stNullItemsBeginCount]
6882  +-------+
6883  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6884  +-------+
6885  | ... |
6886  +-------+
6887  | Alloc | 1st[1st.size() - 1]
6888  +-------+
6889  | |
6890  | |
6891  | |
6892 GetSize() +-------+
6893 
6894 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6895 
6896  0 +-------+
6897  | Alloc | 2nd[0]
6898  +-------+
6899  | Alloc | 2nd[1]
6900  +-------+
6901  | ... |
6902  +-------+
6903  | Alloc | 2nd[2nd.size() - 1]
6904  +-------+
6905  | |
6906  | |
6907  | |
6908  +-------+
6909  | Alloc | 1st[m_1stNullItemsBeginCount]
6910  +-------+
6911  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6912  +-------+
6913  | ... |
6914  +-------+
6915  | Alloc | 1st[1st.size() - 1]
6916  +-------+
6917  | |
6918 GetSize() +-------+
6919 
6920 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6921 
6922  0 +-------+
6923  | |
6924  | |
6925  | |
6926  +-------+
6927  | Alloc | 1st[m_1stNullItemsBeginCount]
6928  +-------+
6929  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6930  +-------+
6931  | ... |
6932  +-------+
6933  | Alloc | 1st[1st.size() - 1]
6934  +-------+
6935  | |
6936  | |
6937  | |
6938  +-------+
6939  | Alloc | 2nd[2nd.size() - 1]
6940  +-------+
6941  | ... |
6942  +-------+
6943  | Alloc | 2nd[1]
6944  +-------+
6945  | Alloc | 2nd[0]
6946 GetSize() +-------+
6947 
6948 */
6949 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6950 {
6951  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6952 public:
6953  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6954  virtual ~VmaBlockMetadata_Linear();
6955  virtual void Init(VkDeviceSize size);
6956 
6957  virtual bool Validate() const;
6958  virtual size_t GetAllocationCount() const;
6959  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6960  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6961  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6962 
6963  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6964  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6965 
6966 #if VMA_STATS_STRING_ENABLED
6967  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6968 #endif
6969 
6970  virtual bool CreateAllocationRequest(
6971  uint32_t currentFrameIndex,
6972  uint32_t frameInUseCount,
6973  VkDeviceSize bufferImageGranularity,
6974  VkDeviceSize allocSize,
6975  VkDeviceSize allocAlignment,
6976  bool upperAddress,
6977  VmaSuballocationType allocType,
6978  bool canMakeOtherLost,
6979  uint32_t strategy,
6980  VmaAllocationRequest* pAllocationRequest);
6981 
6982  virtual bool MakeRequestedAllocationsLost(
6983  uint32_t currentFrameIndex,
6984  uint32_t frameInUseCount,
6985  VmaAllocationRequest* pAllocationRequest);
6986 
6987  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6988 
6989  virtual VkResult CheckCorruption(const void* pBlockData);
6990 
6991  virtual void Alloc(
6992  const VmaAllocationRequest& request,
6993  VmaSuballocationType type,
6994  VkDeviceSize allocSize,
6995  VmaAllocation hAllocation);
6996 
6997  virtual void Free(const VmaAllocation allocation);
6998  virtual void FreeAtOffset(VkDeviceSize offset);
6999 
7000 private:
7001  /*
7002  There are two suballocation vectors, used in ping-pong way.
7003  The one with index m_1stVectorIndex is called 1st.
7004  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
7005  2nd can be non-empty only when 1st is not empty.
7006  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
7007  */
7008  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
7009 
7010  enum SECOND_VECTOR_MODE
7011  {
7012  SECOND_VECTOR_EMPTY,
7013  /*
7014  Suballocations in 2nd vector are created later than the ones in 1st, but they
7015  all have smaller offset.
7016  */
7017  SECOND_VECTOR_RING_BUFFER,
7018  /*
7019  Suballocations in 2nd vector are upper side of double stack.
7020  They all have offsets higher than those in 1st vector.
7021  Top of this stack means smaller offsets, but higher indices in this vector.
7022  */
7023  SECOND_VECTOR_DOUBLE_STACK,
7024  };
7025 
7026  VkDeviceSize m_SumFreeSize;
7027  SuballocationVectorType m_Suballocations0, m_Suballocations1;
7028  uint32_t m_1stVectorIndex;
7029  SECOND_VECTOR_MODE m_2ndVectorMode;
7030 
7031  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7032  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7033  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7034  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7035 
7036  // Number of items in 1st vector with hAllocation = null at the beginning.
7037  size_t m_1stNullItemsBeginCount;
7038  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7039  size_t m_1stNullItemsMiddleCount;
7040  // Number of items in 2nd vector with hAllocation = null.
7041  size_t m_2ndNullItemsCount;
7042 
7043  bool ShouldCompact1st() const;
7044  void CleanupAfterFree();
7045 
7046  bool CreateAllocationRequest_LowerAddress(
7047  uint32_t currentFrameIndex,
7048  uint32_t frameInUseCount,
7049  VkDeviceSize bufferImageGranularity,
7050  VkDeviceSize allocSize,
7051  VkDeviceSize allocAlignment,
7052  VmaSuballocationType allocType,
7053  bool canMakeOtherLost,
7054  uint32_t strategy,
7055  VmaAllocationRequest* pAllocationRequest);
7056  bool CreateAllocationRequest_UpperAddress(
7057  uint32_t currentFrameIndex,
7058  uint32_t frameInUseCount,
7059  VkDeviceSize bufferImageGranularity,
7060  VkDeviceSize allocSize,
7061  VkDeviceSize allocAlignment,
7062  VmaSuballocationType allocType,
7063  bool canMakeOtherLost,
7064  uint32_t strategy,
7065  VmaAllocationRequest* pAllocationRequest);
7066 };
7067 
7068 /*
7069 - GetSize() is the original size of allocated memory block.
7070 - m_UsableSize is this size aligned down to a power of two.
7071  All allocations and calculations happen relative to m_UsableSize.
7072 - GetUnusableSize() is the difference between them.
7073  It is reported as separate, unused range, not available for allocations.
7074 
7075 Node at level 0 has size = m_UsableSize.
7076 Each next level contains nodes with size 2 times smaller than current level.
7077 m_LevelCount is the maximum number of levels to use in the current object.
7078 */
7079 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
7080 {
7081  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
7082 public:
7083  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
7084  virtual ~VmaBlockMetadata_Buddy();
7085  virtual void Init(VkDeviceSize size);
7086 
7087  virtual bool Validate() const;
7088  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
7089  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
7090  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
7091  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
7092 
7093  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
7094  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
7095 
7096 #if VMA_STATS_STRING_ENABLED
7097  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
7098 #endif
7099 
7100  virtual bool CreateAllocationRequest(
7101  uint32_t currentFrameIndex,
7102  uint32_t frameInUseCount,
7103  VkDeviceSize bufferImageGranularity,
7104  VkDeviceSize allocSize,
7105  VkDeviceSize allocAlignment,
7106  bool upperAddress,
7107  VmaSuballocationType allocType,
7108  bool canMakeOtherLost,
7109  uint32_t strategy,
7110  VmaAllocationRequest* pAllocationRequest);
7111 
7112  virtual bool MakeRequestedAllocationsLost(
7113  uint32_t currentFrameIndex,
7114  uint32_t frameInUseCount,
7115  VmaAllocationRequest* pAllocationRequest);
7116 
7117  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
7118 
7119  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
7120 
7121  virtual void Alloc(
7122  const VmaAllocationRequest& request,
7123  VmaSuballocationType type,
7124  VkDeviceSize allocSize,
7125  VmaAllocation hAllocation);
7126 
7127  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
7128  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
7129 
7130 private:
7131  static const VkDeviceSize MIN_NODE_SIZE = 32;
7132  static const size_t MAX_LEVELS = 30;
7133 
7134  struct ValidationContext
7135  {
7136  size_t calculatedAllocationCount;
7137  size_t calculatedFreeCount;
7138  VkDeviceSize calculatedSumFreeSize;
7139 
7140  ValidationContext() :
7141  calculatedAllocationCount(0),
7142  calculatedFreeCount(0),
7143  calculatedSumFreeSize(0) { }
7144  };
7145 
7146  struct Node
7147  {
7148  VkDeviceSize offset;
7149  enum TYPE
7150  {
7151  TYPE_FREE,
7152  TYPE_ALLOCATION,
7153  TYPE_SPLIT,
7154  TYPE_COUNT
7155  } type;
7156  Node* parent;
7157  Node* buddy;
7158 
7159  union
7160  {
7161  struct
7162  {
7163  Node* prev;
7164  Node* next;
7165  } free;
7166  struct
7167  {
7168  VmaAllocation alloc;
7169  } allocation;
7170  struct
7171  {
7172  Node* leftChild;
7173  } split;
7174  };
7175  };
7176 
7177  // Size of the memory block aligned down to a power of two.
7178  VkDeviceSize m_UsableSize;
7179  uint32_t m_LevelCount;
7180 
7181  Node* m_Root;
7182  struct {
7183  Node* front;
7184  Node* back;
7185  } m_FreeList[MAX_LEVELS];
7186  // Number of nodes in the tree with type == TYPE_ALLOCATION.
7187  size_t m_AllocationCount;
7188  // Number of nodes in the tree with type == TYPE_FREE.
7189  size_t m_FreeCount;
7190  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
7191  VkDeviceSize m_SumFreeSize;
7192 
7193  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
7194  void DeleteNode(Node* node);
7195  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
7196  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
7197  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
7198  // Alloc passed just for validation. Can be null.
7199  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
7200  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
7201  // Adds node to the front of FreeList at given level.
7202  // node->type must be FREE.
7203  // node->free.prev, next can be undefined.
7204  void AddToFreeListFront(uint32_t level, Node* node);
7205  // Removes node from FreeList at given level.
7206  // node->type must be FREE.
7207  // node->free.prev, next stay untouched.
7208  void RemoveFromFreeList(uint32_t level, Node* node);
7209 
7210 #if VMA_STATS_STRING_ENABLED
7211  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
7212 #endif
7213 };
7214 
7215 /*
7216 Represents a single block of device memory (`VkDeviceMemory`) with all the
7217 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
7218 
7219 Thread-safety: This class must be externally synchronized.
7220 */
7221 class VmaDeviceMemoryBlock
7222 {
7223  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
7224 public:
7225  VmaBlockMetadata* m_pMetadata;
7226 
7227  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
7228 
7229  ~VmaDeviceMemoryBlock()
7230  {
7231  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
7232  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
7233  }
7234 
7235  // Always call after construction.
7236  void Init(
7237  VmaAllocator hAllocator,
7238  VmaPool hParentPool,
7239  uint32_t newMemoryTypeIndex,
7240  VkDeviceMemory newMemory,
7241  VkDeviceSize newSize,
7242  uint32_t id,
7243  uint32_t algorithm);
7244  // Always call before destruction.
7245  void Destroy(VmaAllocator allocator);
7246 
7247  VmaPool GetParentPool() const { return m_hParentPool; }
7248  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
7249  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7250  uint32_t GetId() const { return m_Id; }
7251  void* GetMappedData() const { return m_pMappedData; }
7252 
7253  // Validates all data structures inside this object. If not valid, returns false.
7254  bool Validate() const;
7255 
7256  VkResult CheckCorruption(VmaAllocator hAllocator);
7257 
7258  // ppData can be null.
7259  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
7260  void Unmap(VmaAllocator hAllocator, uint32_t count);
7261 
7262  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7263  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7264 
7265  VkResult BindBufferMemory(
7266  const VmaAllocator hAllocator,
7267  const VmaAllocation hAllocation,
7268  VkDeviceSize allocationLocalOffset,
7269  VkBuffer hBuffer,
7270  const void* pNext);
7271  VkResult BindImageMemory(
7272  const VmaAllocator hAllocator,
7273  const VmaAllocation hAllocation,
7274  VkDeviceSize allocationLocalOffset,
7275  VkImage hImage,
7276  const void* pNext);
7277 
7278 private:
7279  VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
7280  uint32_t m_MemoryTypeIndex;
7281  uint32_t m_Id;
7282  VkDeviceMemory m_hMemory;
7283 
7284  /*
7285  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
7286  Also protects m_MapCount, m_pMappedData.
7287  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
7288  */
7289  VMA_MUTEX m_Mutex;
7290  uint32_t m_MapCount;
7291  void* m_pMappedData;
7292 };
7293 
7294 struct VmaDefragmentationMove
7295 {
7296  size_t srcBlockIndex;
7297  size_t dstBlockIndex;
7298  VkDeviceSize srcOffset;
7299  VkDeviceSize dstOffset;
7300  VkDeviceSize size;
7301  VmaAllocation hAllocation;
7302  VmaDeviceMemoryBlock* pSrcBlock;
7303  VmaDeviceMemoryBlock* pDstBlock;
7304 };
7305 
7306 class VmaDefragmentationAlgorithm;
7307 
7308 /*
7309 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
7310 Vulkan memory type.
7311 
7312 Synchronized internally with a mutex.
7313 */
7314 struct VmaBlockVector
7315 {
7316  VMA_CLASS_NO_COPY(VmaBlockVector)
7317 public:
7318  VmaBlockVector(
7319  VmaAllocator hAllocator,
7320  VmaPool hParentPool,
7321  uint32_t memoryTypeIndex,
7322  VkDeviceSize preferredBlockSize,
7323  size_t minBlockCount,
7324  size_t maxBlockCount,
7325  VkDeviceSize bufferImageGranularity,
7326  uint32_t frameInUseCount,
7327  bool explicitBlockSize,
7328  uint32_t algorithm,
7329  float priority);
7330  ~VmaBlockVector();
7331 
7332  VkResult CreateMinBlocks();
7333 
7334  VmaAllocator GetAllocator() const { return m_hAllocator; }
7335  VmaPool GetParentPool() const { return m_hParentPool; }
7336  bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
7337  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7338  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
7339  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
7340  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
7341  uint32_t GetAlgorithm() const { return m_Algorithm; }
7342 
7343  void GetPoolStats(VmaPoolStats* pStats);
7344 
7345  bool IsEmpty();
7346  bool IsCorruptionDetectionEnabled() const;
7347 
7348  VkResult Allocate(
7349  uint32_t currentFrameIndex,
7350  VkDeviceSize size,
7351  VkDeviceSize alignment,
7352  const VmaAllocationCreateInfo& createInfo,
7353  VmaSuballocationType suballocType,
7354  size_t allocationCount,
7355  VmaAllocation* pAllocations);
7356 
7357  void Free(const VmaAllocation hAllocation);
7358 
7359  // Adds statistics of this BlockVector to pStats.
7360  void AddStats(VmaStats* pStats);
7361 
7362 #if VMA_STATS_STRING_ENABLED
7363  void PrintDetailedMap(class VmaJsonWriter& json);
7364 #endif
7365 
7366  void MakePoolAllocationsLost(
7367  uint32_t currentFrameIndex,
7368  size_t* pLostAllocationCount);
7369  VkResult CheckCorruption();
7370 
7371  // Saves results in pCtx->res.
7372  void Defragment(
7373  class VmaBlockVectorDefragmentationContext* pCtx,
7375  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
7376  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
7377  VkCommandBuffer commandBuffer);
7378  void DefragmentationEnd(
7379  class VmaBlockVectorDefragmentationContext* pCtx,
7380  uint32_t flags,
7381  VmaDefragmentationStats* pStats);
7382 
7383  uint32_t ProcessDefragmentations(
7384  class VmaBlockVectorDefragmentationContext *pCtx,
7385  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
7386 
7387  void CommitDefragmentations(
7388  class VmaBlockVectorDefragmentationContext *pCtx,
7389  VmaDefragmentationStats* pStats);
7390 
7392  // To be used only while the m_Mutex is locked. Used during defragmentation.
7393 
7394  size_t GetBlockCount() const { return m_Blocks.size(); }
7395  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
7396  size_t CalcAllocationCount() const;
7397  bool IsBufferImageGranularityConflictPossible() const;
7398 
7399 private:
7400  friend class VmaDefragmentationAlgorithm_Generic;
7401 
7402  const VmaAllocator m_hAllocator;
7403  const VmaPool m_hParentPool;
7404  const uint32_t m_MemoryTypeIndex;
7405  const VkDeviceSize m_PreferredBlockSize;
7406  const size_t m_MinBlockCount;
7407  const size_t m_MaxBlockCount;
7408  const VkDeviceSize m_BufferImageGranularity;
7409  const uint32_t m_FrameInUseCount;
7410  const bool m_ExplicitBlockSize;
7411  const uint32_t m_Algorithm;
7412  const float m_Priority;
7413  VMA_RW_MUTEX m_Mutex;
7414 
7415  /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7416  a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7417  bool m_HasEmptyBlock;
7418  // Incrementally sorted by sumFreeSize, ascending.
7419  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7420  uint32_t m_NextBlockId;
7421 
7422  VkDeviceSize CalcMaxBlockSize() const;
7423 
7424  // Finds and removes given block from vector.
7425  void Remove(VmaDeviceMemoryBlock* pBlock);
7426 
7427  // Performs single step in sorting m_Blocks. They may not be fully sorted
7428  // after this call.
7429  void IncrementallySortBlocks();
7430 
7431  VkResult AllocatePage(
7432  uint32_t currentFrameIndex,
7433  VkDeviceSize size,
7434  VkDeviceSize alignment,
7435  const VmaAllocationCreateInfo& createInfo,
7436  VmaSuballocationType suballocType,
7437  VmaAllocation* pAllocation);
7438 
7439  // To be used only without CAN_MAKE_OTHER_LOST flag.
7440  VkResult AllocateFromBlock(
7441  VmaDeviceMemoryBlock* pBlock,
7442  uint32_t currentFrameIndex,
7443  VkDeviceSize size,
7444  VkDeviceSize alignment,
7445  VmaAllocationCreateFlags allocFlags,
7446  void* pUserData,
7447  VmaSuballocationType suballocType,
7448  uint32_t strategy,
7449  VmaAllocation* pAllocation);
7450 
7451  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7452 
7453  // Saves result to pCtx->res.
7454  void ApplyDefragmentationMovesCpu(
7455  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7456  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7457  // Saves result to pCtx->res.
7458  void ApplyDefragmentationMovesGpu(
7459  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7460  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7461  VkCommandBuffer commandBuffer);
7462 
7463  /*
7464  Used during defragmentation. pDefragmentationStats is optional. It's in/out
7465  - updated with new data.
7466  */
7467  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7468 
7469  void UpdateHasEmptyBlock();
7470 };
7471 
7472 struct VmaPool_T
7473 {
7474  VMA_CLASS_NO_COPY(VmaPool_T)
7475 public:
7476  VmaBlockVector m_BlockVector;
7477 
7478  VmaPool_T(
7479  VmaAllocator hAllocator,
7480  const VmaPoolCreateInfo& createInfo,
7481  VkDeviceSize preferredBlockSize);
7482  ~VmaPool_T();
7483 
7484  uint32_t GetId() const { return m_Id; }
7485  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7486 
7487  const char* GetName() const { return m_Name; }
7488  void SetName(const char* pName);
7489 
7490 #if VMA_STATS_STRING_ENABLED
7491  //void PrintDetailedMap(class VmaStringBuilder& sb);
7492 #endif
7493 
7494 private:
7495  uint32_t m_Id;
7496  char* m_Name;
7497  VmaPool_T* m_PrevPool = VMA_NULL;
7498  VmaPool_T* m_NextPool = VMA_NULL;
7499  friend struct VmaPoolListItemTraits;
7500 };
7501 
7502 struct VmaPoolListItemTraits
7503 {
7504  typedef VmaPool_T ItemType;
7505  static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
7506  static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
7507  static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
7508  static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
7509 };
7510 
7511 /*
7512 Performs defragmentation:
7513 
7514 - Updates `pBlockVector->m_pMetadata`.
7515 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7516 - Does not move actual data, only returns requested moves as `moves`.
7517 */
7518 class VmaDefragmentationAlgorithm
7519 {
7520  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7521 public:
7522  VmaDefragmentationAlgorithm(
7523  VmaAllocator hAllocator,
7524  VmaBlockVector* pBlockVector,
7525  uint32_t currentFrameIndex) :
7526  m_hAllocator(hAllocator),
7527  m_pBlockVector(pBlockVector),
7528  m_CurrentFrameIndex(currentFrameIndex)
7529  {
7530  }
7531  virtual ~VmaDefragmentationAlgorithm()
7532  {
7533  }
7534 
7535  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7536  virtual void AddAll() = 0;
7537 
7538  virtual VkResult Defragment(
7539  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7540  VkDeviceSize maxBytesToMove,
7541  uint32_t maxAllocationsToMove,
7542  VmaDefragmentationFlags flags) = 0;
7543 
7544  virtual VkDeviceSize GetBytesMoved() const = 0;
7545  virtual uint32_t GetAllocationsMoved() const = 0;
7546 
7547 protected:
7548  VmaAllocator const m_hAllocator;
7549  VmaBlockVector* const m_pBlockVector;
7550  const uint32_t m_CurrentFrameIndex;
7551 
7552  struct AllocationInfo
7553  {
7554  VmaAllocation m_hAllocation;
7555  VkBool32* m_pChanged;
7556 
7557  AllocationInfo() :
7558  m_hAllocation(VK_NULL_HANDLE),
7559  m_pChanged(VMA_NULL)
7560  {
7561  }
7562  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7563  m_hAllocation(hAlloc),
7564  m_pChanged(pChanged)
7565  {
7566  }
7567  };
7568 };
7569 
7570 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7571 {
7572  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7573 public:
7574  VmaDefragmentationAlgorithm_Generic(
7575  VmaAllocator hAllocator,
7576  VmaBlockVector* pBlockVector,
7577  uint32_t currentFrameIndex,
7578  bool overlappingMoveSupported);
7579  virtual ~VmaDefragmentationAlgorithm_Generic();
7580 
7581  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7582  virtual void AddAll() { m_AllAllocations = true; }
7583 
7584  virtual VkResult Defragment(
7585  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7586  VkDeviceSize maxBytesToMove,
7587  uint32_t maxAllocationsToMove,
7588  VmaDefragmentationFlags flags);
7589 
7590  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7591  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7592 
7593 private:
7594  uint32_t m_AllocationCount;
7595  bool m_AllAllocations;
7596 
7597  VkDeviceSize m_BytesMoved;
7598  uint32_t m_AllocationsMoved;
7599 
7600  struct AllocationInfoSizeGreater
7601  {
7602  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7603  {
7604  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7605  }
7606  };
7607 
7608  struct AllocationInfoOffsetGreater
7609  {
7610  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7611  {
7612  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7613  }
7614  };
7615 
7616  struct BlockInfo
7617  {
7618  size_t m_OriginalBlockIndex;
7619  VmaDeviceMemoryBlock* m_pBlock;
7620  bool m_HasNonMovableAllocations;
7621  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7622 
7623  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7624  m_OriginalBlockIndex(SIZE_MAX),
7625  m_pBlock(VMA_NULL),
7626  m_HasNonMovableAllocations(true),
7627  m_Allocations(pAllocationCallbacks)
7628  {
7629  }
7630 
7631  void CalcHasNonMovableAllocations()
7632  {
7633  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7634  const size_t defragmentAllocCount = m_Allocations.size();
7635  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7636  }
7637 
7638  void SortAllocationsBySizeDescending()
7639  {
7640  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7641  }
7642 
7643  void SortAllocationsByOffsetDescending()
7644  {
7645  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7646  }
7647  };
7648 
7649  struct BlockPointerLess
7650  {
7651  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7652  {
7653  return pLhsBlockInfo->m_pBlock < pRhsBlock;
7654  }
7655  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7656  {
7657  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7658  }
7659  };
7660 
7661  // 1. Blocks with some non-movable allocations go first.
7662  // 2. Blocks with smaller sumFreeSize go first.
7663  struct BlockInfoCompareMoveDestination
7664  {
7665  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7666  {
7667  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7668  {
7669  return true;
7670  }
7671  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7672  {
7673  return false;
7674  }
7675  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7676  {
7677  return true;
7678  }
7679  return false;
7680  }
7681  };
7682 
7683  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7684  BlockInfoVector m_Blocks;
7685 
7686  VkResult DefragmentRound(
7687  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7688  VkDeviceSize maxBytesToMove,
7689  uint32_t maxAllocationsToMove,
7690  bool freeOldAllocations);
7691 
7692  size_t CalcBlocksWithNonMovableCount() const;
7693 
7694  static bool MoveMakesSense(
7695  size_t dstBlockIndex, VkDeviceSize dstOffset,
7696  size_t srcBlockIndex, VkDeviceSize srcOffset);
7697 };
7698 
7699 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7700 {
7701  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7702 public:
7703  VmaDefragmentationAlgorithm_Fast(
7704  VmaAllocator hAllocator,
7705  VmaBlockVector* pBlockVector,
7706  uint32_t currentFrameIndex,
7707  bool overlappingMoveSupported);
7708  virtual ~VmaDefragmentationAlgorithm_Fast();
7709 
7710  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7711  virtual void AddAll() { m_AllAllocations = true; }
7712 
7713  virtual VkResult Defragment(
7714  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7715  VkDeviceSize maxBytesToMove,
7716  uint32_t maxAllocationsToMove,
7717  VmaDefragmentationFlags flags);
7718 
7719  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7720  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7721 
7722 private:
7723  struct BlockInfo
7724  {
7725  size_t origBlockIndex;
7726  };
7727 
7728  class FreeSpaceDatabase
7729  {
7730  public:
7731  FreeSpaceDatabase()
7732  {
7733  FreeSpace s = {};
7734  s.blockInfoIndex = SIZE_MAX;
7735  for(size_t i = 0; i < MAX_COUNT; ++i)
7736  {
7737  m_FreeSpaces[i] = s;
7738  }
7739  }
7740 
7741  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7742  {
7743  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7744  {
7745  return;
7746  }
7747 
7748  // Find first invalid or the smallest structure.
7749  size_t bestIndex = SIZE_MAX;
7750  for(size_t i = 0; i < MAX_COUNT; ++i)
7751  {
7752  // Empty structure.
7753  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7754  {
7755  bestIndex = i;
7756  break;
7757  }
7758  if(m_FreeSpaces[i].size < size &&
7759  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7760  {
7761  bestIndex = i;
7762  }
7763  }
7764 
7765  if(bestIndex != SIZE_MAX)
7766  {
7767  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7768  m_FreeSpaces[bestIndex].offset = offset;
7769  m_FreeSpaces[bestIndex].size = size;
7770  }
7771  }
7772 
7773  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7774  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7775  {
7776  size_t bestIndex = SIZE_MAX;
7777  VkDeviceSize bestFreeSpaceAfter = 0;
7778  for(size_t i = 0; i < MAX_COUNT; ++i)
7779  {
7780  // Structure is valid.
7781  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7782  {
7783  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7784  // Allocation fits into this structure.
7785  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7786  {
7787  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7788  (dstOffset + size);
7789  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7790  {
7791  bestIndex = i;
7792  bestFreeSpaceAfter = freeSpaceAfter;
7793  }
7794  }
7795  }
7796  }
7797 
7798  if(bestIndex != SIZE_MAX)
7799  {
7800  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7801  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7802 
7803  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7804  {
7805  // Leave this structure for remaining empty space.
7806  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7807  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7808  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7809  }
7810  else
7811  {
7812  // This structure becomes invalid.
7813  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7814  }
7815 
7816  return true;
7817  }
7818 
7819  return false;
7820  }
7821 
7822  private:
7823  static const size_t MAX_COUNT = 4;
7824 
7825  struct FreeSpace
7826  {
7827  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7828  VkDeviceSize offset;
7829  VkDeviceSize size;
7830  } m_FreeSpaces[MAX_COUNT];
7831  };
7832 
7833  const bool m_OverlappingMoveSupported;
7834 
7835  uint32_t m_AllocationCount;
7836  bool m_AllAllocations;
7837 
7838  VkDeviceSize m_BytesMoved;
7839  uint32_t m_AllocationsMoved;
7840 
7841  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7842 
7843  void PreprocessMetadata();
7844  void PostprocessMetadata();
7845  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7846 };
7847 
7848 struct VmaBlockDefragmentationContext
7849 {
7850  enum BLOCK_FLAG
7851  {
7852  BLOCK_FLAG_USED = 0x00000001,
7853  };
7854  uint32_t flags;
7855  VkBuffer hBuffer;
7856 };
7857 
7858 class VmaBlockVectorDefragmentationContext
7859 {
7860  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7861 public:
7862  VkResult res;
7863  bool mutexLocked;
7864  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7865  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7866  uint32_t defragmentationMovesProcessed;
7867  uint32_t defragmentationMovesCommitted;
7868  bool hasDefragmentationPlan;
7869 
7870  VmaBlockVectorDefragmentationContext(
7871  VmaAllocator hAllocator,
7872  VmaPool hCustomPool, // Optional.
7873  VmaBlockVector* pBlockVector,
7874  uint32_t currFrameIndex);
7875  ~VmaBlockVectorDefragmentationContext();
7876 
7877  VmaPool GetCustomPool() const { return m_hCustomPool; }
7878  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7879  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7880 
7881  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7882  void AddAll() { m_AllAllocations = true; }
7883 
7884  void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7885 
7886 private:
7887  const VmaAllocator m_hAllocator;
7888  // Null if not from custom pool.
7889  const VmaPool m_hCustomPool;
7890  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7891  VmaBlockVector* const m_pBlockVector;
7892  const uint32_t m_CurrFrameIndex;
7893  // Owner of this object.
7894  VmaDefragmentationAlgorithm* m_pAlgorithm;
7895 
7896  struct AllocInfo
7897  {
7898  VmaAllocation hAlloc;
7899  VkBool32* pChanged;
7900  };
7901  // Used between constructor and Begin.
7902  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7903  bool m_AllAllocations;
7904 };
7905 
7906 struct VmaDefragmentationContext_T
7907 {
7908 private:
7909  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7910 public:
7911  VmaDefragmentationContext_T(
7912  VmaAllocator hAllocator,
7913  uint32_t currFrameIndex,
7914  uint32_t flags,
7915  VmaDefragmentationStats* pStats);
7916  ~VmaDefragmentationContext_T();
7917 
7918  void AddPools(uint32_t poolCount, const VmaPool* pPools);
7919  void AddAllocations(
7920  uint32_t allocationCount,
7921  const VmaAllocation* pAllocations,
7922  VkBool32* pAllocationsChanged);
7923 
7924  /*
7925  Returns:
7926  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7927  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7928  - Negative value if error occurred and object can be destroyed immediately.
7929  */
7930  VkResult Defragment(
7931  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7932  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7933  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7934 
7935  VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7936  VkResult DefragmentPassEnd();
7937 
7938 private:
7939  const VmaAllocator m_hAllocator;
7940  const uint32_t m_CurrFrameIndex;
7941  const uint32_t m_Flags;
7942  VmaDefragmentationStats* const m_pStats;
7943 
7944  VkDeviceSize m_MaxCpuBytesToMove;
7945  uint32_t m_MaxCpuAllocationsToMove;
7946  VkDeviceSize m_MaxGpuBytesToMove;
7947  uint32_t m_MaxGpuAllocationsToMove;
7948 
7949  // Owner of these objects.
7950  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7951  // Owner of these objects.
7952  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7953 };
7954 
7955 #if VMA_RECORDING_ENABLED
7956 
7957 class VmaRecorder
7958 {
7959 public:
7960  VmaRecorder();
7961  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7962  void WriteConfiguration(
7963  const VkPhysicalDeviceProperties& devProps,
7964  const VkPhysicalDeviceMemoryProperties& memProps,
7965  uint32_t vulkanApiVersion,
7966  bool dedicatedAllocationExtensionEnabled,
7967  bool bindMemory2ExtensionEnabled,
7968  bool memoryBudgetExtensionEnabled,
7969  bool deviceCoherentMemoryExtensionEnabled);
7970  ~VmaRecorder();
7971 
7972  void RecordCreateAllocator(uint32_t frameIndex);
7973  void RecordDestroyAllocator(uint32_t frameIndex);
7974  void RecordCreatePool(uint32_t frameIndex,
7975  const VmaPoolCreateInfo& createInfo,
7976  VmaPool pool);
7977  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7978  void RecordAllocateMemory(uint32_t frameIndex,
7979  const VkMemoryRequirements& vkMemReq,
7980  const VmaAllocationCreateInfo& createInfo,
7981  VmaAllocation allocation);
7982  void RecordAllocateMemoryPages(uint32_t frameIndex,
7983  const VkMemoryRequirements& vkMemReq,
7984  const VmaAllocationCreateInfo& createInfo,
7985  uint64_t allocationCount,
7986  const VmaAllocation* pAllocations);
7987  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7988  const VkMemoryRequirements& vkMemReq,
7989  bool requiresDedicatedAllocation,
7990  bool prefersDedicatedAllocation,
7991  const VmaAllocationCreateInfo& createInfo,
7992  VmaAllocation allocation);
7993  void RecordAllocateMemoryForImage(uint32_t frameIndex,
7994  const VkMemoryRequirements& vkMemReq,
7995  bool requiresDedicatedAllocation,
7996  bool prefersDedicatedAllocation,
7997  const VmaAllocationCreateInfo& createInfo,
7998  VmaAllocation allocation);
7999  void RecordFreeMemory(uint32_t frameIndex,
8000  VmaAllocation allocation);
8001  void RecordFreeMemoryPages(uint32_t frameIndex,
8002  uint64_t allocationCount,
8003  const VmaAllocation* pAllocations);
8004  void RecordSetAllocationUserData(uint32_t frameIndex,
8005  VmaAllocation allocation,
8006  const void* pUserData);
8007  void RecordCreateLostAllocation(uint32_t frameIndex,
8008  VmaAllocation allocation);
8009  void RecordMapMemory(uint32_t frameIndex,
8010  VmaAllocation allocation);
8011  void RecordUnmapMemory(uint32_t frameIndex,
8012  VmaAllocation allocation);
8013  void RecordFlushAllocation(uint32_t frameIndex,
8014  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
8015  void RecordInvalidateAllocation(uint32_t frameIndex,
8016  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
8017  void RecordCreateBuffer(uint32_t frameIndex,
8018  const VkBufferCreateInfo& bufCreateInfo,
8019  const VmaAllocationCreateInfo& allocCreateInfo,
8020  VmaAllocation allocation);
8021  void RecordCreateImage(uint32_t frameIndex,
8022  const VkImageCreateInfo& imageCreateInfo,
8023  const VmaAllocationCreateInfo& allocCreateInfo,
8024  VmaAllocation allocation);
8025  void RecordDestroyBuffer(uint32_t frameIndex,
8026  VmaAllocation allocation);
8027  void RecordDestroyImage(uint32_t frameIndex,
8028  VmaAllocation allocation);
8029  void RecordTouchAllocation(uint32_t frameIndex,
8030  VmaAllocation allocation);
8031  void RecordGetAllocationInfo(uint32_t frameIndex,
8032  VmaAllocation allocation);
8033  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
8034  VmaPool pool);
8035  void RecordDefragmentationBegin(uint32_t frameIndex,
8036  const VmaDefragmentationInfo2& info,
8038  void RecordDefragmentationEnd(uint32_t frameIndex,
8040  void RecordSetPoolName(uint32_t frameIndex,
8041  VmaPool pool,
8042  const char* name);
8043 
8044 private:
8045  struct CallParams
8046  {
8047  uint32_t threadId;
8048  double time;
8049  };
8050 
8051  class UserDataString
8052  {
8053  public:
8054  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
8055  const char* GetString() const { return m_Str; }
8056 
8057  private:
8058  char m_PtrStr[17];
8059  const char* m_Str;
8060  };
8061 
8062  bool m_UseMutex;
8063  VmaRecordFlags m_Flags;
8064  FILE* m_File;
8065  VMA_MUTEX m_FileMutex;
8066  std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
8067 
8068  void GetBasicParams(CallParams& outParams);
8069 
8070  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
8071  template<typename T>
8072  void PrintPointerList(uint64_t count, const T* pItems)
8073  {
8074  if(count)
8075  {
8076  fprintf(m_File, "%p", pItems[0]);
8077  for(uint64_t i = 1; i < count; ++i)
8078  {
8079  fprintf(m_File, " %p", pItems[i]);
8080  }
8081  }
8082  }
8083 
8084  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
8085  void Flush();
8086 };
8087 
8088 #endif // #if VMA_RECORDING_ENABLED
8089 
8090 /*
8091 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
8092 */
8093 class VmaAllocationObjectAllocator
8094 {
8095  VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
8096 public:
8097  VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
8098 
8099  template<typename... Types> VmaAllocation Allocate(Types... args);
8100  void Free(VmaAllocation hAlloc);
8101 
8102 private:
8103  VMA_MUTEX m_Mutex;
8104  VmaPoolAllocator<VmaAllocation_T> m_Allocator;
8105 };
8106 
8107 struct VmaCurrentBudgetData
8108 {
8109  VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
8110  VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
8111 
8112 #if VMA_MEMORY_BUDGET
8113  VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
8114  VMA_RW_MUTEX m_BudgetMutex;
8115  uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
8116  uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
8117  uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
8118 #endif // #if VMA_MEMORY_BUDGET
8119 
8120  VmaCurrentBudgetData()
8121  {
8122  for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
8123  {
8124  m_BlockBytes[heapIndex] = 0;
8125  m_AllocationBytes[heapIndex] = 0;
8126 #if VMA_MEMORY_BUDGET
8127  m_VulkanUsage[heapIndex] = 0;
8128  m_VulkanBudget[heapIndex] = 0;
8129  m_BlockBytesAtBudgetFetch[heapIndex] = 0;
8130 #endif
8131  }
8132 
8133 #if VMA_MEMORY_BUDGET
8134  m_OperationsSinceBudgetFetch = 0;
8135 #endif
8136  }
8137 
8138  void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8139  {
8140  m_AllocationBytes[heapIndex] += allocationSize;
8141 #if VMA_MEMORY_BUDGET
8142  ++m_OperationsSinceBudgetFetch;
8143 #endif
8144  }
8145 
8146  void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8147  {
8148  VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
8149  m_AllocationBytes[heapIndex] -= allocationSize;
8150 #if VMA_MEMORY_BUDGET
8151  ++m_OperationsSinceBudgetFetch;
8152 #endif
8153  }
8154 };
8155 
8156 // Main allocator object.
8157 struct VmaAllocator_T
8158 {
8159  VMA_CLASS_NO_COPY(VmaAllocator_T)
8160 public:
8161  bool m_UseMutex;
8162  uint32_t m_VulkanApiVersion;
8163  bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8164  bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8165  bool m_UseExtMemoryBudget;
8166  bool m_UseAmdDeviceCoherentMemory;
8167  bool m_UseKhrBufferDeviceAddress;
8168  bool m_UseExtMemoryPriority;
8169  VkDevice m_hDevice;
8170  VkInstance m_hInstance;
8171  bool m_AllocationCallbacksSpecified;
8172  VkAllocationCallbacks m_AllocationCallbacks;
8173  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
8174  VmaAllocationObjectAllocator m_AllocationObjectAllocator;
8175 
8176  // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
8177  uint32_t m_HeapSizeLimitMask;
8178 
8179  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
8180  VkPhysicalDeviceMemoryProperties m_MemProps;
8181 
8182  // Default pools.
8183  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
8184 
8185  typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
8186  DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
8187  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
8188 
8189  VmaCurrentBudgetData m_Budget;
8190  VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
8191 
8192  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
8193  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
8194  ~VmaAllocator_T();
8195 
8196  const VkAllocationCallbacks* GetAllocationCallbacks() const
8197  {
8198  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
8199  }
8200  const VmaVulkanFunctions& GetVulkanFunctions() const
8201  {
8202  return m_VulkanFunctions;
8203  }
8204 
8205  VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
8206 
8207  VkDeviceSize GetBufferImageGranularity() const
8208  {
8209  return VMA_MAX(
8210  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
8211  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
8212  }
8213 
8214  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
8215  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
8216 
8217  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
8218  {
8219  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
8220  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
8221  }
8222  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
8223  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
8224  {
8225  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
8226  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8227  }
8228  // Minimum alignment for all allocations in specific memory type.
8229  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
8230  {
8231  return IsMemoryTypeNonCoherent(memTypeIndex) ?
8232  VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
8233  (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
8234  }
8235 
8236  bool IsIntegratedGpu() const
8237  {
8238  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
8239  }
8240 
8241  uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
8242 
8243 #if VMA_RECORDING_ENABLED
8244  VmaRecorder* GetRecorder() const { return m_pRecorder; }
8245 #endif
8246 
8247  void GetBufferMemoryRequirements(
8248  VkBuffer hBuffer,
8249  VkMemoryRequirements& memReq,
8250  bool& requiresDedicatedAllocation,
8251  bool& prefersDedicatedAllocation) const;
8252  void GetImageMemoryRequirements(
8253  VkImage hImage,
8254  VkMemoryRequirements& memReq,
8255  bool& requiresDedicatedAllocation,
8256  bool& prefersDedicatedAllocation) const;
8257 
8258  // Main allocation function.
8259  VkResult AllocateMemory(
8260  const VkMemoryRequirements& vkMemReq,
8261  bool requiresDedicatedAllocation,
8262  bool prefersDedicatedAllocation,
8263  VkBuffer dedicatedBuffer,
8264  VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
8265  VkImage dedicatedImage,
8266  const VmaAllocationCreateInfo& createInfo,
8267  VmaSuballocationType suballocType,
8268  size_t allocationCount,
8269  VmaAllocation* pAllocations);
8270 
8271  // Main deallocation function.
8272  void FreeMemory(
8273  size_t allocationCount,
8274  const VmaAllocation* pAllocations);
8275 
8276  void CalculateStats(VmaStats* pStats);
8277 
8278  void GetBudget(
8279  VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
8280 
8281 #if VMA_STATS_STRING_ENABLED
8282  void PrintDetailedMap(class VmaJsonWriter& json);
8283 #endif
8284 
8285  VkResult DefragmentationBegin(
8286  const VmaDefragmentationInfo2& info,
8287  VmaDefragmentationStats* pStats,
8288  VmaDefragmentationContext* pContext);
8289  VkResult DefragmentationEnd(
8290  VmaDefragmentationContext context);
8291 
8292  VkResult DefragmentationPassBegin(
8294  VmaDefragmentationContext context);
8295  VkResult DefragmentationPassEnd(
8296  VmaDefragmentationContext context);
8297 
8298  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
8299  bool TouchAllocation(VmaAllocation hAllocation);
8300 
8301  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
8302  void DestroyPool(VmaPool pool);
8303  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
8304 
8305  void SetCurrentFrameIndex(uint32_t frameIndex);
8306  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
8307 
8308  void MakePoolAllocationsLost(
8309  VmaPool hPool,
8310  size_t* pLostAllocationCount);
8311  VkResult CheckPoolCorruption(VmaPool hPool);
8312  VkResult CheckCorruption(uint32_t memoryTypeBits);
8313 
8314  void CreateLostAllocation(VmaAllocation* pAllocation);
8315 
8316  // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
8317  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
8318  // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
8319  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
8320  // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
8321  VkResult BindVulkanBuffer(
8322  VkDeviceMemory memory,
8323  VkDeviceSize memoryOffset,
8324  VkBuffer buffer,
8325  const void* pNext);
8326  // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
8327  VkResult BindVulkanImage(
8328  VkDeviceMemory memory,
8329  VkDeviceSize memoryOffset,
8330  VkImage image,
8331  const void* pNext);
8332 
8333  VkResult Map(VmaAllocation hAllocation, void** ppData);
8334  void Unmap(VmaAllocation hAllocation);
8335 
8336  VkResult BindBufferMemory(
8337  VmaAllocation hAllocation,
8338  VkDeviceSize allocationLocalOffset,
8339  VkBuffer hBuffer,
8340  const void* pNext);
8341  VkResult BindImageMemory(
8342  VmaAllocation hAllocation,
8343  VkDeviceSize allocationLocalOffset,
8344  VkImage hImage,
8345  const void* pNext);
8346 
8347  VkResult FlushOrInvalidateAllocation(
8348  VmaAllocation hAllocation,
8349  VkDeviceSize offset, VkDeviceSize size,
8350  VMA_CACHE_OPERATION op);
8351  VkResult FlushOrInvalidateAllocations(
8352  uint32_t allocationCount,
8353  const VmaAllocation* allocations,
8354  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
8355  VMA_CACHE_OPERATION op);
8356 
8357  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
8358 
8359  /*
8360  Returns bit mask of memory types that can support defragmentation on GPU as
8361  they support creation of required buffer for copy operations.
8362  */
8363  uint32_t GetGpuDefragmentationMemoryTypeBits();
8364 
8365 private:
8366  VkDeviceSize m_PreferredLargeHeapBlockSize;
8367 
8368  VkPhysicalDevice m_PhysicalDevice;
8369  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
8370  VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
8371 
8372  VMA_RW_MUTEX m_PoolsMutex;
8373  typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
8374  // Protected by m_PoolsMutex.
8375  PoolList m_Pools;
8376  uint32_t m_NextPoolId;
8377 
8378  VmaVulkanFunctions m_VulkanFunctions;
8379 
8380  // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
8381  uint32_t m_GlobalMemoryTypeBits;
8382 
8383 #if VMA_RECORDING_ENABLED
8384  VmaRecorder* m_pRecorder;
8385 #endif
8386 
8387  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
8388 
8389 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8390  void ImportVulkanFunctions_Static();
8391 #endif
8392 
8393  void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8394 
8395 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8396  void ImportVulkanFunctions_Dynamic();
8397 #endif
8398 
8399  void ValidateVulkanFunctions();
8400 
8401  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8402 
8403  VkResult AllocateMemoryOfType(
8404  VkDeviceSize size,
8405  VkDeviceSize alignment,
8406  bool dedicatedAllocation,
8407  VkBuffer dedicatedBuffer,
8408  VkBufferUsageFlags dedicatedBufferUsage,
8409  VkImage dedicatedImage,
8410  const VmaAllocationCreateInfo& createInfo,
8411  uint32_t memTypeIndex,
8412  VmaSuballocationType suballocType,
8413  size_t allocationCount,
8414  VmaAllocation* pAllocations);
8415 
8416  // Helper function only to be used inside AllocateDedicatedMemory.
8417  VkResult AllocateDedicatedMemoryPage(
8418  VkDeviceSize size,
8419  VmaSuballocationType suballocType,
8420  uint32_t memTypeIndex,
8421  const VkMemoryAllocateInfo& allocInfo,
8422  bool map,
8423  bool isUserDataString,
8424  void* pUserData,
8425  VmaAllocation* pAllocation);
8426 
8427  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8428  VkResult AllocateDedicatedMemory(
8429  VkDeviceSize size,
8430  VmaSuballocationType suballocType,
8431  uint32_t memTypeIndex,
8432  bool withinBudget,
8433  bool map,
8434  bool isUserDataString,
8435  void* pUserData,
8436  float priority,
8437  VkBuffer dedicatedBuffer,
8438  VkBufferUsageFlags dedicatedBufferUsage,
8439  VkImage dedicatedImage,
8440  size_t allocationCount,
8441  VmaAllocation* pAllocations);
8442 
8443  void FreeDedicatedMemory(const VmaAllocation allocation);
8444 
8445  /*
8446  Calculates and returns bit mask of memory types that can support defragmentation
8447  on GPU as they support creation of required buffer for copy operations.
8448  */
8449  uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8450 
8451  uint32_t CalculateGlobalMemoryTypeBits() const;
8452 
8453  bool GetFlushOrInvalidateRange(
8454  VmaAllocation allocation,
8455  VkDeviceSize offset, VkDeviceSize size,
8456  VkMappedMemoryRange& outRange) const;
8457 
8458 #if VMA_MEMORY_BUDGET
8459  void UpdateVulkanBudget();
8460 #endif // #if VMA_MEMORY_BUDGET
8461 };
8462 
8464 // Memory allocation #2 after VmaAllocator_T definition
8465 
8466 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8467 {
8468  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8469 }
8470 
8471 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8472 {
8473  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8474 }
8475 
8476 template<typename T>
8477 static T* VmaAllocate(VmaAllocator hAllocator)
8478 {
8479  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8480 }
8481 
8482 template<typename T>
8483 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8484 {
8485  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8486 }
8487 
8488 template<typename T>
8489 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8490 {
8491  if(ptr != VMA_NULL)
8492  {
8493  ptr->~T();
8494  VmaFree(hAllocator, ptr);
8495  }
8496 }
8497 
8498 template<typename T>
8499 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8500 {
8501  if(ptr != VMA_NULL)
8502  {
8503  for(size_t i = count; i--; )
8504  ptr[i].~T();
8505  VmaFree(hAllocator, ptr);
8506  }
8507 }
8508 
8510 // VmaStringBuilder
8511 
8512 #if VMA_STATS_STRING_ENABLED
8513 
8514 class VmaStringBuilder
8515 {
8516 public:
8517  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8518  size_t GetLength() const { return m_Data.size(); }
8519  const char* GetData() const { return m_Data.data(); }
8520 
8521  void Add(char ch) { m_Data.push_back(ch); }
8522  void Add(const char* pStr);
8523  void AddNewLine() { Add('\n'); }
8524  void AddNumber(uint32_t num);
8525  void AddNumber(uint64_t num);
8526  void AddPointer(const void* ptr);
8527 
8528 private:
8529  VmaVector< char, VmaStlAllocator<char> > m_Data;
8530 };
8531 
8532 void VmaStringBuilder::Add(const char* pStr)
8533 {
8534  const size_t strLen = strlen(pStr);
8535  if(strLen > 0)
8536  {
8537  const size_t oldCount = m_Data.size();
8538  m_Data.resize(oldCount + strLen);
8539  memcpy(m_Data.data() + oldCount, pStr, strLen);
8540  }
8541 }
8542 
8543 void VmaStringBuilder::AddNumber(uint32_t num)
8544 {
8545  char buf[11];
8546  buf[10] = '\0';
8547  char *p = &buf[10];
8548  do
8549  {
8550  *--p = '0' + (num % 10);
8551  num /= 10;
8552  }
8553  while(num);
8554  Add(p);
8555 }
8556 
8557 void VmaStringBuilder::AddNumber(uint64_t num)
8558 {
8559  char buf[21];
8560  buf[20] = '\0';
8561  char *p = &buf[20];
8562  do
8563  {
8564  *--p = '0' + (num % 10);
8565  num /= 10;
8566  }
8567  while(num);
8568  Add(p);
8569 }
8570 
8571 void VmaStringBuilder::AddPointer(const void* ptr)
8572 {
8573  char buf[21];
8574  VmaPtrToStr(buf, sizeof(buf), ptr);
8575  Add(buf);
8576 }
8577 
8578 #endif // #if VMA_STATS_STRING_ENABLED
8579 
8581 // VmaJsonWriter
8582 
8583 #if VMA_STATS_STRING_ENABLED
8584 
8585 class VmaJsonWriter
8586 {
8587  VMA_CLASS_NO_COPY(VmaJsonWriter)
8588 public:
8589  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8590  ~VmaJsonWriter();
8591 
8592  void BeginObject(bool singleLine = false);
8593  void EndObject();
8594 
8595  void BeginArray(bool singleLine = false);
8596  void EndArray();
8597 
8598  void WriteString(const char* pStr);
8599  void BeginString(const char* pStr = VMA_NULL);
8600  void ContinueString(const char* pStr);
8601  void ContinueString(uint32_t n);
8602  void ContinueString(uint64_t n);
8603  void ContinueString_Pointer(const void* ptr);
8604  void EndString(const char* pStr = VMA_NULL);
8605 
8606  void WriteNumber(uint32_t n);
8607  void WriteNumber(uint64_t n);
8608  void WriteBool(bool b);
8609  void WriteNull();
8610 
8611 private:
8612  static const char* const INDENT;
8613 
8614  enum COLLECTION_TYPE
8615  {
8616  COLLECTION_TYPE_OBJECT,
8617  COLLECTION_TYPE_ARRAY,
8618  };
8619  struct StackItem
8620  {
8621  COLLECTION_TYPE type;
8622  uint32_t valueCount;
8623  bool singleLineMode;
8624  };
8625 
8626  VmaStringBuilder& m_SB;
8627  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8628  bool m_InsideString;
8629 
8630  void BeginValue(bool isString);
8631  void WriteIndent(bool oneLess = false);
8632 };
8633 
8634 const char* const VmaJsonWriter::INDENT = " ";
8635 
8636 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8637  m_SB(sb),
8638  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8639  m_InsideString(false)
8640 {
8641 }
8642 
8643 VmaJsonWriter::~VmaJsonWriter()
8644 {
8645  VMA_ASSERT(!m_InsideString);
8646  VMA_ASSERT(m_Stack.empty());
8647 }
8648 
8649 void VmaJsonWriter::BeginObject(bool singleLine)
8650 {
8651  VMA_ASSERT(!m_InsideString);
8652 
8653  BeginValue(false);
8654  m_SB.Add('{');
8655 
8656  StackItem item;
8657  item.type = COLLECTION_TYPE_OBJECT;
8658  item.valueCount = 0;
8659  item.singleLineMode = singleLine;
8660  m_Stack.push_back(item);
8661 }
8662 
8663 void VmaJsonWriter::EndObject()
8664 {
8665  VMA_ASSERT(!m_InsideString);
8666 
8667  WriteIndent(true);
8668  m_SB.Add('}');
8669 
8670  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8671  m_Stack.pop_back();
8672 }
8673 
8674 void VmaJsonWriter::BeginArray(bool singleLine)
8675 {
8676  VMA_ASSERT(!m_InsideString);
8677 
8678  BeginValue(false);
8679  m_SB.Add('[');
8680 
8681  StackItem item;
8682  item.type = COLLECTION_TYPE_ARRAY;
8683  item.valueCount = 0;
8684  item.singleLineMode = singleLine;
8685  m_Stack.push_back(item);
8686 }
8687 
8688 void VmaJsonWriter::EndArray()
8689 {
8690  VMA_ASSERT(!m_InsideString);
8691 
8692  WriteIndent(true);
8693  m_SB.Add(']');
8694 
8695  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8696  m_Stack.pop_back();
8697 }
8698 
8699 void VmaJsonWriter::WriteString(const char* pStr)
8700 {
8701  BeginString(pStr);
8702  EndString();
8703 }
8704 
8705 void VmaJsonWriter::BeginString(const char* pStr)
8706 {
8707  VMA_ASSERT(!m_InsideString);
8708 
8709  BeginValue(true);
8710  m_SB.Add('"');
8711  m_InsideString = true;
8712  if(pStr != VMA_NULL && pStr[0] != '\0')
8713  {
8714  ContinueString(pStr);
8715  }
8716 }
8717 
8718 void VmaJsonWriter::ContinueString(const char* pStr)
8719 {
8720  VMA_ASSERT(m_InsideString);
8721 
8722  const size_t strLen = strlen(pStr);
8723  for(size_t i = 0; i < strLen; ++i)
8724  {
8725  char ch = pStr[i];
8726  if(ch == '\\')
8727  {
8728  m_SB.Add("\\\\");
8729  }
8730  else if(ch == '"')
8731  {
8732  m_SB.Add("\\\"");
8733  }
8734  else if(ch >= 32)
8735  {
8736  m_SB.Add(ch);
8737  }
8738  else switch(ch)
8739  {
8740  case '\b':
8741  m_SB.Add("\\b");
8742  break;
8743  case '\f':
8744  m_SB.Add("\\f");
8745  break;
8746  case '\n':
8747  m_SB.Add("\\n");
8748  break;
8749  case '\r':
8750  m_SB.Add("\\r");
8751  break;
8752  case '\t':
8753  m_SB.Add("\\t");
8754  break;
8755  default:
8756  VMA_ASSERT(0 && "Character not currently supported.");
8757  break;
8758  }
8759  }
8760 }
8761 
8762 void VmaJsonWriter::ContinueString(uint32_t n)
8763 {
8764  VMA_ASSERT(m_InsideString);
8765  m_SB.AddNumber(n);
8766 }
8767 
8768 void VmaJsonWriter::ContinueString(uint64_t n)
8769 {
8770  VMA_ASSERT(m_InsideString);
8771  m_SB.AddNumber(n);
8772 }
8773 
8774 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8775 {
8776  VMA_ASSERT(m_InsideString);
8777  m_SB.AddPointer(ptr);
8778 }
8779 
8780 void VmaJsonWriter::EndString(const char* pStr)
8781 {
8782  VMA_ASSERT(m_InsideString);
8783  if(pStr != VMA_NULL && pStr[0] != '\0')
8784  {
8785  ContinueString(pStr);
8786  }
8787  m_SB.Add('"');
8788  m_InsideString = false;
8789 }
8790 
8791 void VmaJsonWriter::WriteNumber(uint32_t n)
8792 {
8793  VMA_ASSERT(!m_InsideString);
8794  BeginValue(false);
8795  m_SB.AddNumber(n);
8796 }
8797 
8798 void VmaJsonWriter::WriteNumber(uint64_t n)
8799 {
8800  VMA_ASSERT(!m_InsideString);
8801  BeginValue(false);
8802  m_SB.AddNumber(n);
8803 }
8804 
8805 void VmaJsonWriter::WriteBool(bool b)
8806 {
8807  VMA_ASSERT(!m_InsideString);
8808  BeginValue(false);
8809  m_SB.Add(b ? "true" : "false");
8810 }
8811 
8812 void VmaJsonWriter::WriteNull()
8813 {
8814  VMA_ASSERT(!m_InsideString);
8815  BeginValue(false);
8816  m_SB.Add("null");
8817 }
8818 
8819 void VmaJsonWriter::BeginValue(bool isString)
8820 {
8821  if(!m_Stack.empty())
8822  {
8823  StackItem& currItem = m_Stack.back();
8824  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8825  currItem.valueCount % 2 == 0)
8826  {
8827  VMA_ASSERT(isString);
8828  }
8829 
8830  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8831  currItem.valueCount % 2 != 0)
8832  {
8833  m_SB.Add(": ");
8834  }
8835  else if(currItem.valueCount > 0)
8836  {
8837  m_SB.Add(", ");
8838  WriteIndent();
8839  }
8840  else
8841  {
8842  WriteIndent();
8843  }
8844  ++currItem.valueCount;
8845  }
8846 }
8847 
8848 void VmaJsonWriter::WriteIndent(bool oneLess)
8849 {
8850  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8851  {
8852  m_SB.AddNewLine();
8853 
8854  size_t count = m_Stack.size();
8855  if(count > 0 && oneLess)
8856  {
8857  --count;
8858  }
8859  for(size_t i = 0; i < count; ++i)
8860  {
8861  m_SB.Add(INDENT);
8862  }
8863  }
8864 }
8865 
8866 #endif // #if VMA_STATS_STRING_ENABLED
8867 
8869 
8870 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8871 {
8872  if(IsUserDataString())
8873  {
8874  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8875 
8876  FreeUserDataString(hAllocator);
8877 
8878  if(pUserData != VMA_NULL)
8879  {
8880  m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8881  }
8882  }
8883  else
8884  {
8885  m_pUserData = pUserData;
8886  }
8887 }
8888 
8889 void VmaAllocation_T::ChangeBlockAllocation(
8890  VmaAllocator hAllocator,
8891  VmaDeviceMemoryBlock* block,
8892  VkDeviceSize offset)
8893 {
8894  VMA_ASSERT(block != VMA_NULL);
8895  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8896 
8897  // Move mapping reference counter from old block to new block.
8898  if(block != m_BlockAllocation.m_Block)
8899  {
8900  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8901  if(IsPersistentMap())
8902  ++mapRefCount;
8903  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8904  block->Map(hAllocator, mapRefCount, VMA_NULL);
8905  }
8906 
8907  m_BlockAllocation.m_Block = block;
8908  m_BlockAllocation.m_Offset = offset;
8909 }
8910 
8911 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8912 {
8913  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8914  m_BlockAllocation.m_Offset = newOffset;
8915 }
8916 
8917 VkDeviceSize VmaAllocation_T::GetOffset() const
8918 {
8919  switch(m_Type)
8920  {
8921  case ALLOCATION_TYPE_BLOCK:
8922  return m_BlockAllocation.m_Offset;
8923  case ALLOCATION_TYPE_DEDICATED:
8924  return 0;
8925  default:
8926  VMA_ASSERT(0);
8927  return 0;
8928  }
8929 }
8930 
8931 VkDeviceMemory VmaAllocation_T::GetMemory() const
8932 {
8933  switch(m_Type)
8934  {
8935  case ALLOCATION_TYPE_BLOCK:
8936  return m_BlockAllocation.m_Block->GetDeviceMemory();
8937  case ALLOCATION_TYPE_DEDICATED:
8938  return m_DedicatedAllocation.m_hMemory;
8939  default:
8940  VMA_ASSERT(0);
8941  return VK_NULL_HANDLE;
8942  }
8943 }
8944 
8945 void* VmaAllocation_T::GetMappedData() const
8946 {
8947  switch(m_Type)
8948  {
8949  case ALLOCATION_TYPE_BLOCK:
8950  if(m_MapCount != 0)
8951  {
8952  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8953  VMA_ASSERT(pBlockData != VMA_NULL);
8954  return (char*)pBlockData + m_BlockAllocation.m_Offset;
8955  }
8956  else
8957  {
8958  return VMA_NULL;
8959  }
8960  break;
8961  case ALLOCATION_TYPE_DEDICATED:
8962  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8963  return m_DedicatedAllocation.m_pMappedData;
8964  default:
8965  VMA_ASSERT(0);
8966  return VMA_NULL;
8967  }
8968 }
8969 
8970 bool VmaAllocation_T::CanBecomeLost() const
8971 {
8972  switch(m_Type)
8973  {
8974  case ALLOCATION_TYPE_BLOCK:
8975  return m_BlockAllocation.m_CanBecomeLost;
8976  case ALLOCATION_TYPE_DEDICATED:
8977  return false;
8978  default:
8979  VMA_ASSERT(0);
8980  return false;
8981  }
8982 }
8983 
8984 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8985 {
8986  VMA_ASSERT(CanBecomeLost());
8987 
8988  /*
8989  Warning: This is a carefully designed algorithm.
8990  Do not modify unless you really know what you're doing :)
8991  */
8992  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8993  for(;;)
8994  {
8995  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8996  {
8997  VMA_ASSERT(0);
8998  return false;
8999  }
9000  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
9001  {
9002  return false;
9003  }
9004  else // Last use time earlier than current time.
9005  {
9006  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
9007  {
9008  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
9009  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
9010  return true;
9011  }
9012  }
9013  }
9014 }
9015 
9016 #if VMA_STATS_STRING_ENABLED
9017 
9018 // Correspond to values of enum VmaSuballocationType.
9019 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
9020  "FREE",
9021  "UNKNOWN",
9022  "BUFFER",
9023  "IMAGE_UNKNOWN",
9024  "IMAGE_LINEAR",
9025  "IMAGE_OPTIMAL",
9026 };
9027 
9028 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
9029 {
9030  json.WriteString("Type");
9031  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
9032 
9033  json.WriteString("Size");
9034  json.WriteNumber(m_Size);
9035 
9036  if(m_pUserData != VMA_NULL)
9037  {
9038  json.WriteString("UserData");
9039  if(IsUserDataString())
9040  {
9041  json.WriteString((const char*)m_pUserData);
9042  }
9043  else
9044  {
9045  json.BeginString();
9046  json.ContinueString_Pointer(m_pUserData);
9047  json.EndString();
9048  }
9049  }
9050 
9051  json.WriteString("CreationFrameIndex");
9052  json.WriteNumber(m_CreationFrameIndex);
9053 
9054  json.WriteString("LastUseFrameIndex");
9055  json.WriteNumber(GetLastUseFrameIndex());
9056 
9057  if(m_BufferImageUsage != 0)
9058  {
9059  json.WriteString("Usage");
9060  json.WriteNumber(m_BufferImageUsage);
9061  }
9062 }
9063 
9064 #endif
9065 
9066 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
9067 {
9068  VMA_ASSERT(IsUserDataString());
9069  VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
9070  m_pUserData = VMA_NULL;
9071 }
9072 
9073 void VmaAllocation_T::BlockAllocMap()
9074 {
9075  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9076 
9077  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9078  {
9079  ++m_MapCount;
9080  }
9081  else
9082  {
9083  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
9084  }
9085 }
9086 
9087 void VmaAllocation_T::BlockAllocUnmap()
9088 {
9089  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9090 
9091  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9092  {
9093  --m_MapCount;
9094  }
9095  else
9096  {
9097  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
9098  }
9099 }
9100 
9101 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
9102 {
9103  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9104 
9105  if(m_MapCount != 0)
9106  {
9107  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9108  {
9109  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
9110  *ppData = m_DedicatedAllocation.m_pMappedData;
9111  ++m_MapCount;
9112  return VK_SUCCESS;
9113  }
9114  else
9115  {
9116  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
9117  return VK_ERROR_MEMORY_MAP_FAILED;
9118  }
9119  }
9120  else
9121  {
9122  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
9123  hAllocator->m_hDevice,
9124  m_DedicatedAllocation.m_hMemory,
9125  0, // offset
9126  VK_WHOLE_SIZE,
9127  0, // flags
9128  ppData);
9129  if(result == VK_SUCCESS)
9130  {
9131  m_DedicatedAllocation.m_pMappedData = *ppData;
9132  m_MapCount = 1;
9133  }
9134  return result;
9135  }
9136 }
9137 
9138 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
9139 {
9140  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9141 
9142  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9143  {
9144  --m_MapCount;
9145  if(m_MapCount == 0)
9146  {
9147  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
9148  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
9149  hAllocator->m_hDevice,
9150  m_DedicatedAllocation.m_hMemory);
9151  }
9152  }
9153  else
9154  {
9155  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
9156  }
9157 }
9158 
9159 #if VMA_STATS_STRING_ENABLED
9160 
9161 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
9162 {
9163  json.BeginObject();
9164 
9165  json.WriteString("Blocks");
9166  json.WriteNumber(stat.blockCount);
9167 
9168  json.WriteString("Allocations");
9169  json.WriteNumber(stat.allocationCount);
9170 
9171  json.WriteString("UnusedRanges");
9172  json.WriteNumber(stat.unusedRangeCount);
9173 
9174  json.WriteString("UsedBytes");
9175  json.WriteNumber(stat.usedBytes);
9176 
9177  json.WriteString("UnusedBytes");
9178  json.WriteNumber(stat.unusedBytes);
9179 
9180  if(stat.allocationCount > 1)
9181  {
9182  json.WriteString("AllocationSize");
9183  json.BeginObject(true);
9184  json.WriteString("Min");
9185  json.WriteNumber(stat.allocationSizeMin);
9186  json.WriteString("Avg");
9187  json.WriteNumber(stat.allocationSizeAvg);
9188  json.WriteString("Max");
9189  json.WriteNumber(stat.allocationSizeMax);
9190  json.EndObject();
9191  }
9192 
9193  if(stat.unusedRangeCount > 1)
9194  {
9195  json.WriteString("UnusedRangeSize");
9196  json.BeginObject(true);
9197  json.WriteString("Min");
9198  json.WriteNumber(stat.unusedRangeSizeMin);
9199  json.WriteString("Avg");
9200  json.WriteNumber(stat.unusedRangeSizeAvg);
9201  json.WriteString("Max");
9202  json.WriteNumber(stat.unusedRangeSizeMax);
9203  json.EndObject();
9204  }
9205 
9206  json.EndObject();
9207 }
9208 
9209 #endif // #if VMA_STATS_STRING_ENABLED
9210 
9211 struct VmaSuballocationItemSizeLess
9212 {
9213  bool operator()(
9214  const VmaSuballocationList::iterator lhs,
9215  const VmaSuballocationList::iterator rhs) const
9216  {
9217  return lhs->size < rhs->size;
9218  }
9219  bool operator()(
9220  const VmaSuballocationList::iterator lhs,
9221  VkDeviceSize rhsSize) const
9222  {
9223  return lhs->size < rhsSize;
9224  }
9225 };
9226 
9227 
9229 // class VmaBlockMetadata
9230 
9231 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
9232  m_Size(0),
9233  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
9234 {
9235 }
9236 
9237 #if VMA_STATS_STRING_ENABLED
9238 
9239 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
9240  VkDeviceSize unusedBytes,
9241  size_t allocationCount,
9242  size_t unusedRangeCount) const
9243 {
9244  json.BeginObject();
9245 
9246  json.WriteString("TotalBytes");
9247  json.WriteNumber(GetSize());
9248 
9249  json.WriteString("UnusedBytes");
9250  json.WriteNumber(unusedBytes);
9251 
9252  json.WriteString("Allocations");
9253  json.WriteNumber((uint64_t)allocationCount);
9254 
9255  json.WriteString("UnusedRanges");
9256  json.WriteNumber((uint64_t)unusedRangeCount);
9257 
9258  json.WriteString("Suballocations");
9259  json.BeginArray();
9260 }
9261 
9262 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
9263  VkDeviceSize offset,
9264  VmaAllocation hAllocation) const
9265 {
9266  json.BeginObject(true);
9267 
9268  json.WriteString("Offset");
9269  json.WriteNumber(offset);
9270 
9271  hAllocation->PrintParameters(json);
9272 
9273  json.EndObject();
9274 }
9275 
9276 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
9277  VkDeviceSize offset,
9278  VkDeviceSize size) const
9279 {
9280  json.BeginObject(true);
9281 
9282  json.WriteString("Offset");
9283  json.WriteNumber(offset);
9284 
9285  json.WriteString("Type");
9286  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
9287 
9288  json.WriteString("Size");
9289  json.WriteNumber(size);
9290 
9291  json.EndObject();
9292 }
9293 
9294 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
9295 {
9296  json.EndArray();
9297  json.EndObject();
9298 }
9299 
9300 #endif // #if VMA_STATS_STRING_ENABLED
9301 
9303 // class VmaBlockMetadata_Generic
9304 
9305 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
9306  VmaBlockMetadata(hAllocator),
9307  m_FreeCount(0),
9308  m_SumFreeSize(0),
9309  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9310  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
9311 {
9312 }
9313 
9314 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
9315 {
9316 }
9317 
9318 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
9319 {
9320  VmaBlockMetadata::Init(size);
9321 
9322  m_FreeCount = 1;
9323  m_SumFreeSize = size;
9324 
9325  VmaSuballocation suballoc = {};
9326  suballoc.offset = 0;
9327  suballoc.size = size;
9328  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9329  suballoc.hAllocation = VK_NULL_HANDLE;
9330 
9331  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9332  m_Suballocations.push_back(suballoc);
9333  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
9334  --suballocItem;
9335  m_FreeSuballocationsBySize.push_back(suballocItem);
9336 }
9337 
9338 bool VmaBlockMetadata_Generic::Validate() const
9339 {
9340  VMA_VALIDATE(!m_Suballocations.empty());
9341 
9342  // Expected offset of new suballocation as calculated from previous ones.
9343  VkDeviceSize calculatedOffset = 0;
9344  // Expected number of free suballocations as calculated from traversing their list.
9345  uint32_t calculatedFreeCount = 0;
9346  // Expected sum size of free suballocations as calculated from traversing their list.
9347  VkDeviceSize calculatedSumFreeSize = 0;
9348  // Expected number of free suballocations that should be registered in
9349  // m_FreeSuballocationsBySize calculated from traversing their list.
9350  size_t freeSuballocationsToRegister = 0;
9351  // True if previous visited suballocation was free.
9352  bool prevFree = false;
9353 
9354  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9355  suballocItem != m_Suballocations.cend();
9356  ++suballocItem)
9357  {
9358  const VmaSuballocation& subAlloc = *suballocItem;
9359 
9360  // Actual offset of this suballocation doesn't match expected one.
9361  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
9362 
9363  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
9364  // Two adjacent free suballocations are invalid. They should be merged.
9365  VMA_VALIDATE(!prevFree || !currFree);
9366 
9367  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
9368 
9369  if(currFree)
9370  {
9371  calculatedSumFreeSize += subAlloc.size;
9372  ++calculatedFreeCount;
9373  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9374  {
9375  ++freeSuballocationsToRegister;
9376  }
9377 
9378  // Margin required between allocations - every free space must be at least that large.
9379  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
9380  }
9381  else
9382  {
9383  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
9384  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
9385 
9386  // Margin required between allocations - previous allocation must be free.
9387  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
9388  }
9389 
9390  calculatedOffset += subAlloc.size;
9391  prevFree = currFree;
9392  }
9393 
9394  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9395  // match expected one.
9396  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9397 
9398  VkDeviceSize lastSize = 0;
9399  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9400  {
9401  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9402 
9403  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9404  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9405  // They must be sorted by size ascending.
9406  VMA_VALIDATE(suballocItem->size >= lastSize);
9407 
9408  lastSize = suballocItem->size;
9409  }
9410 
9411  // Check if totals match calculated values.
9412  VMA_VALIDATE(ValidateFreeSuballocationList());
9413  VMA_VALIDATE(calculatedOffset == GetSize());
9414  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9415  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9416 
9417  return true;
9418 }
9419 
9420 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9421 {
9422  if(!m_FreeSuballocationsBySize.empty())
9423  {
9424  return m_FreeSuballocationsBySize.back()->size;
9425  }
9426  else
9427  {
9428  return 0;
9429  }
9430 }
9431 
9432 bool VmaBlockMetadata_Generic::IsEmpty() const
9433 {
9434  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9435 }
9436 
9437 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9438 {
9439  outInfo.blockCount = 1;
9440 
9441  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9442  outInfo.allocationCount = rangeCount - m_FreeCount;
9443  outInfo.unusedRangeCount = m_FreeCount;
9444 
9445  outInfo.unusedBytes = m_SumFreeSize;
9446  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9447 
9448  outInfo.allocationSizeMin = UINT64_MAX;
9449  outInfo.allocationSizeMax = 0;
9450  outInfo.unusedRangeSizeMin = UINT64_MAX;
9451  outInfo.unusedRangeSizeMax = 0;
9452 
9453  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9454  suballocItem != m_Suballocations.cend();
9455  ++suballocItem)
9456  {
9457  const VmaSuballocation& suballoc = *suballocItem;
9458  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9459  {
9460  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9461  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9462  }
9463  else
9464  {
9465  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9466  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9467  }
9468  }
9469 }
9470 
9471 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9472 {
9473  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9474 
9475  inoutStats.size += GetSize();
9476  inoutStats.unusedSize += m_SumFreeSize;
9477  inoutStats.allocationCount += rangeCount - m_FreeCount;
9478  inoutStats.unusedRangeCount += m_FreeCount;
9479  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9480 }
9481 
9482 #if VMA_STATS_STRING_ENABLED
9483 
9484 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9485 {
9486  PrintDetailedMap_Begin(json,
9487  m_SumFreeSize, // unusedBytes
9488  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9489  m_FreeCount); // unusedRangeCount
9490 
9491  size_t i = 0;
9492  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9493  suballocItem != m_Suballocations.cend();
9494  ++suballocItem, ++i)
9495  {
9496  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9497  {
9498  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9499  }
9500  else
9501  {
9502  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9503  }
9504  }
9505 
9506  PrintDetailedMap_End(json);
9507 }
9508 
9509 #endif // #if VMA_STATS_STRING_ENABLED
9510 
9511 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9512  uint32_t currentFrameIndex,
9513  uint32_t frameInUseCount,
9514  VkDeviceSize bufferImageGranularity,
9515  VkDeviceSize allocSize,
9516  VkDeviceSize allocAlignment,
9517  bool upperAddress,
9518  VmaSuballocationType allocType,
9519  bool canMakeOtherLost,
9520  uint32_t strategy,
9521  VmaAllocationRequest* pAllocationRequest)
9522 {
9523  VMA_ASSERT(allocSize > 0);
9524  VMA_ASSERT(!upperAddress);
9525  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9526  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9527  VMA_HEAVY_ASSERT(Validate());
9528 
9529  pAllocationRequest->type = VmaAllocationRequestType::Normal;
9530 
9531  // There is not enough total free space in this block to fullfill the request: Early return.
9532  if(canMakeOtherLost == false &&
9533  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9534  {
9535  return false;
9536  }
9537 
9538  // New algorithm, efficiently searching freeSuballocationsBySize.
9539  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9540  if(freeSuballocCount > 0)
9541  {
9543  {
9544  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9545  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9546  m_FreeSuballocationsBySize.data(),
9547  m_FreeSuballocationsBySize.data() + freeSuballocCount,
9548  allocSize + 2 * VMA_DEBUG_MARGIN,
9549  VmaSuballocationItemSizeLess());
9550  size_t index = it - m_FreeSuballocationsBySize.data();
9551  for(; index < freeSuballocCount; ++index)
9552  {
9553  if(CheckAllocation(
9554  currentFrameIndex,
9555  frameInUseCount,
9556  bufferImageGranularity,
9557  allocSize,
9558  allocAlignment,
9559  allocType,
9560  m_FreeSuballocationsBySize[index],
9561  false, // canMakeOtherLost
9562  &pAllocationRequest->offset,
9563  &pAllocationRequest->itemsToMakeLostCount,
9564  &pAllocationRequest->sumFreeSize,
9565  &pAllocationRequest->sumItemSize))
9566  {
9567  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9568  return true;
9569  }
9570  }
9571  }
9572  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9573  {
9574  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9575  it != m_Suballocations.end();
9576  ++it)
9577  {
9578  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9579  currentFrameIndex,
9580  frameInUseCount,
9581  bufferImageGranularity,
9582  allocSize,
9583  allocAlignment,
9584  allocType,
9585  it,
9586  false, // canMakeOtherLost
9587  &pAllocationRequest->offset,
9588  &pAllocationRequest->itemsToMakeLostCount,
9589  &pAllocationRequest->sumFreeSize,
9590  &pAllocationRequest->sumItemSize))
9591  {
9592  pAllocationRequest->item = it;
9593  return true;
9594  }
9595  }
9596  }
9597  else // WORST_FIT, FIRST_FIT
9598  {
9599  // Search staring from biggest suballocations.
9600  for(size_t index = freeSuballocCount; index--; )
9601  {
9602  if(CheckAllocation(
9603  currentFrameIndex,
9604  frameInUseCount,
9605  bufferImageGranularity,
9606  allocSize,
9607  allocAlignment,
9608  allocType,
9609  m_FreeSuballocationsBySize[index],
9610  false, // canMakeOtherLost
9611  &pAllocationRequest->offset,
9612  &pAllocationRequest->itemsToMakeLostCount,
9613  &pAllocationRequest->sumFreeSize,
9614  &pAllocationRequest->sumItemSize))
9615  {
9616  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9617  return true;
9618  }
9619  }
9620  }
9621  }
9622 
9623  if(canMakeOtherLost)
9624  {
9625  // Brute-force algorithm. TODO: Come up with something better.
9626 
9627  bool found = false;
9628  VmaAllocationRequest tmpAllocRequest = {};
9629  tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9630  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9631  suballocIt != m_Suballocations.end();
9632  ++suballocIt)
9633  {
9634  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9635  suballocIt->hAllocation->CanBecomeLost())
9636  {
9637  if(CheckAllocation(
9638  currentFrameIndex,
9639  frameInUseCount,
9640  bufferImageGranularity,
9641  allocSize,
9642  allocAlignment,
9643  allocType,
9644  suballocIt,
9645  canMakeOtherLost,
9646  &tmpAllocRequest.offset,
9647  &tmpAllocRequest.itemsToMakeLostCount,
9648  &tmpAllocRequest.sumFreeSize,
9649  &tmpAllocRequest.sumItemSize))
9650  {
9652  {
9653  *pAllocationRequest = tmpAllocRequest;
9654  pAllocationRequest->item = suballocIt;
9655  break;
9656  }
9657  if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9658  {
9659  *pAllocationRequest = tmpAllocRequest;
9660  pAllocationRequest->item = suballocIt;
9661  found = true;
9662  }
9663  }
9664  }
9665  }
9666 
9667  return found;
9668  }
9669 
9670  return false;
9671 }
9672 
9673 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9674  uint32_t currentFrameIndex,
9675  uint32_t frameInUseCount,
9676  VmaAllocationRequest* pAllocationRequest)
9677 {
9678  VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9679 
9680  while(pAllocationRequest->itemsToMakeLostCount > 0)
9681  {
9682  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9683  {
9684  ++pAllocationRequest->item;
9685  }
9686  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9687  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9688  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9689  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9690  {
9691  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9692  --pAllocationRequest->itemsToMakeLostCount;
9693  }
9694  else
9695  {
9696  return false;
9697  }
9698  }
9699 
9700  VMA_HEAVY_ASSERT(Validate());
9701  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9702  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9703 
9704  return true;
9705 }
9706 
9707 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9708 {
9709  uint32_t lostAllocationCount = 0;
9710  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9711  it != m_Suballocations.end();
9712  ++it)
9713  {
9714  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9715  it->hAllocation->CanBecomeLost() &&
9716  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9717  {
9718  it = FreeSuballocation(it);
9719  ++lostAllocationCount;
9720  }
9721  }
9722  return lostAllocationCount;
9723 }
9724 
9725 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9726 {
9727  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9728  it != m_Suballocations.end();
9729  ++it)
9730  {
9731  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9732  {
9733  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9734  {
9735  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9736  return VK_ERROR_VALIDATION_FAILED_EXT;
9737  }
9738  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9739  {
9740  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9741  return VK_ERROR_VALIDATION_FAILED_EXT;
9742  }
9743  }
9744  }
9745 
9746  return VK_SUCCESS;
9747 }
9748 
9749 void VmaBlockMetadata_Generic::Alloc(
9750  const VmaAllocationRequest& request,
9751  VmaSuballocationType type,
9752  VkDeviceSize allocSize,
9753  VmaAllocation hAllocation)
9754 {
9755  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9756  VMA_ASSERT(request.item != m_Suballocations.end());
9757  VmaSuballocation& suballoc = *request.item;
9758  // Given suballocation is a free block.
9759  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9760  // Given offset is inside this suballocation.
9761  VMA_ASSERT(request.offset >= suballoc.offset);
9762  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9763  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9764  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9765 
9766  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9767  // it to become used.
9768  UnregisterFreeSuballocation(request.item);
9769 
9770  suballoc.offset = request.offset;
9771  suballoc.size = allocSize;
9772  suballoc.type = type;
9773  suballoc.hAllocation = hAllocation;
9774 
9775  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9776  if(paddingEnd)
9777  {
9778  VmaSuballocation paddingSuballoc = {};
9779  paddingSuballoc.offset = request.offset + allocSize;
9780  paddingSuballoc.size = paddingEnd;
9781  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9782  VmaSuballocationList::iterator next = request.item;
9783  ++next;
9784  const VmaSuballocationList::iterator paddingEndItem =
9785  m_Suballocations.insert(next, paddingSuballoc);
9786  RegisterFreeSuballocation(paddingEndItem);
9787  }
9788 
9789  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9790  if(paddingBegin)
9791  {
9792  VmaSuballocation paddingSuballoc = {};
9793  paddingSuballoc.offset = request.offset - paddingBegin;
9794  paddingSuballoc.size = paddingBegin;
9795  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9796  const VmaSuballocationList::iterator paddingBeginItem =
9797  m_Suballocations.insert(request.item, paddingSuballoc);
9798  RegisterFreeSuballocation(paddingBeginItem);
9799  }
9800 
9801  // Update totals.
9802  m_FreeCount = m_FreeCount - 1;
9803  if(paddingBegin > 0)
9804  {
9805  ++m_FreeCount;
9806  }
9807  if(paddingEnd > 0)
9808  {
9809  ++m_FreeCount;
9810  }
9811  m_SumFreeSize -= allocSize;
9812 }
9813 
9814 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9815 {
9816  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9817  suballocItem != m_Suballocations.end();
9818  ++suballocItem)
9819  {
9820  VmaSuballocation& suballoc = *suballocItem;
9821  if(suballoc.hAllocation == allocation)
9822  {
9823  FreeSuballocation(suballocItem);
9824  VMA_HEAVY_ASSERT(Validate());
9825  return;
9826  }
9827  }
9828  VMA_ASSERT(0 && "Not found!");
9829 }
9830 
9831 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9832 {
9833  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9834  suballocItem != m_Suballocations.end();
9835  ++suballocItem)
9836  {
9837  VmaSuballocation& suballoc = *suballocItem;
9838  if(suballoc.offset == offset)
9839  {
9840  FreeSuballocation(suballocItem);
9841  return;
9842  }
9843  }
9844  VMA_ASSERT(0 && "Not found!");
9845 }
9846 
9847 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9848 {
9849  VkDeviceSize lastSize = 0;
9850  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9851  {
9852  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9853 
9854  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9855  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9856  VMA_VALIDATE(it->size >= lastSize);
9857  lastSize = it->size;
9858  }
9859  return true;
9860 }
9861 
9862 bool VmaBlockMetadata_Generic::CheckAllocation(
9863  uint32_t currentFrameIndex,
9864  uint32_t frameInUseCount,
9865  VkDeviceSize bufferImageGranularity,
9866  VkDeviceSize allocSize,
9867  VkDeviceSize allocAlignment,
9868  VmaSuballocationType allocType,
9869  VmaSuballocationList::const_iterator suballocItem,
9870  bool canMakeOtherLost,
9871  VkDeviceSize* pOffset,
9872  size_t* itemsToMakeLostCount,
9873  VkDeviceSize* pSumFreeSize,
9874  VkDeviceSize* pSumItemSize) const
9875 {
9876  VMA_ASSERT(allocSize > 0);
9877  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9878  VMA_ASSERT(suballocItem != m_Suballocations.cend());
9879  VMA_ASSERT(pOffset != VMA_NULL);
9880 
9881  *itemsToMakeLostCount = 0;
9882  *pSumFreeSize = 0;
9883  *pSumItemSize = 0;
9884 
9885  if(canMakeOtherLost)
9886  {
9887  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9888  {
9889  *pSumFreeSize = suballocItem->size;
9890  }
9891  else
9892  {
9893  if(suballocItem->hAllocation->CanBecomeLost() &&
9894  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9895  {
9896  ++*itemsToMakeLostCount;
9897  *pSumItemSize = suballocItem->size;
9898  }
9899  else
9900  {
9901  return false;
9902  }
9903  }
9904 
9905  // Remaining size is too small for this request: Early return.
9906  if(GetSize() - suballocItem->offset < allocSize)
9907  {
9908  return false;
9909  }
9910 
9911  // Start from offset equal to beginning of this suballocation.
9912  *pOffset = suballocItem->offset;
9913 
9914  // Apply VMA_DEBUG_MARGIN at the beginning.
9915  if(VMA_DEBUG_MARGIN > 0)
9916  {
9917  *pOffset += VMA_DEBUG_MARGIN;
9918  }
9919 
9920  // Apply alignment.
9921  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9922 
9923  // Check previous suballocations for BufferImageGranularity conflicts.
9924  // Make bigger alignment if necessary.
9925  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
9926  {
9927  bool bufferImageGranularityConflict = false;
9928  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9929  while(prevSuballocItem != m_Suballocations.cbegin())
9930  {
9931  --prevSuballocItem;
9932  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9933  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9934  {
9935  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9936  {
9937  bufferImageGranularityConflict = true;
9938  break;
9939  }
9940  }
9941  else
9942  // Already on previous page.
9943  break;
9944  }
9945  if(bufferImageGranularityConflict)
9946  {
9947  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9948  }
9949  }
9950 
9951  // Now that we have final *pOffset, check if we are past suballocItem.
9952  // If yes, return false - this function should be called for another suballocItem as starting point.
9953  if(*pOffset >= suballocItem->offset + suballocItem->size)
9954  {
9955  return false;
9956  }
9957 
9958  // Calculate padding at the beginning based on current offset.
9959  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9960 
9961  // Calculate required margin at the end.
9962  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9963 
9964  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9965  // Another early return check.
9966  if(suballocItem->offset + totalSize > GetSize())
9967  {
9968  return false;
9969  }
9970 
9971  // Advance lastSuballocItem until desired size is reached.
9972  // Update itemsToMakeLostCount.
9973  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9974  if(totalSize > suballocItem->size)
9975  {
9976  VkDeviceSize remainingSize = totalSize - suballocItem->size;
9977  while(remainingSize > 0)
9978  {
9979  ++lastSuballocItem;
9980  if(lastSuballocItem == m_Suballocations.cend())
9981  {
9982  return false;
9983  }
9984  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9985  {
9986  *pSumFreeSize += lastSuballocItem->size;
9987  }
9988  else
9989  {
9990  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9991  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9992  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9993  {
9994  ++*itemsToMakeLostCount;
9995  *pSumItemSize += lastSuballocItem->size;
9996  }
9997  else
9998  {
9999  return false;
10000  }
10001  }
10002  remainingSize = (lastSuballocItem->size < remainingSize) ?
10003  remainingSize - lastSuballocItem->size : 0;
10004  }
10005  }
10006 
10007  // Check next suballocations for BufferImageGranularity conflicts.
10008  // If conflict exists, we must mark more allocations lost or fail.
10009  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
10010  {
10011  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
10012  ++nextSuballocItem;
10013  while(nextSuballocItem != m_Suballocations.cend())
10014  {
10015  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
10016  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10017  {
10018  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10019  {
10020  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
10021  if(nextSuballoc.hAllocation->CanBecomeLost() &&
10022  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10023  {
10024  ++*itemsToMakeLostCount;
10025  }
10026  else
10027  {
10028  return false;
10029  }
10030  }
10031  }
10032  else
10033  {
10034  // Already on next page.
10035  break;
10036  }
10037  ++nextSuballocItem;
10038  }
10039  }
10040  }
10041  else
10042  {
10043  const VmaSuballocation& suballoc = *suballocItem;
10044  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10045 
10046  *pSumFreeSize = suballoc.size;
10047 
10048  // Size of this suballocation is too small for this request: Early return.
10049  if(suballoc.size < allocSize)
10050  {
10051  return false;
10052  }
10053 
10054  // Start from offset equal to beginning of this suballocation.
10055  *pOffset = suballoc.offset;
10056 
10057  // Apply VMA_DEBUG_MARGIN at the beginning.
10058  if(VMA_DEBUG_MARGIN > 0)
10059  {
10060  *pOffset += VMA_DEBUG_MARGIN;
10061  }
10062 
10063  // Apply alignment.
10064  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
10065 
10066  // Check previous suballocations for BufferImageGranularity conflicts.
10067  // Make bigger alignment if necessary.
10068  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
10069  {
10070  bool bufferImageGranularityConflict = false;
10071  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
10072  while(prevSuballocItem != m_Suballocations.cbegin())
10073  {
10074  --prevSuballocItem;
10075  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
10076  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
10077  {
10078  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10079  {
10080  bufferImageGranularityConflict = true;
10081  break;
10082  }
10083  }
10084  else
10085  // Already on previous page.
10086  break;
10087  }
10088  if(bufferImageGranularityConflict)
10089  {
10090  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
10091  }
10092  }
10093 
10094  // Calculate padding at the beginning based on current offset.
10095  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
10096 
10097  // Calculate required margin at the end.
10098  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
10099 
10100  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
10101  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
10102  {
10103  return false;
10104  }
10105 
10106  // Check next suballocations for BufferImageGranularity conflicts.
10107  // If conflict exists, allocation cannot be made here.
10108  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
10109  {
10110  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
10111  ++nextSuballocItem;
10112  while(nextSuballocItem != m_Suballocations.cend())
10113  {
10114  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
10115  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10116  {
10117  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10118  {
10119  return false;
10120  }
10121  }
10122  else
10123  {
10124  // Already on next page.
10125  break;
10126  }
10127  ++nextSuballocItem;
10128  }
10129  }
10130  }
10131 
10132  // All tests passed: Success. pOffset is already filled.
10133  return true;
10134 }
10135 
10136 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
10137 {
10138  VMA_ASSERT(item != m_Suballocations.end());
10139  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10140 
10141  VmaSuballocationList::iterator nextItem = item;
10142  ++nextItem;
10143  VMA_ASSERT(nextItem != m_Suballocations.end());
10144  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
10145 
10146  item->size += nextItem->size;
10147  --m_FreeCount;
10148  m_Suballocations.erase(nextItem);
10149 }
10150 
10151 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
10152 {
10153  // Change this suballocation to be marked as free.
10154  VmaSuballocation& suballoc = *suballocItem;
10155  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10156  suballoc.hAllocation = VK_NULL_HANDLE;
10157 
10158  // Update totals.
10159  ++m_FreeCount;
10160  m_SumFreeSize += suballoc.size;
10161 
10162  // Merge with previous and/or next suballocation if it's also free.
10163  bool mergeWithNext = false;
10164  bool mergeWithPrev = false;
10165 
10166  VmaSuballocationList::iterator nextItem = suballocItem;
10167  ++nextItem;
10168  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
10169  {
10170  mergeWithNext = true;
10171  }
10172 
10173  VmaSuballocationList::iterator prevItem = suballocItem;
10174  if(suballocItem != m_Suballocations.begin())
10175  {
10176  --prevItem;
10177  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
10178  {
10179  mergeWithPrev = true;
10180  }
10181  }
10182 
10183  if(mergeWithNext)
10184  {
10185  UnregisterFreeSuballocation(nextItem);
10186  MergeFreeWithNext(suballocItem);
10187  }
10188 
10189  if(mergeWithPrev)
10190  {
10191  UnregisterFreeSuballocation(prevItem);
10192  MergeFreeWithNext(prevItem);
10193  RegisterFreeSuballocation(prevItem);
10194  return prevItem;
10195  }
10196  else
10197  {
10198  RegisterFreeSuballocation(suballocItem);
10199  return suballocItem;
10200  }
10201 }
10202 
10203 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
10204 {
10205  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10206  VMA_ASSERT(item->size > 0);
10207 
10208  // You may want to enable this validation at the beginning or at the end of
10209  // this function, depending on what do you want to check.
10210  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10211 
10212  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10213  {
10214  if(m_FreeSuballocationsBySize.empty())
10215  {
10216  m_FreeSuballocationsBySize.push_back(item);
10217  }
10218  else
10219  {
10220  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
10221  }
10222  }
10223 
10224  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10225 }
10226 
10227 
10228 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
10229 {
10230  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10231  VMA_ASSERT(item->size > 0);
10232 
10233  // You may want to enable this validation at the beginning or at the end of
10234  // this function, depending on what do you want to check.
10235  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10236 
10237  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10238  {
10239  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
10240  m_FreeSuballocationsBySize.data(),
10241  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
10242  item,
10243  VmaSuballocationItemSizeLess());
10244  for(size_t index = it - m_FreeSuballocationsBySize.data();
10245  index < m_FreeSuballocationsBySize.size();
10246  ++index)
10247  {
10248  if(m_FreeSuballocationsBySize[index] == item)
10249  {
10250  VmaVectorRemove(m_FreeSuballocationsBySize, index);
10251  return;
10252  }
10253  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
10254  }
10255  VMA_ASSERT(0 && "Not found.");
10256  }
10257 
10258  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10259 }
10260 
10261 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
10262  VkDeviceSize bufferImageGranularity,
10263  VmaSuballocationType& inOutPrevSuballocType) const
10264 {
10265  if(bufferImageGranularity == 1 || IsEmpty())
10266  {
10267  return false;
10268  }
10269 
10270  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
10271  bool typeConflictFound = false;
10272  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
10273  it != m_Suballocations.cend();
10274  ++it)
10275  {
10276  const VmaSuballocationType suballocType = it->type;
10277  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
10278  {
10279  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
10280  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
10281  {
10282  typeConflictFound = true;
10283  }
10284  inOutPrevSuballocType = suballocType;
10285  }
10286  }
10287 
10288  return typeConflictFound || minAlignment >= bufferImageGranularity;
10289 }
10290 
10292 // class VmaBlockMetadata_Linear
10293 
10294 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
10295  VmaBlockMetadata(hAllocator),
10296  m_SumFreeSize(0),
10297  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10298  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10299  m_1stVectorIndex(0),
10300  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
10301  m_1stNullItemsBeginCount(0),
10302  m_1stNullItemsMiddleCount(0),
10303  m_2ndNullItemsCount(0)
10304 {
10305 }
10306 
10307 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
10308 {
10309 }
10310 
10311 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
10312 {
10313  VmaBlockMetadata::Init(size);
10314  m_SumFreeSize = size;
10315 }
10316 
10317 bool VmaBlockMetadata_Linear::Validate() const
10318 {
10319  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10320  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10321 
10322  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
10323  VMA_VALIDATE(!suballocations1st.empty() ||
10324  suballocations2nd.empty() ||
10325  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
10326 
10327  if(!suballocations1st.empty())
10328  {
10329  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
10330  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
10331  // Null item at the end should be just pop_back().
10332  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
10333  }
10334  if(!suballocations2nd.empty())
10335  {
10336  // Null item at the end should be just pop_back().
10337  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
10338  }
10339 
10340  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
10341  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
10342 
10343  VkDeviceSize sumUsedSize = 0;
10344  const size_t suballoc1stCount = suballocations1st.size();
10345  VkDeviceSize offset = VMA_DEBUG_MARGIN;
10346 
10347  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10348  {
10349  const size_t suballoc2ndCount = suballocations2nd.size();
10350  size_t nullItem2ndCount = 0;
10351  for(size_t i = 0; i < suballoc2ndCount; ++i)
10352  {
10353  const VmaSuballocation& suballoc = suballocations2nd[i];
10354  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10355 
10356  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10357  VMA_VALIDATE(suballoc.offset >= offset);
10358 
10359  if(!currFree)
10360  {
10361  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10362  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10363  sumUsedSize += suballoc.size;
10364  }
10365  else
10366  {
10367  ++nullItem2ndCount;
10368  }
10369 
10370  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10371  }
10372 
10373  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10374  }
10375 
10376  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
10377  {
10378  const VmaSuballocation& suballoc = suballocations1st[i];
10379  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
10380  suballoc.hAllocation == VK_NULL_HANDLE);
10381  }
10382 
10383  size_t nullItem1stCount = m_1stNullItemsBeginCount;
10384 
10385  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
10386  {
10387  const VmaSuballocation& suballoc = suballocations1st[i];
10388  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10389 
10390  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10391  VMA_VALIDATE(suballoc.offset >= offset);
10392  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10393 
10394  if(!currFree)
10395  {
10396  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10397  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10398  sumUsedSize += suballoc.size;
10399  }
10400  else
10401  {
10402  ++nullItem1stCount;
10403  }
10404 
10405  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10406  }
10407  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10408 
10409  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10410  {
10411  const size_t suballoc2ndCount = suballocations2nd.size();
10412  size_t nullItem2ndCount = 0;
10413  for(size_t i = suballoc2ndCount; i--; )
10414  {
10415  const VmaSuballocation& suballoc = suballocations2nd[i];
10416  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10417 
10418  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10419  VMA_VALIDATE(suballoc.offset >= offset);
10420 
10421  if(!currFree)
10422  {
10423  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10424  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10425  sumUsedSize += suballoc.size;
10426  }
10427  else
10428  {
10429  ++nullItem2ndCount;
10430  }
10431 
10432  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10433  }
10434 
10435  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10436  }
10437 
10438  VMA_VALIDATE(offset <= GetSize());
10439  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10440 
10441  return true;
10442 }
10443 
10444 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10445 {
10446  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10447  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10448 }
10449 
10450 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10451 {
10452  const VkDeviceSize size = GetSize();
10453 
10454  /*
10455  We don't consider gaps inside allocation vectors with freed allocations because
10456  they are not suitable for reuse in linear allocator. We consider only space that
10457  is available for new allocations.
10458  */
10459  if(IsEmpty())
10460  {
10461  return size;
10462  }
10463 
10464  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10465 
10466  switch(m_2ndVectorMode)
10467  {
10468  case SECOND_VECTOR_EMPTY:
10469  /*
10470  Available space is after end of 1st, as well as before beginning of 1st (which
10471  would make it a ring buffer).
10472  */
10473  {
10474  const size_t suballocations1stCount = suballocations1st.size();
10475  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10476  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10477  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10478  return VMA_MAX(
10479  firstSuballoc.offset,
10480  size - (lastSuballoc.offset + lastSuballoc.size));
10481  }
10482  break;
10483 
10484  case SECOND_VECTOR_RING_BUFFER:
10485  /*
10486  Available space is only between end of 2nd and beginning of 1st.
10487  */
10488  {
10489  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10490  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10491  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10492  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10493  }
10494  break;
10495 
10496  case SECOND_VECTOR_DOUBLE_STACK:
10497  /*
10498  Available space is only between end of 1st and top of 2nd.
10499  */
10500  {
10501  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10502  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10503  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10504  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10505  }
10506  break;
10507 
10508  default:
10509  VMA_ASSERT(0);
10510  return 0;
10511  }
10512 }
10513 
10514 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10515 {
10516  const VkDeviceSize size = GetSize();
10517  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10518  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10519  const size_t suballoc1stCount = suballocations1st.size();
10520  const size_t suballoc2ndCount = suballocations2nd.size();
10521 
10522  outInfo.blockCount = 1;
10523  outInfo.allocationCount = (uint32_t)GetAllocationCount();
10524  outInfo.unusedRangeCount = 0;
10525  outInfo.usedBytes = 0;
10526  outInfo.allocationSizeMin = UINT64_MAX;
10527  outInfo.allocationSizeMax = 0;
10528  outInfo.unusedRangeSizeMin = UINT64_MAX;
10529  outInfo.unusedRangeSizeMax = 0;
10530 
10531  VkDeviceSize lastOffset = 0;
10532 
10533  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10534  {
10535  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10536  size_t nextAlloc2ndIndex = 0;
10537  while(lastOffset < freeSpace2ndTo1stEnd)
10538  {
10539  // Find next non-null allocation or move nextAllocIndex to the end.
10540  while(nextAlloc2ndIndex < suballoc2ndCount &&
10541  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10542  {
10543  ++nextAlloc2ndIndex;
10544  }
10545 
10546  // Found non-null allocation.
10547  if(nextAlloc2ndIndex < suballoc2ndCount)
10548  {
10549  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10550 
10551  // 1. Process free space before this allocation.
10552  if(lastOffset < suballoc.offset)
10553  {
10554  // There is free space from lastOffset to suballoc.offset.
10555  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10556  ++outInfo.unusedRangeCount;
10557  outInfo.unusedBytes += unusedRangeSize;
10558  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10559  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10560  }
10561 
10562  // 2. Process this allocation.
10563  // There is allocation with suballoc.offset, suballoc.size.
10564  outInfo.usedBytes += suballoc.size;
10565  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10566  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10567 
10568  // 3. Prepare for next iteration.
10569  lastOffset = suballoc.offset + suballoc.size;
10570  ++nextAlloc2ndIndex;
10571  }
10572  // We are at the end.
10573  else
10574  {
10575  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10576  if(lastOffset < freeSpace2ndTo1stEnd)
10577  {
10578  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10579  ++outInfo.unusedRangeCount;
10580  outInfo.unusedBytes += unusedRangeSize;
10581  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10582  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10583  }
10584 
10585  // End of loop.
10586  lastOffset = freeSpace2ndTo1stEnd;
10587  }
10588  }
10589  }
10590 
10591  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10592  const VkDeviceSize freeSpace1stTo2ndEnd =
10593  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10594  while(lastOffset < freeSpace1stTo2ndEnd)
10595  {
10596  // Find next non-null allocation or move nextAllocIndex to the end.
10597  while(nextAlloc1stIndex < suballoc1stCount &&
10598  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10599  {
10600  ++nextAlloc1stIndex;
10601  }
10602 
10603  // Found non-null allocation.
10604  if(nextAlloc1stIndex < suballoc1stCount)
10605  {
10606  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10607 
10608  // 1. Process free space before this allocation.
10609  if(lastOffset < suballoc.offset)
10610  {
10611  // There is free space from lastOffset to suballoc.offset.
10612  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10613  ++outInfo.unusedRangeCount;
10614  outInfo.unusedBytes += unusedRangeSize;
10615  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10616  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10617  }
10618 
10619  // 2. Process this allocation.
10620  // There is allocation with suballoc.offset, suballoc.size.
10621  outInfo.usedBytes += suballoc.size;
10622  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10623  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10624 
10625  // 3. Prepare for next iteration.
10626  lastOffset = suballoc.offset + suballoc.size;
10627  ++nextAlloc1stIndex;
10628  }
10629  // We are at the end.
10630  else
10631  {
10632  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10633  if(lastOffset < freeSpace1stTo2ndEnd)
10634  {
10635  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10636  ++outInfo.unusedRangeCount;
10637  outInfo.unusedBytes += unusedRangeSize;
10638  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10639  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10640  }
10641 
10642  // End of loop.
10643  lastOffset = freeSpace1stTo2ndEnd;
10644  }
10645  }
10646 
10647  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10648  {
10649  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10650  while(lastOffset < size)
10651  {
10652  // Find next non-null allocation or move nextAllocIndex to the end.
10653  while(nextAlloc2ndIndex != SIZE_MAX &&
10654  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10655  {
10656  --nextAlloc2ndIndex;
10657  }
10658 
10659  // Found non-null allocation.
10660  if(nextAlloc2ndIndex != SIZE_MAX)
10661  {
10662  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10663 
10664  // 1. Process free space before this allocation.
10665  if(lastOffset < suballoc.offset)
10666  {
10667  // There is free space from lastOffset to suballoc.offset.
10668  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10669  ++outInfo.unusedRangeCount;
10670  outInfo.unusedBytes += unusedRangeSize;
10671  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10672  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10673  }
10674 
10675  // 2. Process this allocation.
10676  // There is allocation with suballoc.offset, suballoc.size.
10677  outInfo.usedBytes += suballoc.size;
10678  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10679  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10680 
10681  // 3. Prepare for next iteration.
10682  lastOffset = suballoc.offset + suballoc.size;
10683  --nextAlloc2ndIndex;
10684  }
10685  // We are at the end.
10686  else
10687  {
10688  // There is free space from lastOffset to size.
10689  if(lastOffset < size)
10690  {
10691  const VkDeviceSize unusedRangeSize = size - lastOffset;
10692  ++outInfo.unusedRangeCount;
10693  outInfo.unusedBytes += unusedRangeSize;
10694  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10695  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10696  }
10697 
10698  // End of loop.
10699  lastOffset = size;
10700  }
10701  }
10702  }
10703 
10704  outInfo.unusedBytes = size - outInfo.usedBytes;
10705 }
10706 
10707 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10708 {
10709  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10710  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10711  const VkDeviceSize size = GetSize();
10712  const size_t suballoc1stCount = suballocations1st.size();
10713  const size_t suballoc2ndCount = suballocations2nd.size();
10714 
10715  inoutStats.size += size;
10716 
10717  VkDeviceSize lastOffset = 0;
10718 
10719  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10720  {
10721  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10722  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10723  while(lastOffset < freeSpace2ndTo1stEnd)
10724  {
10725  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10726  while(nextAlloc2ndIndex < suballoc2ndCount &&
10727  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10728  {
10729  ++nextAlloc2ndIndex;
10730  }
10731 
10732  // Found non-null allocation.
10733  if(nextAlloc2ndIndex < suballoc2ndCount)
10734  {
10735  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10736 
10737  // 1. Process free space before this allocation.
10738  if(lastOffset < suballoc.offset)
10739  {
10740  // There is free space from lastOffset to suballoc.offset.
10741  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10742  inoutStats.unusedSize += unusedRangeSize;
10743  ++inoutStats.unusedRangeCount;
10744  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10745  }
10746 
10747  // 2. Process this allocation.
10748  // There is allocation with suballoc.offset, suballoc.size.
10749  ++inoutStats.allocationCount;
10750 
10751  // 3. Prepare for next iteration.
10752  lastOffset = suballoc.offset + suballoc.size;
10753  ++nextAlloc2ndIndex;
10754  }
10755  // We are at the end.
10756  else
10757  {
10758  if(lastOffset < freeSpace2ndTo1stEnd)
10759  {
10760  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10761  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10762  inoutStats.unusedSize += unusedRangeSize;
10763  ++inoutStats.unusedRangeCount;
10764  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10765  }
10766 
10767  // End of loop.
10768  lastOffset = freeSpace2ndTo1stEnd;
10769  }
10770  }
10771  }
10772 
10773  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10774  const VkDeviceSize freeSpace1stTo2ndEnd =
10775  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10776  while(lastOffset < freeSpace1stTo2ndEnd)
10777  {
10778  // Find next non-null allocation or move nextAllocIndex to the end.
10779  while(nextAlloc1stIndex < suballoc1stCount &&
10780  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10781  {
10782  ++nextAlloc1stIndex;
10783  }
10784 
10785  // Found non-null allocation.
10786  if(nextAlloc1stIndex < suballoc1stCount)
10787  {
10788  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10789 
10790  // 1. Process free space before this allocation.
10791  if(lastOffset < suballoc.offset)
10792  {
10793  // There is free space from lastOffset to suballoc.offset.
10794  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10795  inoutStats.unusedSize += unusedRangeSize;
10796  ++inoutStats.unusedRangeCount;
10797  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10798  }
10799 
10800  // 2. Process this allocation.
10801  // There is allocation with suballoc.offset, suballoc.size.
10802  ++inoutStats.allocationCount;
10803 
10804  // 3. Prepare for next iteration.
10805  lastOffset = suballoc.offset + suballoc.size;
10806  ++nextAlloc1stIndex;
10807  }
10808  // We are at the end.
10809  else
10810  {
10811  if(lastOffset < freeSpace1stTo2ndEnd)
10812  {
10813  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10814  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10815  inoutStats.unusedSize += unusedRangeSize;
10816  ++inoutStats.unusedRangeCount;
10817  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10818  }
10819 
10820  // End of loop.
10821  lastOffset = freeSpace1stTo2ndEnd;
10822  }
10823  }
10824 
10825  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10826  {
10827  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10828  while(lastOffset < size)
10829  {
10830  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10831  while(nextAlloc2ndIndex != SIZE_MAX &&
10832  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10833  {
10834  --nextAlloc2ndIndex;
10835  }
10836 
10837  // Found non-null allocation.
10838  if(nextAlloc2ndIndex != SIZE_MAX)
10839  {
10840  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10841 
10842  // 1. Process free space before this allocation.
10843  if(lastOffset < suballoc.offset)
10844  {
10845  // There is free space from lastOffset to suballoc.offset.
10846  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10847  inoutStats.unusedSize += unusedRangeSize;
10848  ++inoutStats.unusedRangeCount;
10849  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10850  }
10851 
10852  // 2. Process this allocation.
10853  // There is allocation with suballoc.offset, suballoc.size.
10854  ++inoutStats.allocationCount;
10855 
10856  // 3. Prepare for next iteration.
10857  lastOffset = suballoc.offset + suballoc.size;
10858  --nextAlloc2ndIndex;
10859  }
10860  // We are at the end.
10861  else
10862  {
10863  if(lastOffset < size)
10864  {
10865  // There is free space from lastOffset to size.
10866  const VkDeviceSize unusedRangeSize = size - lastOffset;
10867  inoutStats.unusedSize += unusedRangeSize;
10868  ++inoutStats.unusedRangeCount;
10869  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10870  }
10871 
10872  // End of loop.
10873  lastOffset = size;
10874  }
10875  }
10876  }
10877 }
10878 
10879 #if VMA_STATS_STRING_ENABLED
10880 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10881 {
10882  const VkDeviceSize size = GetSize();
10883  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10884  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10885  const size_t suballoc1stCount = suballocations1st.size();
10886  const size_t suballoc2ndCount = suballocations2nd.size();
10887 
10888  // FIRST PASS
10889 
10890  size_t unusedRangeCount = 0;
10891  VkDeviceSize usedBytes = 0;
10892 
10893  VkDeviceSize lastOffset = 0;
10894 
10895  size_t alloc2ndCount = 0;
10896  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10897  {
10898  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10899  size_t nextAlloc2ndIndex = 0;
10900  while(lastOffset < freeSpace2ndTo1stEnd)
10901  {
10902  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10903  while(nextAlloc2ndIndex < suballoc2ndCount &&
10904  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10905  {
10906  ++nextAlloc2ndIndex;
10907  }
10908 
10909  // Found non-null allocation.
10910  if(nextAlloc2ndIndex < suballoc2ndCount)
10911  {
10912  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10913 
10914  // 1. Process free space before this allocation.
10915  if(lastOffset < suballoc.offset)
10916  {
10917  // There is free space from lastOffset to suballoc.offset.
10918  ++unusedRangeCount;
10919  }
10920 
10921  // 2. Process this allocation.
10922  // There is allocation with suballoc.offset, suballoc.size.
10923  ++alloc2ndCount;
10924  usedBytes += suballoc.size;
10925 
10926  // 3. Prepare for next iteration.
10927  lastOffset = suballoc.offset + suballoc.size;
10928  ++nextAlloc2ndIndex;
10929  }
10930  // We are at the end.
10931  else
10932  {
10933  if(lastOffset < freeSpace2ndTo1stEnd)
10934  {
10935  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10936  ++unusedRangeCount;
10937  }
10938 
10939  // End of loop.
10940  lastOffset = freeSpace2ndTo1stEnd;
10941  }
10942  }
10943  }
10944 
10945  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10946  size_t alloc1stCount = 0;
10947  const VkDeviceSize freeSpace1stTo2ndEnd =
10948  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10949  while(lastOffset < freeSpace1stTo2ndEnd)
10950  {
10951  // Find next non-null allocation or move nextAllocIndex to the end.
10952  while(nextAlloc1stIndex < suballoc1stCount &&
10953  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10954  {
10955  ++nextAlloc1stIndex;
10956  }
10957 
10958  // Found non-null allocation.
10959  if(nextAlloc1stIndex < suballoc1stCount)
10960  {
10961  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10962 
10963  // 1. Process free space before this allocation.
10964  if(lastOffset < suballoc.offset)
10965  {
10966  // There is free space from lastOffset to suballoc.offset.
10967  ++unusedRangeCount;
10968  }
10969 
10970  // 2. Process this allocation.
10971  // There is allocation with suballoc.offset, suballoc.size.
10972  ++alloc1stCount;
10973  usedBytes += suballoc.size;
10974 
10975  // 3. Prepare for next iteration.
10976  lastOffset = suballoc.offset + suballoc.size;
10977  ++nextAlloc1stIndex;
10978  }
10979  // We are at the end.
10980  else
10981  {
10982  if(lastOffset < size)
10983  {
10984  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10985  ++unusedRangeCount;
10986  }
10987 
10988  // End of loop.
10989  lastOffset = freeSpace1stTo2ndEnd;
10990  }
10991  }
10992 
10993  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10994  {
10995  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10996  while(lastOffset < size)
10997  {
10998  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10999  while(nextAlloc2ndIndex != SIZE_MAX &&
11000  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11001  {
11002  --nextAlloc2ndIndex;
11003  }
11004 
11005  // Found non-null allocation.
11006  if(nextAlloc2ndIndex != SIZE_MAX)
11007  {
11008  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11009 
11010  // 1. Process free space before this allocation.
11011  if(lastOffset < suballoc.offset)
11012  {
11013  // There is free space from lastOffset to suballoc.offset.
11014  ++unusedRangeCount;
11015  }
11016 
11017  // 2. Process this allocation.
11018  // There is allocation with suballoc.offset, suballoc.size.
11019  ++alloc2ndCount;
11020  usedBytes += suballoc.size;
11021 
11022  // 3. Prepare for next iteration.
11023  lastOffset = suballoc.offset + suballoc.size;
11024  --nextAlloc2ndIndex;
11025  }
11026  // We are at the end.
11027  else
11028  {
11029  if(lastOffset < size)
11030  {
11031  // There is free space from lastOffset to size.
11032  ++unusedRangeCount;
11033  }
11034 
11035  // End of loop.
11036  lastOffset = size;
11037  }
11038  }
11039  }
11040 
11041  const VkDeviceSize unusedBytes = size - usedBytes;
11042  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
11043 
11044  // SECOND PASS
11045  lastOffset = 0;
11046 
11047  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11048  {
11049  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
11050  size_t nextAlloc2ndIndex = 0;
11051  while(lastOffset < freeSpace2ndTo1stEnd)
11052  {
11053  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11054  while(nextAlloc2ndIndex < suballoc2ndCount &&
11055  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11056  {
11057  ++nextAlloc2ndIndex;
11058  }
11059 
11060  // Found non-null allocation.
11061  if(nextAlloc2ndIndex < suballoc2ndCount)
11062  {
11063  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11064 
11065  // 1. Process free space before this allocation.
11066  if(lastOffset < suballoc.offset)
11067  {
11068  // There is free space from lastOffset to suballoc.offset.
11069  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11070  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11071  }
11072 
11073  // 2. Process this allocation.
11074  // There is allocation with suballoc.offset, suballoc.size.
11075  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11076 
11077  // 3. Prepare for next iteration.
11078  lastOffset = suballoc.offset + suballoc.size;
11079  ++nextAlloc2ndIndex;
11080  }
11081  // We are at the end.
11082  else
11083  {
11084  if(lastOffset < freeSpace2ndTo1stEnd)
11085  {
11086  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
11087  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
11088  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11089  }
11090 
11091  // End of loop.
11092  lastOffset = freeSpace2ndTo1stEnd;
11093  }
11094  }
11095  }
11096 
11097  nextAlloc1stIndex = m_1stNullItemsBeginCount;
11098  while(lastOffset < freeSpace1stTo2ndEnd)
11099  {
11100  // Find next non-null allocation or move nextAllocIndex to the end.
11101  while(nextAlloc1stIndex < suballoc1stCount &&
11102  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
11103  {
11104  ++nextAlloc1stIndex;
11105  }
11106 
11107  // Found non-null allocation.
11108  if(nextAlloc1stIndex < suballoc1stCount)
11109  {
11110  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
11111 
11112  // 1. Process free space before this allocation.
11113  if(lastOffset < suballoc.offset)
11114  {
11115  // There is free space from lastOffset to suballoc.offset.
11116  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11117  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11118  }
11119 
11120  // 2. Process this allocation.
11121  // There is allocation with suballoc.offset, suballoc.size.
11122  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11123 
11124  // 3. Prepare for next iteration.
11125  lastOffset = suballoc.offset + suballoc.size;
11126  ++nextAlloc1stIndex;
11127  }
11128  // We are at the end.
11129  else
11130  {
11131  if(lastOffset < freeSpace1stTo2ndEnd)
11132  {
11133  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
11134  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
11135  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11136  }
11137 
11138  // End of loop.
11139  lastOffset = freeSpace1stTo2ndEnd;
11140  }
11141  }
11142 
11143  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11144  {
11145  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
11146  while(lastOffset < size)
11147  {
11148  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11149  while(nextAlloc2ndIndex != SIZE_MAX &&
11150  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11151  {
11152  --nextAlloc2ndIndex;
11153  }
11154 
11155  // Found non-null allocation.
11156  if(nextAlloc2ndIndex != SIZE_MAX)
11157  {
11158  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11159 
11160  // 1. Process free space before this allocation.
11161  if(lastOffset < suballoc.offset)
11162  {
11163  // There is free space from lastOffset to suballoc.offset.
11164  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11165  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11166  }
11167 
11168  // 2. Process this allocation.
11169  // There is allocation with suballoc.offset, suballoc.size.
11170  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11171 
11172  // 3. Prepare for next iteration.
11173  lastOffset = suballoc.offset + suballoc.size;
11174  --nextAlloc2ndIndex;
11175  }
11176  // We are at the end.
11177  else
11178  {
11179  if(lastOffset < size)
11180  {
11181  // There is free space from lastOffset to size.
11182  const VkDeviceSize unusedRangeSize = size - lastOffset;
11183  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11184  }
11185 
11186  // End of loop.
11187  lastOffset = size;
11188  }
11189  }
11190  }
11191 
11192  PrintDetailedMap_End(json);
11193 }
11194 #endif // #if VMA_STATS_STRING_ENABLED
11195 
11196 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
11197  uint32_t currentFrameIndex,
11198  uint32_t frameInUseCount,
11199  VkDeviceSize bufferImageGranularity,
11200  VkDeviceSize allocSize,
11201  VkDeviceSize allocAlignment,
11202  bool upperAddress,
11203  VmaSuballocationType allocType,
11204  bool canMakeOtherLost,
11205  uint32_t strategy,
11206  VmaAllocationRequest* pAllocationRequest)
11207 {
11208  VMA_ASSERT(allocSize > 0);
11209  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
11210  VMA_ASSERT(pAllocationRequest != VMA_NULL);
11211  VMA_HEAVY_ASSERT(Validate());
11212  return upperAddress ?
11213  CreateAllocationRequest_UpperAddress(
11214  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11215  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
11216  CreateAllocationRequest_LowerAddress(
11217  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11218  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
11219 }
11220 
11221 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
11222  uint32_t currentFrameIndex,
11223  uint32_t frameInUseCount,
11224  VkDeviceSize bufferImageGranularity,
11225  VkDeviceSize allocSize,
11226  VkDeviceSize allocAlignment,
11227  VmaSuballocationType allocType,
11228  bool canMakeOtherLost,
11229  uint32_t strategy,
11230  VmaAllocationRequest* pAllocationRequest)
11231 {
11232  const VkDeviceSize size = GetSize();
11233  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11234  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11235 
11236  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11237  {
11238  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
11239  return false;
11240  }
11241 
11242  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
11243  if(allocSize > size)
11244  {
11245  return false;
11246  }
11247  VkDeviceSize resultBaseOffset = size - allocSize;
11248  if(!suballocations2nd.empty())
11249  {
11250  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11251  resultBaseOffset = lastSuballoc.offset - allocSize;
11252  if(allocSize > lastSuballoc.offset)
11253  {
11254  return false;
11255  }
11256  }
11257 
11258  // Start from offset equal to end of free space.
11259  VkDeviceSize resultOffset = resultBaseOffset;
11260 
11261  // Apply VMA_DEBUG_MARGIN at the end.
11262  if(VMA_DEBUG_MARGIN > 0)
11263  {
11264  if(resultOffset < VMA_DEBUG_MARGIN)
11265  {
11266  return false;
11267  }
11268  resultOffset -= VMA_DEBUG_MARGIN;
11269  }
11270 
11271  // Apply alignment.
11272  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
11273 
11274  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
11275  // Make bigger alignment if necessary.
11276  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11277  {
11278  bool bufferImageGranularityConflict = false;
11279  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11280  {
11281  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11282  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11283  {
11284  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
11285  {
11286  bufferImageGranularityConflict = true;
11287  break;
11288  }
11289  }
11290  else
11291  // Already on previous page.
11292  break;
11293  }
11294  if(bufferImageGranularityConflict)
11295  {
11296  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
11297  }
11298  }
11299 
11300  // There is enough free space.
11301  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
11302  suballocations1st.back().offset + suballocations1st.back().size :
11303  0;
11304  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
11305  {
11306  // Check previous suballocations for BufferImageGranularity conflicts.
11307  // If conflict exists, allocation cannot be made here.
11308  if(bufferImageGranularity > 1)
11309  {
11310  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11311  {
11312  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11313  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11314  {
11315  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
11316  {
11317  return false;
11318  }
11319  }
11320  else
11321  {
11322  // Already on next page.
11323  break;
11324  }
11325  }
11326  }
11327 
11328  // All tests passed: Success.
11329  pAllocationRequest->offset = resultOffset;
11330  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
11331  pAllocationRequest->sumItemSize = 0;
11332  // pAllocationRequest->item unused.
11333  pAllocationRequest->itemsToMakeLostCount = 0;
11334  pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
11335  return true;
11336  }
11337 
11338  return false;
11339 }
11340 
11341 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
11342  uint32_t currentFrameIndex,
11343  uint32_t frameInUseCount,
11344  VkDeviceSize bufferImageGranularity,
11345  VkDeviceSize allocSize,
11346  VkDeviceSize allocAlignment,
11347  VmaSuballocationType allocType,
11348  bool canMakeOtherLost,
11349  uint32_t strategy,
11350  VmaAllocationRequest* pAllocationRequest)
11351 {
11352  const VkDeviceSize size = GetSize();
11353  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11354  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11355 
11356  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11357  {
11358  // Try to allocate at the end of 1st vector.
11359 
11360  VkDeviceSize resultBaseOffset = 0;
11361  if(!suballocations1st.empty())
11362  {
11363  const VmaSuballocation& lastSuballoc = suballocations1st.back();
11364  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11365  }
11366 
11367  // Start from offset equal to beginning of free space.
11368  VkDeviceSize resultOffset = resultBaseOffset;
11369 
11370  // Apply VMA_DEBUG_MARGIN at the beginning.
11371  if(VMA_DEBUG_MARGIN > 0)
11372  {
11373  resultOffset += VMA_DEBUG_MARGIN;
11374  }
11375 
11376  // Apply alignment.
11377  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11378 
11379  // Check previous suballocations for BufferImageGranularity conflicts.
11380  // Make bigger alignment if necessary.
11381  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
11382  {
11383  bool bufferImageGranularityConflict = false;
11384  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11385  {
11386  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11387  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11388  {
11389  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11390  {
11391  bufferImageGranularityConflict = true;
11392  break;
11393  }
11394  }
11395  else
11396  // Already on previous page.
11397  break;
11398  }
11399  if(bufferImageGranularityConflict)
11400  {
11401  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11402  }
11403  }
11404 
11405  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11406  suballocations2nd.back().offset : size;
11407 
11408  // There is enough free space at the end after alignment.
11409  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11410  {
11411  // Check next suballocations for BufferImageGranularity conflicts.
11412  // If conflict exists, allocation cannot be made here.
11413  if((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11414  {
11415  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11416  {
11417  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11418  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11419  {
11420  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11421  {
11422  return false;
11423  }
11424  }
11425  else
11426  {
11427  // Already on previous page.
11428  break;
11429  }
11430  }
11431  }
11432 
11433  // All tests passed: Success.
11434  pAllocationRequest->offset = resultOffset;
11435  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11436  pAllocationRequest->sumItemSize = 0;
11437  // pAllocationRequest->item, customData unused.
11438  pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11439  pAllocationRequest->itemsToMakeLostCount = 0;
11440  return true;
11441  }
11442  }
11443 
11444  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11445  // beginning of 1st vector as the end of free space.
11446  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11447  {
11448  VMA_ASSERT(!suballocations1st.empty());
11449 
11450  VkDeviceSize resultBaseOffset = 0;
11451  if(!suballocations2nd.empty())
11452  {
11453  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11454  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11455  }
11456 
11457  // Start from offset equal to beginning of free space.
11458  VkDeviceSize resultOffset = resultBaseOffset;
11459 
11460  // Apply VMA_DEBUG_MARGIN at the beginning.
11461  if(VMA_DEBUG_MARGIN > 0)
11462  {
11463  resultOffset += VMA_DEBUG_MARGIN;
11464  }
11465 
11466  // Apply alignment.
11467  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11468 
11469  // Check previous suballocations for BufferImageGranularity conflicts.
11470  // Make bigger alignment if necessary.
11471  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11472  {
11473  bool bufferImageGranularityConflict = false;
11474  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11475  {
11476  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11477  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11478  {
11479  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11480  {
11481  bufferImageGranularityConflict = true;
11482  break;
11483  }
11484  }
11485  else
11486  // Already on previous page.
11487  break;
11488  }
11489  if(bufferImageGranularityConflict)
11490  {
11491  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11492  }
11493  }
11494 
11495  pAllocationRequest->itemsToMakeLostCount = 0;
11496  pAllocationRequest->sumItemSize = 0;
11497  size_t index1st = m_1stNullItemsBeginCount;
11498 
11499  if(canMakeOtherLost)
11500  {
11501  while(index1st < suballocations1st.size() &&
11502  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11503  {
11504  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11505  const VmaSuballocation& suballoc = suballocations1st[index1st];
11506  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11507  {
11508  // No problem.
11509  }
11510  else
11511  {
11512  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11513  if(suballoc.hAllocation->CanBecomeLost() &&
11514  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11515  {
11516  ++pAllocationRequest->itemsToMakeLostCount;
11517  pAllocationRequest->sumItemSize += suballoc.size;
11518  }
11519  else
11520  {
11521  return false;
11522  }
11523  }
11524  ++index1st;
11525  }
11526 
11527  // Check next suballocations for BufferImageGranularity conflicts.
11528  // If conflict exists, we must mark more allocations lost or fail.
11529  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11530  {
11531  while(index1st < suballocations1st.size())
11532  {
11533  const VmaSuballocation& suballoc = suballocations1st[index1st];
11534  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11535  {
11536  if(suballoc.hAllocation != VK_NULL_HANDLE)
11537  {
11538  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11539  if(suballoc.hAllocation->CanBecomeLost() &&
11540  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11541  {
11542  ++pAllocationRequest->itemsToMakeLostCount;
11543  pAllocationRequest->sumItemSize += suballoc.size;
11544  }
11545  else
11546  {
11547  return false;
11548  }
11549  }
11550  }
11551  else
11552  {
11553  // Already on next page.
11554  break;
11555  }
11556  ++index1st;
11557  }
11558  }
11559 
11560  // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11561  if(index1st == suballocations1st.size() &&
11562  resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11563  {
11564  // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11565  VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11566  }
11567  }
11568 
11569  // There is enough free space at the end after alignment.
11570  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11571  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11572  {
11573  // Check next suballocations for BufferImageGranularity conflicts.
11574  // If conflict exists, allocation cannot be made here.
11575  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11576  {
11577  for(size_t nextSuballocIndex = index1st;
11578  nextSuballocIndex < suballocations1st.size();
11579  nextSuballocIndex++)
11580  {
11581  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11582  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11583  {
11584  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11585  {
11586  return false;
11587  }
11588  }
11589  else
11590  {
11591  // Already on next page.
11592  break;
11593  }
11594  }
11595  }
11596 
11597  // All tests passed: Success.
11598  pAllocationRequest->offset = resultOffset;
11599  pAllocationRequest->sumFreeSize =
11600  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11601  - resultBaseOffset
11602  - pAllocationRequest->sumItemSize;
11603  pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11604  // pAllocationRequest->item, customData unused.
11605  return true;
11606  }
11607  }
11608 
11609  return false;
11610 }
11611 
11612 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11613  uint32_t currentFrameIndex,
11614  uint32_t frameInUseCount,
11615  VmaAllocationRequest* pAllocationRequest)
11616 {
11617  if(pAllocationRequest->itemsToMakeLostCount == 0)
11618  {
11619  return true;
11620  }
11621 
11622  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11623 
11624  // We always start from 1st.
11625  SuballocationVectorType* suballocations = &AccessSuballocations1st();
11626  size_t index = m_1stNullItemsBeginCount;
11627  size_t madeLostCount = 0;
11628  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11629  {
11630  if(index == suballocations->size())
11631  {
11632  index = 0;
11633  // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11634  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11635  {
11636  suballocations = &AccessSuballocations2nd();
11637  }
11638  // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11639  // suballocations continues pointing at AccessSuballocations1st().
11640  VMA_ASSERT(!suballocations->empty());
11641  }
11642  VmaSuballocation& suballoc = (*suballocations)[index];
11643  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11644  {
11645  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11646  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11647  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11648  {
11649  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11650  suballoc.hAllocation = VK_NULL_HANDLE;
11651  m_SumFreeSize += suballoc.size;
11652  if(suballocations == &AccessSuballocations1st())
11653  {
11654  ++m_1stNullItemsMiddleCount;
11655  }
11656  else
11657  {
11658  ++m_2ndNullItemsCount;
11659  }
11660  ++madeLostCount;
11661  }
11662  else
11663  {
11664  return false;
11665  }
11666  }
11667  ++index;
11668  }
11669 
11670  CleanupAfterFree();
11671  //VMA_HEAVY_ASSERT(Validate()); // Already called by CleanupAfterFree().
11672 
11673  return true;
11674 }
11675 
11676 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11677 {
11678  uint32_t lostAllocationCount = 0;
11679 
11680  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11681  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11682  {
11683  VmaSuballocation& suballoc = suballocations1st[i];
11684  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11685  suballoc.hAllocation->CanBecomeLost() &&
11686  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11687  {
11688  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11689  suballoc.hAllocation = VK_NULL_HANDLE;
11690  ++m_1stNullItemsMiddleCount;
11691  m_SumFreeSize += suballoc.size;
11692  ++lostAllocationCount;
11693  }
11694  }
11695 
11696  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11697  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11698  {
11699  VmaSuballocation& suballoc = suballocations2nd[i];
11700  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11701  suballoc.hAllocation->CanBecomeLost() &&
11702  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11703  {
11704  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11705  suballoc.hAllocation = VK_NULL_HANDLE;
11706  ++m_2ndNullItemsCount;
11707  m_SumFreeSize += suballoc.size;
11708  ++lostAllocationCount;
11709  }
11710  }
11711 
11712  if(lostAllocationCount)
11713  {
11714  CleanupAfterFree();
11715  }
11716 
11717  return lostAllocationCount;
11718 }
11719 
11720 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11721 {
11722  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11723  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11724  {
11725  const VmaSuballocation& suballoc = suballocations1st[i];
11726  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11727  {
11728  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11729  {
11730  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11731  return VK_ERROR_VALIDATION_FAILED_EXT;
11732  }
11733  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11734  {
11735  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11736  return VK_ERROR_VALIDATION_FAILED_EXT;
11737  }
11738  }
11739  }
11740 
11741  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11742  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11743  {
11744  const VmaSuballocation& suballoc = suballocations2nd[i];
11745  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11746  {
11747  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11748  {
11749  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11750  return VK_ERROR_VALIDATION_FAILED_EXT;
11751  }
11752  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11753  {
11754  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11755  return VK_ERROR_VALIDATION_FAILED_EXT;
11756  }
11757  }
11758  }
11759 
11760  return VK_SUCCESS;
11761 }
11762 
11763 void VmaBlockMetadata_Linear::Alloc(
11764  const VmaAllocationRequest& request,
11765  VmaSuballocationType type,
11766  VkDeviceSize allocSize,
11767  VmaAllocation hAllocation)
11768 {
11769  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11770 
11771  switch(request.type)
11772  {
11773  case VmaAllocationRequestType::UpperAddress:
11774  {
11775  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11776  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11777  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11778  suballocations2nd.push_back(newSuballoc);
11779  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11780  }
11781  break;
11782  case VmaAllocationRequestType::EndOf1st:
11783  {
11784  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11785 
11786  VMA_ASSERT(suballocations1st.empty() ||
11787  request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11788  // Check if it fits before the end of the block.
11789  VMA_ASSERT(request.offset + allocSize <= GetSize());
11790 
11791  suballocations1st.push_back(newSuballoc);
11792  }
11793  break;
11794  case VmaAllocationRequestType::EndOf2nd:
11795  {
11796  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11797  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11798  VMA_ASSERT(!suballocations1st.empty() &&
11799  request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11800  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11801 
11802  switch(m_2ndVectorMode)
11803  {
11804  case SECOND_VECTOR_EMPTY:
11805  // First allocation from second part ring buffer.
11806  VMA_ASSERT(suballocations2nd.empty());
11807  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11808  break;
11809  case SECOND_VECTOR_RING_BUFFER:
11810  // 2-part ring buffer is already started.
11811  VMA_ASSERT(!suballocations2nd.empty());
11812  break;
11813  case SECOND_VECTOR_DOUBLE_STACK:
11814  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11815  break;
11816  default:
11817  VMA_ASSERT(0);
11818  }
11819 
11820  suballocations2nd.push_back(newSuballoc);
11821  }
11822  break;
11823  default:
11824  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11825  }
11826 
11827  m_SumFreeSize -= newSuballoc.size;
11828 }
11829 
11830 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11831 {
11832  FreeAtOffset(allocation->GetOffset());
11833 }
11834 
11835 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11836 {
11837  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11838  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11839 
11840  if(!suballocations1st.empty())
11841  {
11842  // First allocation: Mark it as next empty at the beginning.
11843  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11844  if(firstSuballoc.offset == offset)
11845  {
11846  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11847  firstSuballoc.hAllocation = VK_NULL_HANDLE;
11848  m_SumFreeSize += firstSuballoc.size;
11849  ++m_1stNullItemsBeginCount;
11850  CleanupAfterFree();
11851  return;
11852  }
11853  }
11854 
11855  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11856  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11857  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11858  {
11859  VmaSuballocation& lastSuballoc = suballocations2nd.back();
11860  if(lastSuballoc.offset == offset)
11861  {
11862  m_SumFreeSize += lastSuballoc.size;
11863  suballocations2nd.pop_back();
11864  CleanupAfterFree();
11865  return;
11866  }
11867  }
11868  // Last allocation in 1st vector.
11869  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11870  {
11871  VmaSuballocation& lastSuballoc = suballocations1st.back();
11872  if(lastSuballoc.offset == offset)
11873  {
11874  m_SumFreeSize += lastSuballoc.size;
11875  suballocations1st.pop_back();
11876  CleanupAfterFree();
11877  return;
11878  }
11879  }
11880 
11881  // Item from the middle of 1st vector.
11882  {
11883  VmaSuballocation refSuballoc;
11884  refSuballoc.offset = offset;
11885  // Rest of members stays uninitialized intentionally for better performance.
11886  SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11887  suballocations1st.begin() + m_1stNullItemsBeginCount,
11888  suballocations1st.end(),
11889  refSuballoc,
11890  VmaSuballocationOffsetLess());
11891  if(it != suballocations1st.end())
11892  {
11893  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11894  it->hAllocation = VK_NULL_HANDLE;
11895  ++m_1stNullItemsMiddleCount;
11896  m_SumFreeSize += it->size;
11897  CleanupAfterFree();
11898  return;
11899  }
11900  }
11901 
11902  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11903  {
11904  // Item from the middle of 2nd vector.
11905  VmaSuballocation refSuballoc;
11906  refSuballoc.offset = offset;
11907  // Rest of members stays uninitialized intentionally for better performance.
11908  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11909  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11910  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11911  if(it != suballocations2nd.end())
11912  {
11913  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11914  it->hAllocation = VK_NULL_HANDLE;
11915  ++m_2ndNullItemsCount;
11916  m_SumFreeSize += it->size;
11917  CleanupAfterFree();
11918  return;
11919  }
11920  }
11921 
11922  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11923 }
11924 
11925 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11926 {
11927  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11928  const size_t suballocCount = AccessSuballocations1st().size();
11929  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11930 }
11931 
11932 void VmaBlockMetadata_Linear::CleanupAfterFree()
11933 {
11934  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11935  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11936 
11937  if(IsEmpty())
11938  {
11939  suballocations1st.clear();
11940  suballocations2nd.clear();
11941  m_1stNullItemsBeginCount = 0;
11942  m_1stNullItemsMiddleCount = 0;
11943  m_2ndNullItemsCount = 0;
11944  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11945  }
11946  else
11947  {
11948  const size_t suballoc1stCount = suballocations1st.size();
11949  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11950  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11951 
11952  // Find more null items at the beginning of 1st vector.
11953  while(m_1stNullItemsBeginCount < suballoc1stCount &&
11954  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11955  {
11956  ++m_1stNullItemsBeginCount;
11957  --m_1stNullItemsMiddleCount;
11958  }
11959 
11960  // Find more null items at the end of 1st vector.
11961  while(m_1stNullItemsMiddleCount > 0 &&
11962  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11963  {
11964  --m_1stNullItemsMiddleCount;
11965  suballocations1st.pop_back();
11966  }
11967 
11968  // Find more null items at the end of 2nd vector.
11969  while(m_2ndNullItemsCount > 0 &&
11970  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11971  {
11972  --m_2ndNullItemsCount;
11973  suballocations2nd.pop_back();
11974  }
11975 
11976  // Find more null items at the beginning of 2nd vector.
11977  while(m_2ndNullItemsCount > 0 &&
11978  suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11979  {
11980  --m_2ndNullItemsCount;
11981  VmaVectorRemove(suballocations2nd, 0);
11982  }
11983 
11984  if(ShouldCompact1st())
11985  {
11986  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11987  size_t srcIndex = m_1stNullItemsBeginCount;
11988  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11989  {
11990  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11991  {
11992  ++srcIndex;
11993  }
11994  if(dstIndex != srcIndex)
11995  {
11996  suballocations1st[dstIndex] = suballocations1st[srcIndex];
11997  }
11998  ++srcIndex;
11999  }
12000  suballocations1st.resize(nonNullItemCount);
12001  m_1stNullItemsBeginCount = 0;
12002  m_1stNullItemsMiddleCount = 0;
12003  }
12004 
12005  // 2nd vector became empty.
12006  if(suballocations2nd.empty())
12007  {
12008  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
12009  }
12010 
12011  // 1st vector became empty.
12012  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
12013  {
12014  suballocations1st.clear();
12015  m_1stNullItemsBeginCount = 0;
12016 
12017  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
12018  {
12019  // Swap 1st with 2nd. Now 2nd is empty.
12020  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
12021  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
12022  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
12023  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
12024  {
12025  ++m_1stNullItemsBeginCount;
12026  --m_1stNullItemsMiddleCount;
12027  }
12028  m_2ndNullItemsCount = 0;
12029  m_1stVectorIndex ^= 1;
12030  }
12031  }
12032  }
12033 
12034  VMA_HEAVY_ASSERT(Validate());
12035 }
12036 
12037 
12039 // class VmaBlockMetadata_Buddy
12040 
12041 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
12042  VmaBlockMetadata(hAllocator),
12043  m_Root(VMA_NULL),
12044  m_AllocationCount(0),
12045  m_FreeCount(1),
12046  m_SumFreeSize(0)
12047 {
12048  memset(m_FreeList, 0, sizeof(m_FreeList));
12049 }
12050 
12051 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
12052 {
12053  DeleteNode(m_Root);
12054 }
12055 
12056 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
12057 {
12058  VmaBlockMetadata::Init(size);
12059 
12060  m_UsableSize = VmaPrevPow2(size);
12061  m_SumFreeSize = m_UsableSize;
12062 
12063  // Calculate m_LevelCount.
12064  m_LevelCount = 1;
12065  while(m_LevelCount < MAX_LEVELS &&
12066  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
12067  {
12068  ++m_LevelCount;
12069  }
12070 
12071  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
12072  rootNode->offset = 0;
12073  rootNode->type = Node::TYPE_FREE;
12074  rootNode->parent = VMA_NULL;
12075  rootNode->buddy = VMA_NULL;
12076 
12077  m_Root = rootNode;
12078  AddToFreeListFront(0, rootNode);
12079 }
12080 
12081 bool VmaBlockMetadata_Buddy::Validate() const
12082 {
12083  // Validate tree.
12084  ValidationContext ctx;
12085  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
12086  {
12087  VMA_VALIDATE(false && "ValidateNode failed.");
12088  }
12089  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
12090  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
12091 
12092  // Validate free node lists.
12093  for(uint32_t level = 0; level < m_LevelCount; ++level)
12094  {
12095  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
12096  m_FreeList[level].front->free.prev == VMA_NULL);
12097 
12098  for(Node* node = m_FreeList[level].front;
12099  node != VMA_NULL;
12100  node = node->free.next)
12101  {
12102  VMA_VALIDATE(node->type == Node::TYPE_FREE);
12103 
12104  if(node->free.next == VMA_NULL)
12105  {
12106  VMA_VALIDATE(m_FreeList[level].back == node);
12107  }
12108  else
12109  {
12110  VMA_VALIDATE(node->free.next->free.prev == node);
12111  }
12112  }
12113  }
12114 
12115  // Validate that free lists ar higher levels are empty.
12116  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
12117  {
12118  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
12119  }
12120 
12121  return true;
12122 }
12123 
12124 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
12125 {
12126  for(uint32_t level = 0; level < m_LevelCount; ++level)
12127  {
12128  if(m_FreeList[level].front != VMA_NULL)
12129  {
12130  return LevelToNodeSize(level);
12131  }
12132  }
12133  return 0;
12134 }
12135 
12136 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
12137 {
12138  const VkDeviceSize unusableSize = GetUnusableSize();
12139 
12140  outInfo.blockCount = 1;
12141 
12142  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
12143  outInfo.usedBytes = outInfo.unusedBytes = 0;
12144 
12145  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
12146  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
12147  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
12148 
12149  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
12150 
12151  if(unusableSize > 0)
12152  {
12153  ++outInfo.unusedRangeCount;
12154  outInfo.unusedBytes += unusableSize;
12155  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
12156  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
12157  }
12158 }
12159 
12160 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
12161 {
12162  const VkDeviceSize unusableSize = GetUnusableSize();
12163 
12164  inoutStats.size += GetSize();
12165  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
12166  inoutStats.allocationCount += m_AllocationCount;
12167  inoutStats.unusedRangeCount += m_FreeCount;
12168  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
12169 
12170  if(unusableSize > 0)
12171  {
12172  ++inoutStats.unusedRangeCount;
12173  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
12174  }
12175 }
12176 
12177 #if VMA_STATS_STRING_ENABLED
12178 
12179 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
12180 {
12181  // TODO optimize
12182  VmaStatInfo stat;
12183  CalcAllocationStatInfo(stat);
12184 
12185  PrintDetailedMap_Begin(
12186  json,
12187  stat.unusedBytes,
12188  stat.allocationCount,
12189  stat.unusedRangeCount);
12190 
12191  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
12192 
12193  const VkDeviceSize unusableSize = GetUnusableSize();
12194  if(unusableSize > 0)
12195  {
12196  PrintDetailedMap_UnusedRange(json,
12197  m_UsableSize, // offset
12198  unusableSize); // size
12199  }
12200 
12201  PrintDetailedMap_End(json);
12202 }
12203 
12204 #endif // #if VMA_STATS_STRING_ENABLED
12205 
12206 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
12207  uint32_t currentFrameIndex,
12208  uint32_t frameInUseCount,
12209  VkDeviceSize bufferImageGranularity,
12210  VkDeviceSize allocSize,
12211  VkDeviceSize allocAlignment,
12212  bool upperAddress,
12213  VmaSuballocationType allocType,
12214  bool canMakeOtherLost,
12215  uint32_t strategy,
12216  VmaAllocationRequest* pAllocationRequest)
12217 {
12218  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
12219 
12220  // Simple way to respect bufferImageGranularity. May be optimized some day.
12221  // Whenever it might be an OPTIMAL image...
12222  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
12223  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
12224  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
12225  {
12226  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
12227  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
12228  }
12229 
12230  if(allocSize > m_UsableSize)
12231  {
12232  return false;
12233  }
12234 
12235  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12236  for(uint32_t level = targetLevel + 1; level--; )
12237  {
12238  for(Node* freeNode = m_FreeList[level].front;
12239  freeNode != VMA_NULL;
12240  freeNode = freeNode->free.next)
12241  {
12242  if(freeNode->offset % allocAlignment == 0)
12243  {
12244  pAllocationRequest->type = VmaAllocationRequestType::Normal;
12245  pAllocationRequest->offset = freeNode->offset;
12246  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
12247  pAllocationRequest->sumItemSize = 0;
12248  pAllocationRequest->itemsToMakeLostCount = 0;
12249  pAllocationRequest->customData = (void*)(uintptr_t)level;
12250  return true;
12251  }
12252  }
12253  }
12254 
12255  return false;
12256 }
12257 
12258 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
12259  uint32_t currentFrameIndex,
12260  uint32_t frameInUseCount,
12261  VmaAllocationRequest* pAllocationRequest)
12262 {
12263  /*
12264  Lost allocations are not supported in buddy allocator at the moment.
12265  Support might be added in the future.
12266  */
12267  return pAllocationRequest->itemsToMakeLostCount == 0;
12268 }
12269 
12270 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
12271 {
12272  /*
12273  Lost allocations are not supported in buddy allocator at the moment.
12274  Support might be added in the future.
12275  */
12276  return 0;
12277 }
12278 
12279 void VmaBlockMetadata_Buddy::Alloc(
12280  const VmaAllocationRequest& request,
12281  VmaSuballocationType type,
12282  VkDeviceSize allocSize,
12283  VmaAllocation hAllocation)
12284 {
12285  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
12286 
12287  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12288  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
12289 
12290  Node* currNode = m_FreeList[currLevel].front;
12291  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12292  while(currNode->offset != request.offset)
12293  {
12294  currNode = currNode->free.next;
12295  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12296  }
12297 
12298  // Go down, splitting free nodes.
12299  while(currLevel < targetLevel)
12300  {
12301  // currNode is already first free node at currLevel.
12302  // Remove it from list of free nodes at this currLevel.
12303  RemoveFromFreeList(currLevel, currNode);
12304 
12305  const uint32_t childrenLevel = currLevel + 1;
12306 
12307  // Create two free sub-nodes.
12308  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
12309  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
12310 
12311  leftChild->offset = currNode->offset;
12312  leftChild->type = Node::TYPE_FREE;
12313  leftChild->parent = currNode;
12314  leftChild->buddy = rightChild;
12315 
12316  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
12317  rightChild->type = Node::TYPE_FREE;
12318  rightChild->parent = currNode;
12319  rightChild->buddy = leftChild;
12320 
12321  // Convert current currNode to split type.
12322  currNode->type = Node::TYPE_SPLIT;
12323  currNode->split.leftChild = leftChild;
12324 
12325  // Add child nodes to free list. Order is important!
12326  AddToFreeListFront(childrenLevel, rightChild);
12327  AddToFreeListFront(childrenLevel, leftChild);
12328 
12329  ++m_FreeCount;
12330  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
12331  ++currLevel;
12332  currNode = m_FreeList[currLevel].front;
12333 
12334  /*
12335  We can be sure that currNode, as left child of node previously split,
12336  also fullfills the alignment requirement.
12337  */
12338  }
12339 
12340  // Remove from free list.
12341  VMA_ASSERT(currLevel == targetLevel &&
12342  currNode != VMA_NULL &&
12343  currNode->type == Node::TYPE_FREE);
12344  RemoveFromFreeList(currLevel, currNode);
12345 
12346  // Convert to allocation node.
12347  currNode->type = Node::TYPE_ALLOCATION;
12348  currNode->allocation.alloc = hAllocation;
12349 
12350  ++m_AllocationCount;
12351  --m_FreeCount;
12352  m_SumFreeSize -= allocSize;
12353 }
12354 
12355 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
12356 {
12357  if(node->type == Node::TYPE_SPLIT)
12358  {
12359  DeleteNode(node->split.leftChild->buddy);
12360  DeleteNode(node->split.leftChild);
12361  }
12362 
12363  vma_delete(GetAllocationCallbacks(), node);
12364 }
12365 
12366 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
12367 {
12368  VMA_VALIDATE(level < m_LevelCount);
12369  VMA_VALIDATE(curr->parent == parent);
12370  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
12371  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
12372  switch(curr->type)
12373  {
12374  case Node::TYPE_FREE:
12375  // curr->free.prev, next are validated separately.
12376  ctx.calculatedSumFreeSize += levelNodeSize;
12377  ++ctx.calculatedFreeCount;
12378  break;
12379  case Node::TYPE_ALLOCATION:
12380  ++ctx.calculatedAllocationCount;
12381  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
12382  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
12383  break;
12384  case Node::TYPE_SPLIT:
12385  {
12386  const uint32_t childrenLevel = level + 1;
12387  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
12388  const Node* const leftChild = curr->split.leftChild;
12389  VMA_VALIDATE(leftChild != VMA_NULL);
12390  VMA_VALIDATE(leftChild->offset == curr->offset);
12391  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12392  {
12393  VMA_VALIDATE(false && "ValidateNode for left child failed.");
12394  }
12395  const Node* const rightChild = leftChild->buddy;
12396  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12397  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12398  {
12399  VMA_VALIDATE(false && "ValidateNode for right child failed.");
12400  }
12401  }
12402  break;
12403  default:
12404  return false;
12405  }
12406 
12407  return true;
12408 }
12409 
12410 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12411 {
12412  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12413  uint32_t level = 0;
12414  VkDeviceSize currLevelNodeSize = m_UsableSize;
12415  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12416  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12417  {
12418  ++level;
12419  currLevelNodeSize = nextLevelNodeSize;
12420  nextLevelNodeSize = currLevelNodeSize >> 1;
12421  }
12422  return level;
12423 }
12424 
12425 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12426 {
12427  // Find node and level.
12428  Node* node = m_Root;
12429  VkDeviceSize nodeOffset = 0;
12430  uint32_t level = 0;
12431  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12432  while(node->type == Node::TYPE_SPLIT)
12433  {
12434  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12435  if(offset < nodeOffset + nextLevelSize)
12436  {
12437  node = node->split.leftChild;
12438  }
12439  else
12440  {
12441  node = node->split.leftChild->buddy;
12442  nodeOffset += nextLevelSize;
12443  }
12444  ++level;
12445  levelNodeSize = nextLevelSize;
12446  }
12447 
12448  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12449  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12450 
12451  ++m_FreeCount;
12452  --m_AllocationCount;
12453  m_SumFreeSize += alloc->GetSize();
12454 
12455  node->type = Node::TYPE_FREE;
12456 
12457  // Join free nodes if possible.
12458  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12459  {
12460  RemoveFromFreeList(level, node->buddy);
12461  Node* const parent = node->parent;
12462 
12463  vma_delete(GetAllocationCallbacks(), node->buddy);
12464  vma_delete(GetAllocationCallbacks(), node);
12465  parent->type = Node::TYPE_FREE;
12466 
12467  node = parent;
12468  --level;
12469  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12470  --m_FreeCount;
12471  }
12472 
12473  AddToFreeListFront(level, node);
12474 }
12475 
12476 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12477 {
12478  switch(node->type)
12479  {
12480  case Node::TYPE_FREE:
12481  ++outInfo.unusedRangeCount;
12482  outInfo.unusedBytes += levelNodeSize;
12483  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12484  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12485  break;
12486  case Node::TYPE_ALLOCATION:
12487  {
12488  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12489  ++outInfo.allocationCount;
12490  outInfo.usedBytes += allocSize;
12491  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12492  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12493 
12494  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12495  if(unusedRangeSize > 0)
12496  {
12497  ++outInfo.unusedRangeCount;
12498  outInfo.unusedBytes += unusedRangeSize;
12499  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12500  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12501  }
12502  }
12503  break;
12504  case Node::TYPE_SPLIT:
12505  {
12506  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12507  const Node* const leftChild = node->split.leftChild;
12508  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12509  const Node* const rightChild = leftChild->buddy;
12510  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12511  }
12512  break;
12513  default:
12514  VMA_ASSERT(0);
12515  }
12516 }
12517 
12518 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12519 {
12520  VMA_ASSERT(node->type == Node::TYPE_FREE);
12521 
12522  // List is empty.
12523  Node* const frontNode = m_FreeList[level].front;
12524  if(frontNode == VMA_NULL)
12525  {
12526  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12527  node->free.prev = node->free.next = VMA_NULL;
12528  m_FreeList[level].front = m_FreeList[level].back = node;
12529  }
12530  else
12531  {
12532  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12533  node->free.prev = VMA_NULL;
12534  node->free.next = frontNode;
12535  frontNode->free.prev = node;
12536  m_FreeList[level].front = node;
12537  }
12538 }
12539 
12540 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12541 {
12542  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12543 
12544  // It is at the front.
12545  if(node->free.prev == VMA_NULL)
12546  {
12547  VMA_ASSERT(m_FreeList[level].front == node);
12548  m_FreeList[level].front = node->free.next;
12549  }
12550  else
12551  {
12552  Node* const prevFreeNode = node->free.prev;
12553  VMA_ASSERT(prevFreeNode->free.next == node);
12554  prevFreeNode->free.next = node->free.next;
12555  }
12556 
12557  // It is at the back.
12558  if(node->free.next == VMA_NULL)
12559  {
12560  VMA_ASSERT(m_FreeList[level].back == node);
12561  m_FreeList[level].back = node->free.prev;
12562  }
12563  else
12564  {
12565  Node* const nextFreeNode = node->free.next;
12566  VMA_ASSERT(nextFreeNode->free.prev == node);
12567  nextFreeNode->free.prev = node->free.prev;
12568  }
12569 }
12570 
12571 #if VMA_STATS_STRING_ENABLED
12572 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12573 {
12574  switch(node->type)
12575  {
12576  case Node::TYPE_FREE:
12577  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12578  break;
12579  case Node::TYPE_ALLOCATION:
12580  {
12581  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12582  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12583  if(allocSize < levelNodeSize)
12584  {
12585  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12586  }
12587  }
12588  break;
12589  case Node::TYPE_SPLIT:
12590  {
12591  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12592  const Node* const leftChild = node->split.leftChild;
12593  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12594  const Node* const rightChild = leftChild->buddy;
12595  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12596  }
12597  break;
12598  default:
12599  VMA_ASSERT(0);
12600  }
12601 }
12602 #endif // #if VMA_STATS_STRING_ENABLED
12603 
12604 
12606 // class VmaDeviceMemoryBlock
12607 
12608 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12609  m_pMetadata(VMA_NULL),
12610  m_MemoryTypeIndex(UINT32_MAX),
12611  m_Id(0),
12612  m_hMemory(VK_NULL_HANDLE),
12613  m_MapCount(0),
12614  m_pMappedData(VMA_NULL)
12615 {
12616 }
12617 
12618 void VmaDeviceMemoryBlock::Init(
12619  VmaAllocator hAllocator,
12620  VmaPool hParentPool,
12621  uint32_t newMemoryTypeIndex,
12622  VkDeviceMemory newMemory,
12623  VkDeviceSize newSize,
12624  uint32_t id,
12625  uint32_t algorithm)
12626 {
12627  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12628 
12629  m_hParentPool = hParentPool;
12630  m_MemoryTypeIndex = newMemoryTypeIndex;
12631  m_Id = id;
12632  m_hMemory = newMemory;
12633 
12634  switch(algorithm)
12635  {
12637  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12638  break;
12640  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12641  break;
12642  default:
12643  VMA_ASSERT(0);
12644  // Fall-through.
12645  case 0:
12646  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12647  }
12648  m_pMetadata->Init(newSize);
12649 }
12650 
12651 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12652 {
12653  // This is the most important assert in the entire library.
12654  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12655  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12656 
12657  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12658  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12659  m_hMemory = VK_NULL_HANDLE;
12660 
12661  vma_delete(allocator, m_pMetadata);
12662  m_pMetadata = VMA_NULL;
12663 }
12664 
12665 bool VmaDeviceMemoryBlock::Validate() const
12666 {
12667  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12668  (m_pMetadata->GetSize() != 0));
12669 
12670  return m_pMetadata->Validate();
12671 }
12672 
12673 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12674 {
12675  void* pData = nullptr;
12676  VkResult res = Map(hAllocator, 1, &pData);
12677  if(res != VK_SUCCESS)
12678  {
12679  return res;
12680  }
12681 
12682  res = m_pMetadata->CheckCorruption(pData);
12683 
12684  Unmap(hAllocator, 1);
12685 
12686  return res;
12687 }
12688 
12689 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12690 {
12691  if(count == 0)
12692  {
12693  return VK_SUCCESS;
12694  }
12695 
12696  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12697  if(m_MapCount != 0)
12698  {
12699  m_MapCount += count;
12700  VMA_ASSERT(m_pMappedData != VMA_NULL);
12701  if(ppData != VMA_NULL)
12702  {
12703  *ppData = m_pMappedData;
12704  }
12705  return VK_SUCCESS;
12706  }
12707  else
12708  {
12709  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12710  hAllocator->m_hDevice,
12711  m_hMemory,
12712  0, // offset
12713  VK_WHOLE_SIZE,
12714  0, // flags
12715  &m_pMappedData);
12716  if(result == VK_SUCCESS)
12717  {
12718  if(ppData != VMA_NULL)
12719  {
12720  *ppData = m_pMappedData;
12721  }
12722  m_MapCount = count;
12723  }
12724  return result;
12725  }
12726 }
12727 
12728 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12729 {
12730  if(count == 0)
12731  {
12732  return;
12733  }
12734 
12735  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12736  if(m_MapCount >= count)
12737  {
12738  m_MapCount -= count;
12739  if(m_MapCount == 0)
12740  {
12741  m_pMappedData = VMA_NULL;
12742  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12743  }
12744  }
12745  else
12746  {
12747  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12748  }
12749 }
12750 
12751 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12752 {
12753  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12754  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12755 
12756  void* pData;
12757  VkResult res = Map(hAllocator, 1, &pData);
12758  if(res != VK_SUCCESS)
12759  {
12760  return res;
12761  }
12762 
12763  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12764  VmaWriteMagicValue(pData, allocOffset + allocSize);
12765 
12766  Unmap(hAllocator, 1);
12767 
12768  return VK_SUCCESS;
12769 }
12770 
12771 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12772 {
12773  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12774  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12775 
12776  void* pData;
12777  VkResult res = Map(hAllocator, 1, &pData);
12778  if(res != VK_SUCCESS)
12779  {
12780  return res;
12781  }
12782 
12783  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12784  {
12785  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12786  }
12787  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12788  {
12789  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12790  }
12791 
12792  Unmap(hAllocator, 1);
12793 
12794  return VK_SUCCESS;
12795 }
12796 
12797 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12798  const VmaAllocator hAllocator,
12799  const VmaAllocation hAllocation,
12800  VkDeviceSize allocationLocalOffset,
12801  VkBuffer hBuffer,
12802  const void* pNext)
12803 {
12804  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12805  hAllocation->GetBlock() == this);
12806  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12807  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12808  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12809  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12810  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12811  return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12812 }
12813 
12814 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12815  const VmaAllocator hAllocator,
12816  const VmaAllocation hAllocation,
12817  VkDeviceSize allocationLocalOffset,
12818  VkImage hImage,
12819  const void* pNext)
12820 {
12821  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12822  hAllocation->GetBlock() == this);
12823  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12824  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12825  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12826  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12827  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12828  return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12829 }
12830 
12831 static void InitStatInfo(VmaStatInfo& outInfo)
12832 {
12833  memset(&outInfo, 0, sizeof(outInfo));
12834  outInfo.allocationSizeMin = UINT64_MAX;
12835  outInfo.unusedRangeSizeMin = UINT64_MAX;
12836 }
12837 
12838 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12839 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12840 {
12841  inoutInfo.blockCount += srcInfo.blockCount;
12842  inoutInfo.allocationCount += srcInfo.allocationCount;
12843  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12844  inoutInfo.usedBytes += srcInfo.usedBytes;
12845  inoutInfo.unusedBytes += srcInfo.unusedBytes;
12846  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12847  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12848  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12849  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12850 }
12851 
12852 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12853 {
12854  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12855  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12856  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12857  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12858 }
12859 
12860 VmaPool_T::VmaPool_T(
12861  VmaAllocator hAllocator,
12862  const VmaPoolCreateInfo& createInfo,
12863  VkDeviceSize preferredBlockSize) :
12864  m_BlockVector(
12865  hAllocator,
12866  this, // hParentPool
12867  createInfo.memoryTypeIndex,
12868  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12869  createInfo.minBlockCount,
12870  createInfo.maxBlockCount,
12871  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12872  createInfo.frameInUseCount,
12873  createInfo.blockSize != 0, // explicitBlockSize
12874  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK,
12875  createInfo.priority), // algorithm
12876  m_Id(0),
12877  m_Name(VMA_NULL)
12878 {
12879 }
12880 
12881 VmaPool_T::~VmaPool_T()
12882 {
12883  VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
12884 }
12885 
12886 void VmaPool_T::SetName(const char* pName)
12887 {
12888  const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12889  VmaFreeString(allocs, m_Name);
12890 
12891  if(pName != VMA_NULL)
12892  {
12893  m_Name = VmaCreateStringCopy(allocs, pName);
12894  }
12895  else
12896  {
12897  m_Name = VMA_NULL;
12898  }
12899 }
12900 
12901 #if VMA_STATS_STRING_ENABLED
12902 
12903 #endif // #if VMA_STATS_STRING_ENABLED
12904 
12905 VmaBlockVector::VmaBlockVector(
12906  VmaAllocator hAllocator,
12907  VmaPool hParentPool,
12908  uint32_t memoryTypeIndex,
12909  VkDeviceSize preferredBlockSize,
12910  size_t minBlockCount,
12911  size_t maxBlockCount,
12912  VkDeviceSize bufferImageGranularity,
12913  uint32_t frameInUseCount,
12914  bool explicitBlockSize,
12915  uint32_t algorithm,
12916  float priority) :
12917  m_hAllocator(hAllocator),
12918  m_hParentPool(hParentPool),
12919  m_MemoryTypeIndex(memoryTypeIndex),
12920  m_PreferredBlockSize(preferredBlockSize),
12921  m_MinBlockCount(minBlockCount),
12922  m_MaxBlockCount(maxBlockCount),
12923  m_BufferImageGranularity(bufferImageGranularity),
12924  m_FrameInUseCount(frameInUseCount),
12925  m_ExplicitBlockSize(explicitBlockSize),
12926  m_Algorithm(algorithm),
12927  m_Priority(priority),
12928  m_HasEmptyBlock(false),
12929  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12930  m_NextBlockId(0)
12931 {
12932 }
12933 
12934 VmaBlockVector::~VmaBlockVector()
12935 {
12936  for(size_t i = m_Blocks.size(); i--; )
12937  {
12938  m_Blocks[i]->Destroy(m_hAllocator);
12939  vma_delete(m_hAllocator, m_Blocks[i]);
12940  }
12941 }
12942 
12943 VkResult VmaBlockVector::CreateMinBlocks()
12944 {
12945  for(size_t i = 0; i < m_MinBlockCount; ++i)
12946  {
12947  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12948  if(res != VK_SUCCESS)
12949  {
12950  return res;
12951  }
12952  }
12953  return VK_SUCCESS;
12954 }
12955 
12956 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12957 {
12958  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12959 
12960  const size_t blockCount = m_Blocks.size();
12961 
12962  pStats->size = 0;
12963  pStats->unusedSize = 0;
12964  pStats->allocationCount = 0;
12965  pStats->unusedRangeCount = 0;
12966  pStats->unusedRangeSizeMax = 0;
12967  pStats->blockCount = blockCount;
12968 
12969  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12970  {
12971  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12972  VMA_ASSERT(pBlock);
12973  VMA_HEAVY_ASSERT(pBlock->Validate());
12974  pBlock->m_pMetadata->AddPoolStats(*pStats);
12975  }
12976 }
12977 
12978 bool VmaBlockVector::IsEmpty()
12979 {
12980  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12981  return m_Blocks.empty();
12982 }
12983 
12984 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12985 {
12986  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12987  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12988  (VMA_DEBUG_MARGIN > 0) &&
12989  (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12990  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12991 }
12992 
12993 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12994 
12995 VkResult VmaBlockVector::Allocate(
12996  uint32_t currentFrameIndex,
12997  VkDeviceSize size,
12998  VkDeviceSize alignment,
12999  const VmaAllocationCreateInfo& createInfo,
13000  VmaSuballocationType suballocType,
13001  size_t allocationCount,
13002  VmaAllocation* pAllocations)
13003 {
13004  size_t allocIndex;
13005  VkResult res = VK_SUCCESS;
13006 
13007  if(IsCorruptionDetectionEnabled())
13008  {
13009  size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
13010  alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
13011  }
13012 
13013  {
13014  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13015  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13016  {
13017  res = AllocatePage(
13018  currentFrameIndex,
13019  size,
13020  alignment,
13021  createInfo,
13022  suballocType,
13023  pAllocations + allocIndex);
13024  if(res != VK_SUCCESS)
13025  {
13026  break;
13027  }
13028  }
13029  }
13030 
13031  if(res != VK_SUCCESS)
13032  {
13033  // Free all already created allocations.
13034  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13035  while(allocIndex--)
13036  {
13037  VmaAllocation_T* const alloc = pAllocations[allocIndex];
13038  const VkDeviceSize allocSize = alloc->GetSize();
13039  Free(alloc);
13040  m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize);
13041  }
13042  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
13043  }
13044 
13045  return res;
13046 }
13047 
13048 VkResult VmaBlockVector::AllocatePage(
13049  uint32_t currentFrameIndex,
13050  VkDeviceSize size,
13051  VkDeviceSize alignment,
13052  const VmaAllocationCreateInfo& createInfo,
13053  VmaSuballocationType suballocType,
13054  VmaAllocation* pAllocation)
13055 {
13056  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13057  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
13058  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13059  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13060 
13061  VkDeviceSize freeMemory;
13062  {
13063  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13064  VmaBudget heapBudget = {};
13065  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13066  freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
13067  }
13068 
13069  const bool canFallbackToDedicated = !IsCustomPool();
13070  const bool canCreateNewBlock =
13071  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
13072  (m_Blocks.size() < m_MaxBlockCount) &&
13073  (freeMemory >= size || !canFallbackToDedicated);
13074  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
13075 
13076  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
13077  // Which in turn is available only when maxBlockCount = 1.
13078  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
13079  {
13080  canMakeOtherLost = false;
13081  }
13082 
13083  // Upper address can only be used with linear allocator and within single memory block.
13084  if(isUpperAddress &&
13085  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
13086  {
13087  return VK_ERROR_FEATURE_NOT_PRESENT;
13088  }
13089 
13090  // Validate strategy.
13091  switch(strategy)
13092  {
13093  case 0:
13095  break;
13099  break;
13100  default:
13101  return VK_ERROR_FEATURE_NOT_PRESENT;
13102  }
13103 
13104  // Early reject: requested allocation size is larger that maximum block size for this block vector.
13105  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
13106  {
13107  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13108  }
13109 
13110  /*
13111  Under certain condition, this whole section can be skipped for optimization, so
13112  we move on directly to trying to allocate with canMakeOtherLost. That's the case
13113  e.g. for custom pools with linear algorithm.
13114  */
13115  if(!canMakeOtherLost || canCreateNewBlock)
13116  {
13117  // 1. Search existing allocations. Try to allocate without making other allocations lost.
13118  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
13120 
13121  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13122  {
13123  // Use only last block.
13124  if(!m_Blocks.empty())
13125  {
13126  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
13127  VMA_ASSERT(pCurrBlock);
13128  VkResult res = AllocateFromBlock(
13129  pCurrBlock,
13130  currentFrameIndex,
13131  size,
13132  alignment,
13133  allocFlagsCopy,
13134  createInfo.pUserData,
13135  suballocType,
13136  strategy,
13137  pAllocation);
13138  if(res == VK_SUCCESS)
13139  {
13140  VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
13141  return VK_SUCCESS;
13142  }
13143  }
13144  }
13145  else
13146  {
13148  {
13149  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13150  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13151  {
13152  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13153  VMA_ASSERT(pCurrBlock);
13154  VkResult res = AllocateFromBlock(
13155  pCurrBlock,
13156  currentFrameIndex,
13157  size,
13158  alignment,
13159  allocFlagsCopy,
13160  createInfo.pUserData,
13161  suballocType,
13162  strategy,
13163  pAllocation);
13164  if(res == VK_SUCCESS)
13165  {
13166  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13167  return VK_SUCCESS;
13168  }
13169  }
13170  }
13171  else // WORST_FIT, FIRST_FIT
13172  {
13173  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13174  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13175  {
13176  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13177  VMA_ASSERT(pCurrBlock);
13178  VkResult res = AllocateFromBlock(
13179  pCurrBlock,
13180  currentFrameIndex,
13181  size,
13182  alignment,
13183  allocFlagsCopy,
13184  createInfo.pUserData,
13185  suballocType,
13186  strategy,
13187  pAllocation);
13188  if(res == VK_SUCCESS)
13189  {
13190  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13191  return VK_SUCCESS;
13192  }
13193  }
13194  }
13195  }
13196 
13197  // 2. Try to create new block.
13198  if(canCreateNewBlock)
13199  {
13200  // Calculate optimal size for new block.
13201  VkDeviceSize newBlockSize = m_PreferredBlockSize;
13202  uint32_t newBlockSizeShift = 0;
13203  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
13204 
13205  if(!m_ExplicitBlockSize)
13206  {
13207  // Allocate 1/8, 1/4, 1/2 as first blocks.
13208  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
13209  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
13210  {
13211  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13212  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
13213  {
13214  newBlockSize = smallerNewBlockSize;
13215  ++newBlockSizeShift;
13216  }
13217  else
13218  {
13219  break;
13220  }
13221  }
13222  }
13223 
13224  size_t newBlockIndex = 0;
13225  VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13226  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13227  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
13228  if(!m_ExplicitBlockSize)
13229  {
13230  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
13231  {
13232  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13233  if(smallerNewBlockSize >= size)
13234  {
13235  newBlockSize = smallerNewBlockSize;
13236  ++newBlockSizeShift;
13237  res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13238  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13239  }
13240  else
13241  {
13242  break;
13243  }
13244  }
13245  }
13246 
13247  if(res == VK_SUCCESS)
13248  {
13249  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
13250  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
13251 
13252  res = AllocateFromBlock(
13253  pBlock,
13254  currentFrameIndex,
13255  size,
13256  alignment,
13257  allocFlagsCopy,
13258  createInfo.pUserData,
13259  suballocType,
13260  strategy,
13261  pAllocation);
13262  if(res == VK_SUCCESS)
13263  {
13264  VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
13265  return VK_SUCCESS;
13266  }
13267  else
13268  {
13269  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
13270  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13271  }
13272  }
13273  }
13274  }
13275 
13276  // 3. Try to allocate from existing blocks with making other allocations lost.
13277  if(canMakeOtherLost)
13278  {
13279  uint32_t tryIndex = 0;
13280  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
13281  {
13282  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
13283  VmaAllocationRequest bestRequest = {};
13284  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
13285 
13286  // 1. Search existing allocations.
13288  {
13289  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13290  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13291  {
13292  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13293  VMA_ASSERT(pCurrBlock);
13294  VmaAllocationRequest currRequest = {};
13295  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13296  currentFrameIndex,
13297  m_FrameInUseCount,
13298  m_BufferImageGranularity,
13299  size,
13300  alignment,
13301  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13302  suballocType,
13303  canMakeOtherLost,
13304  strategy,
13305  &currRequest))
13306  {
13307  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13308  if(pBestRequestBlock == VMA_NULL ||
13309  currRequestCost < bestRequestCost)
13310  {
13311  pBestRequestBlock = pCurrBlock;
13312  bestRequest = currRequest;
13313  bestRequestCost = currRequestCost;
13314 
13315  if(bestRequestCost == 0)
13316  {
13317  break;
13318  }
13319  }
13320  }
13321  }
13322  }
13323  else // WORST_FIT, FIRST_FIT
13324  {
13325  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13326  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13327  {
13328  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13329  VMA_ASSERT(pCurrBlock);
13330  VmaAllocationRequest currRequest = {};
13331  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13332  currentFrameIndex,
13333  m_FrameInUseCount,
13334  m_BufferImageGranularity,
13335  size,
13336  alignment,
13337  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13338  suballocType,
13339  canMakeOtherLost,
13340  strategy,
13341  &currRequest))
13342  {
13343  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13344  if(pBestRequestBlock == VMA_NULL ||
13345  currRequestCost < bestRequestCost ||
13347  {
13348  pBestRequestBlock = pCurrBlock;
13349  bestRequest = currRequest;
13350  bestRequestCost = currRequestCost;
13351 
13352  if(bestRequestCost == 0 ||
13354  {
13355  break;
13356  }
13357  }
13358  }
13359  }
13360  }
13361 
13362  if(pBestRequestBlock != VMA_NULL)
13363  {
13364  if(mapped)
13365  {
13366  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13367  if(res != VK_SUCCESS)
13368  {
13369  return res;
13370  }
13371  }
13372 
13373  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13374  currentFrameIndex,
13375  m_FrameInUseCount,
13376  &bestRequest))
13377  {
13378  // Allocate from this pBlock.
13379  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13380  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13381  UpdateHasEmptyBlock();
13382  (*pAllocation)->InitBlockAllocation(
13383  pBestRequestBlock,
13384  bestRequest.offset,
13385  alignment,
13386  size,
13387  m_MemoryTypeIndex,
13388  suballocType,
13389  mapped,
13390  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13391  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13392  VMA_DEBUG_LOG(" Returned from existing block");
13393  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13394  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13395  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13396  {
13397  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13398  }
13399  if(IsCorruptionDetectionEnabled())
13400  {
13401  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13402  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13403  }
13404  return VK_SUCCESS;
13405  }
13406  // else: Some allocations must have been touched while we are here. Next try.
13407  }
13408  else
13409  {
13410  // Could not find place in any of the blocks - break outer loop.
13411  break;
13412  }
13413  }
13414  /* Maximum number of tries exceeded - a very unlike event when many other
13415  threads are simultaneously touching allocations making it impossible to make
13416  lost at the same time as we try to allocate. */
13417  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13418  {
13419  return VK_ERROR_TOO_MANY_OBJECTS;
13420  }
13421  }
13422 
13423  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13424 }
13425 
13426 void VmaBlockVector::Free(
13427  const VmaAllocation hAllocation)
13428 {
13429  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13430 
13431  bool budgetExceeded = false;
13432  {
13433  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13434  VmaBudget heapBudget = {};
13435  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13436  budgetExceeded = heapBudget.usage >= heapBudget.budget;
13437  }
13438 
13439  // Scope for lock.
13440  {
13441  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13442 
13443  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13444 
13445  if(IsCorruptionDetectionEnabled())
13446  {
13447  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13448  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13449  }
13450 
13451  if(hAllocation->IsPersistentMap())
13452  {
13453  pBlock->Unmap(m_hAllocator, 1);
13454  }
13455 
13456  pBlock->m_pMetadata->Free(hAllocation);
13457  VMA_HEAVY_ASSERT(pBlock->Validate());
13458 
13459  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13460 
13461  const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13462  // pBlock became empty after this deallocation.
13463  if(pBlock->m_pMetadata->IsEmpty())
13464  {
13465  // Already has empty block. We don't want to have two, so delete this one.
13466  if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13467  {
13468  pBlockToDelete = pBlock;
13469  Remove(pBlock);
13470  }
13471  // else: We now have an empty block - leave it.
13472  }
13473  // pBlock didn't become empty, but we have another empty block - find and free that one.
13474  // (This is optional, heuristics.)
13475  else if(m_HasEmptyBlock && canDeleteBlock)
13476  {
13477  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13478  if(pLastBlock->m_pMetadata->IsEmpty())
13479  {
13480  pBlockToDelete = pLastBlock;
13481  m_Blocks.pop_back();
13482  }
13483  }
13484 
13485  UpdateHasEmptyBlock();
13486  IncrementallySortBlocks();
13487  }
13488 
13489  // Destruction of a free block. Deferred until this point, outside of mutex
13490  // lock, for performance reason.
13491  if(pBlockToDelete != VMA_NULL)
13492  {
13493  VMA_DEBUG_LOG(" Deleted empty block");
13494  pBlockToDelete->Destroy(m_hAllocator);
13495  vma_delete(m_hAllocator, pBlockToDelete);
13496  }
13497 }
13498 
13499 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13500 {
13501  VkDeviceSize result = 0;
13502  for(size_t i = m_Blocks.size(); i--; )
13503  {
13504  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13505  if(result >= m_PreferredBlockSize)
13506  {
13507  break;
13508  }
13509  }
13510  return result;
13511 }
13512 
13513 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13514 {
13515  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13516  {
13517  if(m_Blocks[blockIndex] == pBlock)
13518  {
13519  VmaVectorRemove(m_Blocks, blockIndex);
13520  return;
13521  }
13522  }
13523  VMA_ASSERT(0);
13524 }
13525 
13526 void VmaBlockVector::IncrementallySortBlocks()
13527 {
13528  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13529  {
13530  // Bubble sort only until first swap.
13531  for(size_t i = 1; i < m_Blocks.size(); ++i)
13532  {
13533  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13534  {
13535  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13536  return;
13537  }
13538  }
13539  }
13540 }
13541 
13542 VkResult VmaBlockVector::AllocateFromBlock(
13543  VmaDeviceMemoryBlock* pBlock,
13544  uint32_t currentFrameIndex,
13545  VkDeviceSize size,
13546  VkDeviceSize alignment,
13547  VmaAllocationCreateFlags allocFlags,
13548  void* pUserData,
13549  VmaSuballocationType suballocType,
13550  uint32_t strategy,
13551  VmaAllocation* pAllocation)
13552 {
13553  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13554  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13555  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13556  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13557 
13558  VmaAllocationRequest currRequest = {};
13559  if(pBlock->m_pMetadata->CreateAllocationRequest(
13560  currentFrameIndex,
13561  m_FrameInUseCount,
13562  m_BufferImageGranularity,
13563  size,
13564  alignment,
13565  isUpperAddress,
13566  suballocType,
13567  false, // canMakeOtherLost
13568  strategy,
13569  &currRequest))
13570  {
13571  // Allocate from pCurrBlock.
13572  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13573 
13574  if(mapped)
13575  {
13576  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13577  if(res != VK_SUCCESS)
13578  {
13579  return res;
13580  }
13581  }
13582 
13583  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13584  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13585  UpdateHasEmptyBlock();
13586  (*pAllocation)->InitBlockAllocation(
13587  pBlock,
13588  currRequest.offset,
13589  alignment,
13590  size,
13591  m_MemoryTypeIndex,
13592  suballocType,
13593  mapped,
13594  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13595  VMA_HEAVY_ASSERT(pBlock->Validate());
13596  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13597  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13598  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13599  {
13600  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13601  }
13602  if(IsCorruptionDetectionEnabled())
13603  {
13604  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13605  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13606  }
13607  return VK_SUCCESS;
13608  }
13609  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13610 }
13611 
13612 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13613 {
13614  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13615  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13616  allocInfo.allocationSize = blockSize;
13617 
13618 #if VMA_BUFFER_DEVICE_ADDRESS
13619  // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13620  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13621  if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13622  {
13623  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13624  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13625  }
13626 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13627 
13628 #if VMA_MEMORY_PRIORITY
13629  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
13630  if(m_hAllocator->m_UseExtMemoryPriority)
13631  {
13632  priorityInfo.priority = m_Priority;
13633  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
13634  }
13635 #endif // #if VMA_MEMORY_PRIORITY
13636 
13637  VkDeviceMemory mem = VK_NULL_HANDLE;
13638  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13639  if(res < 0)
13640  {
13641  return res;
13642  }
13643 
13644  // New VkDeviceMemory successfully created.
13645 
13646  // Create new Allocation for it.
13647  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13648  pBlock->Init(
13649  m_hAllocator,
13650  m_hParentPool,
13651  m_MemoryTypeIndex,
13652  mem,
13653  allocInfo.allocationSize,
13654  m_NextBlockId++,
13655  m_Algorithm);
13656 
13657  m_Blocks.push_back(pBlock);
13658  if(pNewBlockIndex != VMA_NULL)
13659  {
13660  *pNewBlockIndex = m_Blocks.size() - 1;
13661  }
13662 
13663  return VK_SUCCESS;
13664 }
13665 
13666 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13667  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13668  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13669 {
13670  const size_t blockCount = m_Blocks.size();
13671  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13672 
13673  enum BLOCK_FLAG
13674  {
13675  BLOCK_FLAG_USED = 0x00000001,
13676  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13677  };
13678 
13679  struct BlockInfo
13680  {
13681  uint32_t flags;
13682  void* pMappedData;
13683  };
13684  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13685  blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13686  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13687 
13688  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13689  const size_t moveCount = moves.size();
13690  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13691  {
13692  const VmaDefragmentationMove& move = moves[moveIndex];
13693  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13694  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13695  }
13696 
13697  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13698 
13699  // Go over all blocks. Get mapped pointer or map if necessary.
13700  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13701  {
13702  BlockInfo& currBlockInfo = blockInfo[blockIndex];
13703  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13704  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13705  {
13706  currBlockInfo.pMappedData = pBlock->GetMappedData();
13707  // It is not originally mapped - map it.
13708  if(currBlockInfo.pMappedData == VMA_NULL)
13709  {
13710  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13711  if(pDefragCtx->res == VK_SUCCESS)
13712  {
13713  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13714  }
13715  }
13716  }
13717  }
13718 
13719  // Go over all moves. Do actual data transfer.
13720  if(pDefragCtx->res == VK_SUCCESS)
13721  {
13722  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13723  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13724 
13725  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13726  {
13727  const VmaDefragmentationMove& move = moves[moveIndex];
13728 
13729  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13730  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13731 
13732  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13733 
13734  // Invalidate source.
13735  if(isNonCoherent)
13736  {
13737  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13738  memRange.memory = pSrcBlock->GetDeviceMemory();
13739  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13740  memRange.size = VMA_MIN(
13741  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13742  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13743  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13744  }
13745 
13746  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13747  memmove(
13748  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13749  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13750  static_cast<size_t>(move.size));
13751 
13752  if(IsCorruptionDetectionEnabled())
13753  {
13754  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13755  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13756  }
13757 
13758  // Flush destination.
13759  if(isNonCoherent)
13760  {
13761  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13762  memRange.memory = pDstBlock->GetDeviceMemory();
13763  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13764  memRange.size = VMA_MIN(
13765  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13766  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13767  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13768  }
13769  }
13770  }
13771 
13772  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13773  // Regardless of pCtx->res == VK_SUCCESS.
13774  for(size_t blockIndex = blockCount; blockIndex--; )
13775  {
13776  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13777  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13778  {
13779  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13780  pBlock->Unmap(m_hAllocator, 1);
13781  }
13782  }
13783 }
13784 
13785 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13786  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13787  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13788  VkCommandBuffer commandBuffer)
13789 {
13790  const size_t blockCount = m_Blocks.size();
13791 
13792  pDefragCtx->blockContexts.resize(blockCount);
13793  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13794 
13795  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13796  const size_t moveCount = moves.size();
13797  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13798  {
13799  const VmaDefragmentationMove& move = moves[moveIndex];
13800 
13801  //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13802  {
13803  // Old school move still require us to map the whole block
13804  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13805  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13806  }
13807  }
13808 
13809  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13810 
13811  // Go over all blocks. Create and bind buffer for whole block if necessary.
13812  {
13813  VkBufferCreateInfo bufCreateInfo;
13814  VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13815 
13816  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13817  {
13818  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13819  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13820  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13821  {
13822  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13823  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13824  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13825  if(pDefragCtx->res == VK_SUCCESS)
13826  {
13827  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13828  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13829  }
13830  }
13831  }
13832  }
13833 
13834  // Go over all moves. Post data transfer commands to command buffer.
13835  if(pDefragCtx->res == VK_SUCCESS)
13836  {
13837  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13838  {
13839  const VmaDefragmentationMove& move = moves[moveIndex];
13840 
13841  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13842  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13843 
13844  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13845 
13846  VkBufferCopy region = {
13847  move.srcOffset,
13848  move.dstOffset,
13849  move.size };
13850  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13851  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13852  }
13853  }
13854 
13855  // Save buffers to defrag context for later destruction.
13856  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13857  {
13858  pDefragCtx->res = VK_NOT_READY;
13859  }
13860 }
13861 
13862 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13863 {
13864  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13865  {
13866  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13867  if(pBlock->m_pMetadata->IsEmpty())
13868  {
13869  if(m_Blocks.size() > m_MinBlockCount)
13870  {
13871  if(pDefragmentationStats != VMA_NULL)
13872  {
13873  ++pDefragmentationStats->deviceMemoryBlocksFreed;
13874  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13875  }
13876 
13877  VmaVectorRemove(m_Blocks, blockIndex);
13878  pBlock->Destroy(m_hAllocator);
13879  vma_delete(m_hAllocator, pBlock);
13880  }
13881  else
13882  {
13883  break;
13884  }
13885  }
13886  }
13887  UpdateHasEmptyBlock();
13888 }
13889 
13890 void VmaBlockVector::UpdateHasEmptyBlock()
13891 {
13892  m_HasEmptyBlock = false;
13893  for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13894  {
13895  VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13896  if(pBlock->m_pMetadata->IsEmpty())
13897  {
13898  m_HasEmptyBlock = true;
13899  break;
13900  }
13901  }
13902 }
13903 
13904 #if VMA_STATS_STRING_ENABLED
13905 
13906 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13907 {
13908  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13909 
13910  json.BeginObject();
13911 
13912  if(IsCustomPool())
13913  {
13914  const char* poolName = m_hParentPool->GetName();
13915  if(poolName != VMA_NULL && poolName[0] != '\0')
13916  {
13917  json.WriteString("Name");
13918  json.WriteString(poolName);
13919  }
13920 
13921  json.WriteString("MemoryTypeIndex");
13922  json.WriteNumber(m_MemoryTypeIndex);
13923 
13924  json.WriteString("BlockSize");
13925  json.WriteNumber(m_PreferredBlockSize);
13926 
13927  json.WriteString("BlockCount");
13928  json.BeginObject(true);
13929  if(m_MinBlockCount > 0)
13930  {
13931  json.WriteString("Min");
13932  json.WriteNumber((uint64_t)m_MinBlockCount);
13933  }
13934  if(m_MaxBlockCount < SIZE_MAX)
13935  {
13936  json.WriteString("Max");
13937  json.WriteNumber((uint64_t)m_MaxBlockCount);
13938  }
13939  json.WriteString("Cur");
13940  json.WriteNumber((uint64_t)m_Blocks.size());
13941  json.EndObject();
13942 
13943  if(m_FrameInUseCount > 0)
13944  {
13945  json.WriteString("FrameInUseCount");
13946  json.WriteNumber(m_FrameInUseCount);
13947  }
13948 
13949  if(m_Algorithm != 0)
13950  {
13951  json.WriteString("Algorithm");
13952  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13953  }
13954  }
13955  else
13956  {
13957  json.WriteString("PreferredBlockSize");
13958  json.WriteNumber(m_PreferredBlockSize);
13959  }
13960 
13961  json.WriteString("Blocks");
13962  json.BeginObject();
13963  for(size_t i = 0; i < m_Blocks.size(); ++i)
13964  {
13965  json.BeginString();
13966  json.ContinueString(m_Blocks[i]->GetId());
13967  json.EndString();
13968 
13969  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13970  }
13971  json.EndObject();
13972 
13973  json.EndObject();
13974 }
13975 
13976 #endif // #if VMA_STATS_STRING_ENABLED
13977 
13978 void VmaBlockVector::Defragment(
13979  class VmaBlockVectorDefragmentationContext* pCtx,
13981  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13982  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13983  VkCommandBuffer commandBuffer)
13984 {
13985  pCtx->res = VK_SUCCESS;
13986 
13987  const VkMemoryPropertyFlags memPropFlags =
13988  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13989  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13990 
13991  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13992  isHostVisible;
13993  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13994  !IsCorruptionDetectionEnabled() &&
13995  ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13996 
13997  // There are options to defragment this memory type.
13998  if(canDefragmentOnCpu || canDefragmentOnGpu)
13999  {
14000  bool defragmentOnGpu;
14001  // There is only one option to defragment this memory type.
14002  if(canDefragmentOnGpu != canDefragmentOnCpu)
14003  {
14004  defragmentOnGpu = canDefragmentOnGpu;
14005  }
14006  // Both options are available: Heuristics to choose the best one.
14007  else
14008  {
14009  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
14010  m_hAllocator->IsIntegratedGpu();
14011  }
14012 
14013  bool overlappingMoveSupported = !defragmentOnGpu;
14014 
14015  if(m_hAllocator->m_UseMutex)
14016  {
14018  {
14019  if(!m_Mutex.TryLockWrite())
14020  {
14021  pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
14022  return;
14023  }
14024  }
14025  else
14026  {
14027  m_Mutex.LockWrite();
14028  pCtx->mutexLocked = true;
14029  }
14030  }
14031 
14032  pCtx->Begin(overlappingMoveSupported, flags);
14033 
14034  // Defragment.
14035 
14036  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
14037  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
14038  pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
14039 
14040  // Accumulate statistics.
14041  if(pStats != VMA_NULL)
14042  {
14043  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
14044  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
14045  pStats->bytesMoved += bytesMoved;
14046  pStats->allocationsMoved += allocationsMoved;
14047  VMA_ASSERT(bytesMoved <= maxBytesToMove);
14048  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
14049  if(defragmentOnGpu)
14050  {
14051  maxGpuBytesToMove -= bytesMoved;
14052  maxGpuAllocationsToMove -= allocationsMoved;
14053  }
14054  else
14055  {
14056  maxCpuBytesToMove -= bytesMoved;
14057  maxCpuAllocationsToMove -= allocationsMoved;
14058  }
14059  }
14060 
14062  {
14063  if(m_hAllocator->m_UseMutex)
14064  m_Mutex.UnlockWrite();
14065 
14066  if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
14067  pCtx->res = VK_NOT_READY;
14068 
14069  return;
14070  }
14071 
14072  if(pCtx->res >= VK_SUCCESS)
14073  {
14074  if(defragmentOnGpu)
14075  {
14076  ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
14077  }
14078  else
14079  {
14080  ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
14081  }
14082  }
14083  }
14084 }
14085 
14086 void VmaBlockVector::DefragmentationEnd(
14087  class VmaBlockVectorDefragmentationContext* pCtx,
14088  uint32_t flags,
14089  VmaDefragmentationStats* pStats)
14090 {
14091  if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
14092  {
14093  VMA_ASSERT(pCtx->mutexLocked == false);
14094 
14095  // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
14096  // lock protecting us. Since we mutate state here, we have to take the lock out now
14097  m_Mutex.LockWrite();
14098  pCtx->mutexLocked = true;
14099  }
14100 
14101  // If the mutex isn't locked we didn't do any work and there is nothing to delete.
14102  if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
14103  {
14104  // Destroy buffers.
14105  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
14106  {
14107  VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
14108  if(blockCtx.hBuffer)
14109  {
14110  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
14111  }
14112  }
14113 
14114  if(pCtx->res >= VK_SUCCESS)
14115  {
14116  FreeEmptyBlocks(pStats);
14117  }
14118  }
14119 
14120  if(pCtx->mutexLocked)
14121  {
14122  VMA_ASSERT(m_hAllocator->m_UseMutex);
14123  m_Mutex.UnlockWrite();
14124  }
14125 }
14126 
14127 uint32_t VmaBlockVector::ProcessDefragmentations(
14128  class VmaBlockVectorDefragmentationContext *pCtx,
14129  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
14130 {
14131  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14132 
14133  const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
14134 
14135  for(uint32_t i = 0; i < moveCount; ++ i)
14136  {
14137  VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
14138 
14139  pMove->allocation = move.hAllocation;
14140  pMove->memory = move.pDstBlock->GetDeviceMemory();
14141  pMove->offset = move.dstOffset;
14142 
14143  ++ pMove;
14144  }
14145 
14146  pCtx->defragmentationMovesProcessed += moveCount;
14147 
14148  return moveCount;
14149 }
14150 
14151 void VmaBlockVector::CommitDefragmentations(
14152  class VmaBlockVectorDefragmentationContext *pCtx,
14153  VmaDefragmentationStats* pStats)
14154 {
14155  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14156 
14157  for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
14158  {
14159  const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
14160 
14161  move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
14162  move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
14163  }
14164 
14165  pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
14166  FreeEmptyBlocks(pStats);
14167 }
14168 
14169 size_t VmaBlockVector::CalcAllocationCount() const
14170 {
14171  size_t result = 0;
14172  for(size_t i = 0; i < m_Blocks.size(); ++i)
14173  {
14174  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
14175  }
14176  return result;
14177 }
14178 
14179 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
14180 {
14181  if(m_BufferImageGranularity == 1)
14182  {
14183  return false;
14184  }
14185  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
14186  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
14187  {
14188  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
14189  VMA_ASSERT(m_Algorithm == 0);
14190  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
14191  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
14192  {
14193  return true;
14194  }
14195  }
14196  return false;
14197 }
14198 
14199 void VmaBlockVector::MakePoolAllocationsLost(
14200  uint32_t currentFrameIndex,
14201  size_t* pLostAllocationCount)
14202 {
14203  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14204  size_t lostAllocationCount = 0;
14205  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14206  {
14207  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14208  VMA_ASSERT(pBlock);
14209  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
14210  }
14211  if(pLostAllocationCount != VMA_NULL)
14212  {
14213  *pLostAllocationCount = lostAllocationCount;
14214  }
14215 }
14216 
14217 VkResult VmaBlockVector::CheckCorruption()
14218 {
14219  if(!IsCorruptionDetectionEnabled())
14220  {
14221  return VK_ERROR_FEATURE_NOT_PRESENT;
14222  }
14223 
14224  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14225  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14226  {
14227  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14228  VMA_ASSERT(pBlock);
14229  VkResult res = pBlock->CheckCorruption(m_hAllocator);
14230  if(res != VK_SUCCESS)
14231  {
14232  return res;
14233  }
14234  }
14235  return VK_SUCCESS;
14236 }
14237 
14238 void VmaBlockVector::AddStats(VmaStats* pStats)
14239 {
14240  const uint32_t memTypeIndex = m_MemoryTypeIndex;
14241  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
14242 
14243  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14244 
14245  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14246  {
14247  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14248  VMA_ASSERT(pBlock);
14249  VMA_HEAVY_ASSERT(pBlock->Validate());
14250  VmaStatInfo allocationStatInfo;
14251  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
14252  VmaAddStatInfo(pStats->total, allocationStatInfo);
14253  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14254  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14255  }
14256 }
14257 
14259 // VmaDefragmentationAlgorithm_Generic members definition
14260 
14261 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
14262  VmaAllocator hAllocator,
14263  VmaBlockVector* pBlockVector,
14264  uint32_t currentFrameIndex,
14265  bool overlappingMoveSupported) :
14266  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14267  m_AllocationCount(0),
14268  m_AllAllocations(false),
14269  m_BytesMoved(0),
14270  m_AllocationsMoved(0),
14271  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
14272 {
14273  // Create block info for each block.
14274  const size_t blockCount = m_pBlockVector->m_Blocks.size();
14275  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14276  {
14277  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
14278  pBlockInfo->m_OriginalBlockIndex = blockIndex;
14279  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
14280  m_Blocks.push_back(pBlockInfo);
14281  }
14282 
14283  // Sort them by m_pBlock pointer value.
14284  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
14285 }
14286 
14287 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14288 {
14289  for(size_t i = m_Blocks.size(); i--; )
14290  {
14291  vma_delete(m_hAllocator, m_Blocks[i]);
14292  }
14293 }
14294 
14295 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14296 {
14297  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14298  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14299  {
14300  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14301  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14302  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14303  {
14304  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14305  (*it)->m_Allocations.push_back(allocInfo);
14306  }
14307  else
14308  {
14309  VMA_ASSERT(0);
14310  }
14311 
14312  ++m_AllocationCount;
14313  }
14314 }
14315 
14316 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14317  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14318  VkDeviceSize maxBytesToMove,
14319  uint32_t maxAllocationsToMove,
14320  bool freeOldAllocations)
14321 {
14322  if(m_Blocks.empty())
14323  {
14324  return VK_SUCCESS;
14325  }
14326 
14327  // This is a choice based on research.
14328  // Option 1:
14329  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14330  // Option 2:
14331  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14332  // Option 3:
14333  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14334 
14335  size_t srcBlockMinIndex = 0;
14336  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14337  /*
14338  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14339  {
14340  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14341  if(blocksWithNonMovableCount > 0)
14342  {
14343  srcBlockMinIndex = blocksWithNonMovableCount - 1;
14344  }
14345  }
14346  */
14347 
14348  size_t srcBlockIndex = m_Blocks.size() - 1;
14349  size_t srcAllocIndex = SIZE_MAX;
14350  for(;;)
14351  {
14352  // 1. Find next allocation to move.
14353  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14354  // 1.2. Then start from last to first m_Allocations.
14355  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14356  {
14357  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14358  {
14359  // Finished: no more allocations to process.
14360  if(srcBlockIndex == srcBlockMinIndex)
14361  {
14362  return VK_SUCCESS;
14363  }
14364  else
14365  {
14366  --srcBlockIndex;
14367  srcAllocIndex = SIZE_MAX;
14368  }
14369  }
14370  else
14371  {
14372  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14373  }
14374  }
14375 
14376  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14377  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14378 
14379  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14380  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14381  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14382  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14383 
14384  // 2. Try to find new place for this allocation in preceding or current block.
14385  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14386  {
14387  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14388  VmaAllocationRequest dstAllocRequest;
14389  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14390  m_CurrentFrameIndex,
14391  m_pBlockVector->GetFrameInUseCount(),
14392  m_pBlockVector->GetBufferImageGranularity(),
14393  size,
14394  alignment,
14395  false, // upperAddress
14396  suballocType,
14397  false, // canMakeOtherLost
14398  strategy,
14399  &dstAllocRequest) &&
14400  MoveMakesSense(
14401  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14402  {
14403  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14404 
14405  // Reached limit on number of allocations or bytes to move.
14406  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14407  (m_BytesMoved + size > maxBytesToMove))
14408  {
14409  return VK_SUCCESS;
14410  }
14411 
14412  VmaDefragmentationMove move = {};
14413  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14414  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14415  move.srcOffset = srcOffset;
14416  move.dstOffset = dstAllocRequest.offset;
14417  move.size = size;
14418  move.hAllocation = allocInfo.m_hAllocation;
14419  move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14420  move.pDstBlock = pDstBlockInfo->m_pBlock;
14421 
14422  moves.push_back(move);
14423 
14424  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14425  dstAllocRequest,
14426  suballocType,
14427  size,
14428  allocInfo.m_hAllocation);
14429 
14430  if(freeOldAllocations)
14431  {
14432  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14433  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14434  }
14435 
14436  if(allocInfo.m_pChanged != VMA_NULL)
14437  {
14438  *allocInfo.m_pChanged = VK_TRUE;
14439  }
14440 
14441  ++m_AllocationsMoved;
14442  m_BytesMoved += size;
14443 
14444  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14445 
14446  break;
14447  }
14448  }
14449 
14450  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14451 
14452  if(srcAllocIndex > 0)
14453  {
14454  --srcAllocIndex;
14455  }
14456  else
14457  {
14458  if(srcBlockIndex > 0)
14459  {
14460  --srcBlockIndex;
14461  srcAllocIndex = SIZE_MAX;
14462  }
14463  else
14464  {
14465  return VK_SUCCESS;
14466  }
14467  }
14468  }
14469 }
14470 
14471 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14472 {
14473  size_t result = 0;
14474  for(size_t i = 0; i < m_Blocks.size(); ++i)
14475  {
14476  if(m_Blocks[i]->m_HasNonMovableAllocations)
14477  {
14478  ++result;
14479  }
14480  }
14481  return result;
14482 }
14483 
14484 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14485  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14486  VkDeviceSize maxBytesToMove,
14487  uint32_t maxAllocationsToMove,
14489 {
14490  if(!m_AllAllocations && m_AllocationCount == 0)
14491  {
14492  return VK_SUCCESS;
14493  }
14494 
14495  const size_t blockCount = m_Blocks.size();
14496  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14497  {
14498  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14499 
14500  if(m_AllAllocations)
14501  {
14502  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14503  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14504  it != pMetadata->m_Suballocations.end();
14505  ++it)
14506  {
14507  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14508  {
14509  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14510  pBlockInfo->m_Allocations.push_back(allocInfo);
14511  }
14512  }
14513  }
14514 
14515  pBlockInfo->CalcHasNonMovableAllocations();
14516 
14517  // This is a choice based on research.
14518  // Option 1:
14519  pBlockInfo->SortAllocationsByOffsetDescending();
14520  // Option 2:
14521  //pBlockInfo->SortAllocationsBySizeDescending();
14522  }
14523 
14524  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14525  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14526 
14527  // This is a choice based on research.
14528  const uint32_t roundCount = 2;
14529 
14530  // Execute defragmentation rounds (the main part).
14531  VkResult result = VK_SUCCESS;
14532  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14533  {
14534  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14535  }
14536 
14537  return result;
14538 }
14539 
14540 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14541  size_t dstBlockIndex, VkDeviceSize dstOffset,
14542  size_t srcBlockIndex, VkDeviceSize srcOffset)
14543 {
14544  if(dstBlockIndex < srcBlockIndex)
14545  {
14546  return true;
14547  }
14548  if(dstBlockIndex > srcBlockIndex)
14549  {
14550  return false;
14551  }
14552  if(dstOffset < srcOffset)
14553  {
14554  return true;
14555  }
14556  return false;
14557 }
14558 
14560 // VmaDefragmentationAlgorithm_Fast
14561 
14562 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14563  VmaAllocator hAllocator,
14564  VmaBlockVector* pBlockVector,
14565  uint32_t currentFrameIndex,
14566  bool overlappingMoveSupported) :
14567  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14568  m_OverlappingMoveSupported(overlappingMoveSupported),
14569  m_AllocationCount(0),
14570  m_AllAllocations(false),
14571  m_BytesMoved(0),
14572  m_AllocationsMoved(0),
14573  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14574 {
14575  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14576 
14577 }
14578 
14579 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14580 {
14581 }
14582 
14583 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14584  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14585  VkDeviceSize maxBytesToMove,
14586  uint32_t maxAllocationsToMove,
14588 {
14589  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14590 
14591  const size_t blockCount = m_pBlockVector->GetBlockCount();
14592  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14593  {
14594  return VK_SUCCESS;
14595  }
14596 
14597  PreprocessMetadata();
14598 
14599  // Sort blocks in order from most destination.
14600 
14601  m_BlockInfos.resize(blockCount);
14602  for(size_t i = 0; i < blockCount; ++i)
14603  {
14604  m_BlockInfos[i].origBlockIndex = i;
14605  }
14606 
14607  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14608  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14609  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14610  });
14611 
14612  // THE MAIN ALGORITHM
14613 
14614  FreeSpaceDatabase freeSpaceDb;
14615 
14616  size_t dstBlockInfoIndex = 0;
14617  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14618  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14619  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14620  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14621  VkDeviceSize dstOffset = 0;
14622 
14623  bool end = false;
14624  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14625  {
14626  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14627  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14628  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14629  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14630  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14631  {
14632  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14633  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14634  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14635  if(m_AllocationsMoved == maxAllocationsToMove ||
14636  m_BytesMoved + srcAllocSize > maxBytesToMove)
14637  {
14638  end = true;
14639  break;
14640  }
14641  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14642 
14643  VmaDefragmentationMove move = {};
14644  // Try to place it in one of free spaces from the database.
14645  size_t freeSpaceInfoIndex;
14646  VkDeviceSize dstAllocOffset;
14647  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14648  freeSpaceInfoIndex, dstAllocOffset))
14649  {
14650  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14651  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14652  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14653 
14654  // Same block
14655  if(freeSpaceInfoIndex == srcBlockInfoIndex)
14656  {
14657  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14658 
14659  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14660 
14661  VmaSuballocation suballoc = *srcSuballocIt;
14662  suballoc.offset = dstAllocOffset;
14663  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14664  m_BytesMoved += srcAllocSize;
14665  ++m_AllocationsMoved;
14666 
14667  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14668  ++nextSuballocIt;
14669  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14670  srcSuballocIt = nextSuballocIt;
14671 
14672  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14673 
14674  move.srcBlockIndex = srcOrigBlockIndex;
14675  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14676  move.srcOffset = srcAllocOffset;
14677  move.dstOffset = dstAllocOffset;
14678  move.size = srcAllocSize;
14679 
14680  moves.push_back(move);
14681  }
14682  // Different block
14683  else
14684  {
14685  // MOVE OPTION 2: Move the allocation to a different block.
14686 
14687  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14688 
14689  VmaSuballocation suballoc = *srcSuballocIt;
14690  suballoc.offset = dstAllocOffset;
14691  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14692  m_BytesMoved += srcAllocSize;
14693  ++m_AllocationsMoved;
14694 
14695  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14696  ++nextSuballocIt;
14697  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14698  srcSuballocIt = nextSuballocIt;
14699 
14700  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14701 
14702  move.srcBlockIndex = srcOrigBlockIndex;
14703  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14704  move.srcOffset = srcAllocOffset;
14705  move.dstOffset = dstAllocOffset;
14706  move.size = srcAllocSize;
14707 
14708  moves.push_back(move);
14709  }
14710  }
14711  else
14712  {
14713  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14714 
14715  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14716  while(dstBlockInfoIndex < srcBlockInfoIndex &&
14717  dstAllocOffset + srcAllocSize > dstBlockSize)
14718  {
14719  // But before that, register remaining free space at the end of dst block.
14720  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14721 
14722  ++dstBlockInfoIndex;
14723  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14724  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14725  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14726  dstBlockSize = pDstMetadata->GetSize();
14727  dstOffset = 0;
14728  dstAllocOffset = 0;
14729  }
14730 
14731  // Same block
14732  if(dstBlockInfoIndex == srcBlockInfoIndex)
14733  {
14734  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14735 
14736  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14737 
14738  bool skipOver = overlap;
14739  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14740  {
14741  // If destination and source place overlap, skip if it would move it
14742  // by only < 1/64 of its size.
14743  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14744  }
14745 
14746  if(skipOver)
14747  {
14748  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14749 
14750  dstOffset = srcAllocOffset + srcAllocSize;
14751  ++srcSuballocIt;
14752  }
14753  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14754  else
14755  {
14756  srcSuballocIt->offset = dstAllocOffset;
14757  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14758  dstOffset = dstAllocOffset + srcAllocSize;
14759  m_BytesMoved += srcAllocSize;
14760  ++m_AllocationsMoved;
14761  ++srcSuballocIt;
14762 
14763  move.srcBlockIndex = srcOrigBlockIndex;
14764  move.dstBlockIndex = dstOrigBlockIndex;
14765  move.srcOffset = srcAllocOffset;
14766  move.dstOffset = dstAllocOffset;
14767  move.size = srcAllocSize;
14768 
14769  moves.push_back(move);
14770  }
14771  }
14772  // Different block
14773  else
14774  {
14775  // MOVE OPTION 2: Move the allocation to a different block.
14776 
14777  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14778  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14779 
14780  VmaSuballocation suballoc = *srcSuballocIt;
14781  suballoc.offset = dstAllocOffset;
14782  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14783  dstOffset = dstAllocOffset + srcAllocSize;
14784  m_BytesMoved += srcAllocSize;
14785  ++m_AllocationsMoved;
14786 
14787  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14788  ++nextSuballocIt;
14789  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14790  srcSuballocIt = nextSuballocIt;
14791 
14792  pDstMetadata->m_Suballocations.push_back(suballoc);
14793 
14794  move.srcBlockIndex = srcOrigBlockIndex;
14795  move.dstBlockIndex = dstOrigBlockIndex;
14796  move.srcOffset = srcAllocOffset;
14797  move.dstOffset = dstAllocOffset;
14798  move.size = srcAllocSize;
14799 
14800  moves.push_back(move);
14801  }
14802  }
14803  }
14804  }
14805 
14806  m_BlockInfos.clear();
14807 
14808  PostprocessMetadata();
14809 
14810  return VK_SUCCESS;
14811 }
14812 
14813 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14814 {
14815  const size_t blockCount = m_pBlockVector->GetBlockCount();
14816  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14817  {
14818  VmaBlockMetadata_Generic* const pMetadata =
14819  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14820  pMetadata->m_FreeCount = 0;
14821  pMetadata->m_SumFreeSize = pMetadata->GetSize();
14822  pMetadata->m_FreeSuballocationsBySize.clear();
14823  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14824  it != pMetadata->m_Suballocations.end(); )
14825  {
14826  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14827  {
14828  VmaSuballocationList::iterator nextIt = it;
14829  ++nextIt;
14830  pMetadata->m_Suballocations.erase(it);
14831  it = nextIt;
14832  }
14833  else
14834  {
14835  ++it;
14836  }
14837  }
14838  }
14839 }
14840 
14841 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14842 {
14843  const size_t blockCount = m_pBlockVector->GetBlockCount();
14844  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14845  {
14846  VmaBlockMetadata_Generic* const pMetadata =
14847  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14848  const VkDeviceSize blockSize = pMetadata->GetSize();
14849 
14850  // No allocations in this block - entire area is free.
14851  if(pMetadata->m_Suballocations.empty())
14852  {
14853  pMetadata->m_FreeCount = 1;
14854  //pMetadata->m_SumFreeSize is already set to blockSize.
14855  VmaSuballocation suballoc = {
14856  0, // offset
14857  blockSize, // size
14858  VMA_NULL, // hAllocation
14859  VMA_SUBALLOCATION_TYPE_FREE };
14860  pMetadata->m_Suballocations.push_back(suballoc);
14861  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14862  }
14863  // There are some allocations in this block.
14864  else
14865  {
14866  VkDeviceSize offset = 0;
14867  VmaSuballocationList::iterator it;
14868  for(it = pMetadata->m_Suballocations.begin();
14869  it != pMetadata->m_Suballocations.end();
14870  ++it)
14871  {
14872  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14873  VMA_ASSERT(it->offset >= offset);
14874 
14875  // Need to insert preceding free space.
14876  if(it->offset > offset)
14877  {
14878  ++pMetadata->m_FreeCount;
14879  const VkDeviceSize freeSize = it->offset - offset;
14880  VmaSuballocation suballoc = {
14881  offset, // offset
14882  freeSize, // size
14883  VMA_NULL, // hAllocation
14884  VMA_SUBALLOCATION_TYPE_FREE };
14885  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14886  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14887  {
14888  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14889  }
14890  }
14891 
14892  pMetadata->m_SumFreeSize -= it->size;
14893  offset = it->offset + it->size;
14894  }
14895 
14896  // Need to insert trailing free space.
14897  if(offset < blockSize)
14898  {
14899  ++pMetadata->m_FreeCount;
14900  const VkDeviceSize freeSize = blockSize - offset;
14901  VmaSuballocation suballoc = {
14902  offset, // offset
14903  freeSize, // size
14904  VMA_NULL, // hAllocation
14905  VMA_SUBALLOCATION_TYPE_FREE };
14906  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14907  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14908  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14909  {
14910  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14911  }
14912  }
14913 
14914  VMA_SORT(
14915  pMetadata->m_FreeSuballocationsBySize.begin(),
14916  pMetadata->m_FreeSuballocationsBySize.end(),
14917  VmaSuballocationItemSizeLess());
14918  }
14919 
14920  VMA_HEAVY_ASSERT(pMetadata->Validate());
14921  }
14922 }
14923 
14924 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14925 {
14926  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14927  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14928  while(it != pMetadata->m_Suballocations.end())
14929  {
14930  if(it->offset < suballoc.offset)
14931  {
14932  ++it;
14933  }
14934  }
14935  pMetadata->m_Suballocations.insert(it, suballoc);
14936 }
14937 
14939 // VmaBlockVectorDefragmentationContext
14940 
14941 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14942  VmaAllocator hAllocator,
14943  VmaPool hCustomPool,
14944  VmaBlockVector* pBlockVector,
14945  uint32_t currFrameIndex) :
14946  res(VK_SUCCESS),
14947  mutexLocked(false),
14948  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14949  defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14950  defragmentationMovesProcessed(0),
14951  defragmentationMovesCommitted(0),
14952  hasDefragmentationPlan(0),
14953  m_hAllocator(hAllocator),
14954  m_hCustomPool(hCustomPool),
14955  m_pBlockVector(pBlockVector),
14956  m_CurrFrameIndex(currFrameIndex),
14957  m_pAlgorithm(VMA_NULL),
14958  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14959  m_AllAllocations(false)
14960 {
14961 }
14962 
14963 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14964 {
14965  vma_delete(m_hAllocator, m_pAlgorithm);
14966 }
14967 
14968 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14969 {
14970  AllocInfo info = { hAlloc, pChanged };
14971  m_Allocations.push_back(info);
14972 }
14973 
14974 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14975 {
14976  const bool allAllocations = m_AllAllocations ||
14977  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14978 
14979  /********************************
14980  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14981  ********************************/
14982 
14983  /*
14984  Fast algorithm is supported only when certain criteria are met:
14985  - VMA_DEBUG_MARGIN is 0.
14986  - All allocations in this block vector are moveable.
14987  - There is no possibility of image/buffer granularity conflict.
14988  - The defragmentation is not incremental
14989  */
14990  if(VMA_DEBUG_MARGIN == 0 &&
14991  allAllocations &&
14992  !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14994  {
14995  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14996  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14997  }
14998  else
14999  {
15000  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
15001  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
15002  }
15003 
15004  if(allAllocations)
15005  {
15006  m_pAlgorithm->AddAll();
15007  }
15008  else
15009  {
15010  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
15011  {
15012  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
15013  }
15014  }
15015 }
15016 
15018 // VmaDefragmentationContext
15019 
15020 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
15021  VmaAllocator hAllocator,
15022  uint32_t currFrameIndex,
15023  uint32_t flags,
15024  VmaDefragmentationStats* pStats) :
15025  m_hAllocator(hAllocator),
15026  m_CurrFrameIndex(currFrameIndex),
15027  m_Flags(flags),
15028  m_pStats(pStats),
15029  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
15030 {
15031  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
15032 }
15033 
15034 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
15035 {
15036  for(size_t i = m_CustomPoolContexts.size(); i--; )
15037  {
15038  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
15039  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15040  vma_delete(m_hAllocator, pBlockVectorCtx);
15041  }
15042  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
15043  {
15044  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
15045  if(pBlockVectorCtx)
15046  {
15047  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15048  vma_delete(m_hAllocator, pBlockVectorCtx);
15049  }
15050  }
15051 }
15052 
15053 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
15054 {
15055  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15056  {
15057  VmaPool pool = pPools[poolIndex];
15058  VMA_ASSERT(pool);
15059  // Pools with algorithm other than default are not defragmented.
15060  if(pool->m_BlockVector.GetAlgorithm() == 0)
15061  {
15062  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15063 
15064  for(size_t i = m_CustomPoolContexts.size(); i--; )
15065  {
15066  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
15067  {
15068  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15069  break;
15070  }
15071  }
15072 
15073  if(!pBlockVectorDefragCtx)
15074  {
15075  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15076  m_hAllocator,
15077  pool,
15078  &pool->m_BlockVector,
15079  m_CurrFrameIndex);
15080  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15081  }
15082 
15083  pBlockVectorDefragCtx->AddAll();
15084  }
15085  }
15086 }
15087 
15088 void VmaDefragmentationContext_T::AddAllocations(
15089  uint32_t allocationCount,
15090  const VmaAllocation* pAllocations,
15091  VkBool32* pAllocationsChanged)
15092 {
15093  // Dispatch pAllocations among defragmentators. Create them when necessary.
15094  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15095  {
15096  const VmaAllocation hAlloc = pAllocations[allocIndex];
15097  VMA_ASSERT(hAlloc);
15098  // DedicatedAlloc cannot be defragmented.
15099  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
15100  // Lost allocation cannot be defragmented.
15101  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
15102  {
15103  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15104 
15105  const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
15106  // This allocation belongs to custom pool.
15107  if(hAllocPool != VK_NULL_HANDLE)
15108  {
15109  // Pools with algorithm other than default are not defragmented.
15110  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
15111  {
15112  for(size_t i = m_CustomPoolContexts.size(); i--; )
15113  {
15114  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
15115  {
15116  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15117  break;
15118  }
15119  }
15120  if(!pBlockVectorDefragCtx)
15121  {
15122  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15123  m_hAllocator,
15124  hAllocPool,
15125  &hAllocPool->m_BlockVector,
15126  m_CurrFrameIndex);
15127  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15128  }
15129  }
15130  }
15131  // This allocation belongs to default pool.
15132  else
15133  {
15134  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
15135  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
15136  if(!pBlockVectorDefragCtx)
15137  {
15138  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15139  m_hAllocator,
15140  VMA_NULL, // hCustomPool
15141  m_hAllocator->m_pBlockVectors[memTypeIndex],
15142  m_CurrFrameIndex);
15143  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
15144  }
15145  }
15146 
15147  if(pBlockVectorDefragCtx)
15148  {
15149  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
15150  &pAllocationsChanged[allocIndex] : VMA_NULL;
15151  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
15152  }
15153  }
15154  }
15155 }
15156 
15157 VkResult VmaDefragmentationContext_T::Defragment(
15158  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
15159  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
15160  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
15161 {
15162  if(pStats)
15163  {
15164  memset(pStats, 0, sizeof(VmaDefragmentationStats));
15165  }
15166 
15168  {
15169  // For incremental defragmetnations, we just earmark how much we can move
15170  // The real meat is in the defragmentation steps
15171  m_MaxCpuBytesToMove = maxCpuBytesToMove;
15172  m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
15173 
15174  m_MaxGpuBytesToMove = maxGpuBytesToMove;
15175  m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
15176 
15177  if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
15178  m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
15179  return VK_SUCCESS;
15180 
15181  return VK_NOT_READY;
15182  }
15183 
15184  if(commandBuffer == VK_NULL_HANDLE)
15185  {
15186  maxGpuBytesToMove = 0;
15187  maxGpuAllocationsToMove = 0;
15188  }
15189 
15190  VkResult res = VK_SUCCESS;
15191 
15192  // Process default pools.
15193  for(uint32_t memTypeIndex = 0;
15194  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
15195  ++memTypeIndex)
15196  {
15197  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15198  if(pBlockVectorCtx)
15199  {
15200  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15201  pBlockVectorCtx->GetBlockVector()->Defragment(
15202  pBlockVectorCtx,
15203  pStats, flags,
15204  maxCpuBytesToMove, maxCpuAllocationsToMove,
15205  maxGpuBytesToMove, maxGpuAllocationsToMove,
15206  commandBuffer);
15207  if(pBlockVectorCtx->res != VK_SUCCESS)
15208  {
15209  res = pBlockVectorCtx->res;
15210  }
15211  }
15212  }
15213 
15214  // Process custom pools.
15215  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15216  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
15217  ++customCtxIndex)
15218  {
15219  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15220  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15221  pBlockVectorCtx->GetBlockVector()->Defragment(
15222  pBlockVectorCtx,
15223  pStats, flags,
15224  maxCpuBytesToMove, maxCpuAllocationsToMove,
15225  maxGpuBytesToMove, maxGpuAllocationsToMove,
15226  commandBuffer);
15227  if(pBlockVectorCtx->res != VK_SUCCESS)
15228  {
15229  res = pBlockVectorCtx->res;
15230  }
15231  }
15232 
15233  return res;
15234 }
15235 
15236 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
15237 {
15238  VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
15239  uint32_t movesLeft = pInfo->moveCount;
15240 
15241  // Process default pools.
15242  for(uint32_t memTypeIndex = 0;
15243  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15244  ++memTypeIndex)
15245  {
15246  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15247  if(pBlockVectorCtx)
15248  {
15249  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15250 
15251  if(!pBlockVectorCtx->hasDefragmentationPlan)
15252  {
15253  pBlockVectorCtx->GetBlockVector()->Defragment(
15254  pBlockVectorCtx,
15255  m_pStats, m_Flags,
15256  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15257  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15258  VK_NULL_HANDLE);
15259 
15260  if(pBlockVectorCtx->res < VK_SUCCESS)
15261  continue;
15262 
15263  pBlockVectorCtx->hasDefragmentationPlan = true;
15264  }
15265 
15266  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15267  pBlockVectorCtx,
15268  pCurrentMove, movesLeft);
15269 
15270  movesLeft -= processed;
15271  pCurrentMove += processed;
15272  }
15273  }
15274 
15275  // Process custom pools.
15276  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15277  customCtxIndex < customCtxCount;
15278  ++customCtxIndex)
15279  {
15280  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15281  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15282 
15283  if(!pBlockVectorCtx->hasDefragmentationPlan)
15284  {
15285  pBlockVectorCtx->GetBlockVector()->Defragment(
15286  pBlockVectorCtx,
15287  m_pStats, m_Flags,
15288  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15289  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15290  VK_NULL_HANDLE);
15291 
15292  if(pBlockVectorCtx->res < VK_SUCCESS)
15293  continue;
15294 
15295  pBlockVectorCtx->hasDefragmentationPlan = true;
15296  }
15297 
15298  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15299  pBlockVectorCtx,
15300  pCurrentMove, movesLeft);
15301 
15302  movesLeft -= processed;
15303  pCurrentMove += processed;
15304  }
15305 
15306  pInfo->moveCount = pInfo->moveCount - movesLeft;
15307 
15308  return VK_SUCCESS;
15309 }
15310 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15311 {
15312  VkResult res = VK_SUCCESS;
15313 
15314  // Process default pools.
15315  for(uint32_t memTypeIndex = 0;
15316  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15317  ++memTypeIndex)
15318  {
15319  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15320  if(pBlockVectorCtx)
15321  {
15322  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15323 
15324  if(!pBlockVectorCtx->hasDefragmentationPlan)
15325  {
15326  res = VK_NOT_READY;
15327  continue;
15328  }
15329 
15330  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15331  pBlockVectorCtx, m_pStats);
15332 
15333  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15334  res = VK_NOT_READY;
15335  }
15336  }
15337 
15338  // Process custom pools.
15339  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15340  customCtxIndex < customCtxCount;
15341  ++customCtxIndex)
15342  {
15343  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15344  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15345 
15346  if(!pBlockVectorCtx->hasDefragmentationPlan)
15347  {
15348  res = VK_NOT_READY;
15349  continue;
15350  }
15351 
15352  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15353  pBlockVectorCtx, m_pStats);
15354 
15355  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15356  res = VK_NOT_READY;
15357  }
15358 
15359  return res;
15360 }
15361 
15363 // VmaRecorder
15364 
15365 #if VMA_RECORDING_ENABLED
15366 
15367 VmaRecorder::VmaRecorder() :
15368  m_UseMutex(true),
15369  m_Flags(0),
15370  m_File(VMA_NULL),
15371  m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15372 {
15373 }
15374 
15375 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15376 {
15377  m_UseMutex = useMutex;
15378  m_Flags = settings.flags;
15379 
15380 #if defined(_WIN32)
15381  // Open file for writing.
15382  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15383 
15384  if(err != 0)
15385  {
15386  return VK_ERROR_INITIALIZATION_FAILED;
15387  }
15388 #else
15389  // Open file for writing.
15390  m_File = fopen(settings.pFilePath, "wb");
15391 
15392  if(m_File == 0)
15393  {
15394  return VK_ERROR_INITIALIZATION_FAILED;
15395  }
15396 #endif
15397 
15398  // Write header.
15399  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15400  fprintf(m_File, "%s\n", "1,8");
15401 
15402  return VK_SUCCESS;
15403 }
15404 
15405 VmaRecorder::~VmaRecorder()
15406 {
15407  if(m_File != VMA_NULL)
15408  {
15409  fclose(m_File);
15410  }
15411 }
15412 
15413 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15414 {
15415  CallParams callParams;
15416  GetBasicParams(callParams);
15417 
15418  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15419  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15420  Flush();
15421 }
15422 
15423 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15424 {
15425  CallParams callParams;
15426  GetBasicParams(callParams);
15427 
15428  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15429  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15430  Flush();
15431 }
15432 
15433 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15434 {
15435  CallParams callParams;
15436  GetBasicParams(callParams);
15437 
15438  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15439  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15440  createInfo.memoryTypeIndex,
15441  createInfo.flags,
15442  createInfo.blockSize,
15443  (uint64_t)createInfo.minBlockCount,
15444  (uint64_t)createInfo.maxBlockCount,
15445  createInfo.frameInUseCount,
15446  pool);
15447  Flush();
15448 }
15449 
15450 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15451 {
15452  CallParams callParams;
15453  GetBasicParams(callParams);
15454 
15455  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15456  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15457  pool);
15458  Flush();
15459 }
15460 
15461 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15462  const VkMemoryRequirements& vkMemReq,
15463  const VmaAllocationCreateInfo& createInfo,
15464  VmaAllocation allocation)
15465 {
15466  CallParams callParams;
15467  GetBasicParams(callParams);
15468 
15469  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15470  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15471  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15472  vkMemReq.size,
15473  vkMemReq.alignment,
15474  vkMemReq.memoryTypeBits,
15475  createInfo.flags,
15476  createInfo.usage,
15477  createInfo.requiredFlags,
15478  createInfo.preferredFlags,
15479  createInfo.memoryTypeBits,
15480  createInfo.pool,
15481  allocation,
15482  userDataStr.GetString());
15483  Flush();
15484 }
15485 
15486 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15487  const VkMemoryRequirements& vkMemReq,
15488  const VmaAllocationCreateInfo& createInfo,
15489  uint64_t allocationCount,
15490  const VmaAllocation* pAllocations)
15491 {
15492  CallParams callParams;
15493  GetBasicParams(callParams);
15494 
15495  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15496  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15497  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15498  vkMemReq.size,
15499  vkMemReq.alignment,
15500  vkMemReq.memoryTypeBits,
15501  createInfo.flags,
15502  createInfo.usage,
15503  createInfo.requiredFlags,
15504  createInfo.preferredFlags,
15505  createInfo.memoryTypeBits,
15506  createInfo.pool);
15507  PrintPointerList(allocationCount, pAllocations);
15508  fprintf(m_File, ",%s\n", userDataStr.GetString());
15509  Flush();
15510 }
15511 
15512 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15513  const VkMemoryRequirements& vkMemReq,
15514  bool requiresDedicatedAllocation,
15515  bool prefersDedicatedAllocation,
15516  const VmaAllocationCreateInfo& createInfo,
15517  VmaAllocation allocation)
15518 {
15519  CallParams callParams;
15520  GetBasicParams(callParams);
15521 
15522  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15523  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15524  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,
15525  vkMemReq.size,
15526  vkMemReq.alignment,
15527  vkMemReq.memoryTypeBits,
15528  requiresDedicatedAllocation ? 1 : 0,
15529  prefersDedicatedAllocation ? 1 : 0,
15530  createInfo.flags,
15531  createInfo.usage,
15532  createInfo.requiredFlags,
15533  createInfo.preferredFlags,
15534  createInfo.memoryTypeBits,
15535  createInfo.pool,
15536  allocation,
15537  userDataStr.GetString());
15538  Flush();
15539 }
15540 
15541 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15542  const VkMemoryRequirements& vkMemReq,
15543  bool requiresDedicatedAllocation,
15544  bool prefersDedicatedAllocation,
15545  const VmaAllocationCreateInfo& createInfo,
15546  VmaAllocation allocation)
15547 {
15548  CallParams callParams;
15549  GetBasicParams(callParams);
15550 
15551  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15552  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15553  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,
15554  vkMemReq.size,
15555  vkMemReq.alignment,
15556  vkMemReq.memoryTypeBits,
15557  requiresDedicatedAllocation ? 1 : 0,
15558  prefersDedicatedAllocation ? 1 : 0,
15559  createInfo.flags,
15560  createInfo.usage,
15561  createInfo.requiredFlags,
15562  createInfo.preferredFlags,
15563  createInfo.memoryTypeBits,
15564  createInfo.pool,
15565  allocation,
15566  userDataStr.GetString());
15567  Flush();
15568 }
15569 
15570 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15571  VmaAllocation allocation)
15572 {
15573  CallParams callParams;
15574  GetBasicParams(callParams);
15575 
15576  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15577  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15578  allocation);
15579  Flush();
15580 }
15581 
15582 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15583  uint64_t allocationCount,
15584  const VmaAllocation* pAllocations)
15585 {
15586  CallParams callParams;
15587  GetBasicParams(callParams);
15588 
15589  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15590  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15591  PrintPointerList(allocationCount, pAllocations);
15592  fprintf(m_File, "\n");
15593  Flush();
15594 }
15595 
15596 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15597  VmaAllocation allocation,
15598  const void* pUserData)
15599 {
15600  CallParams callParams;
15601  GetBasicParams(callParams);
15602 
15603  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15604  UserDataString userDataStr(
15605  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15606  pUserData);
15607  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15608  allocation,
15609  userDataStr.GetString());
15610  Flush();
15611 }
15612 
15613 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15614  VmaAllocation allocation)
15615 {
15616  CallParams callParams;
15617  GetBasicParams(callParams);
15618 
15619  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15620  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15621  allocation);
15622  Flush();
15623 }
15624 
15625 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15626  VmaAllocation allocation)
15627 {
15628  CallParams callParams;
15629  GetBasicParams(callParams);
15630 
15631  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15632  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15633  allocation);
15634  Flush();
15635 }
15636 
15637 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15638  VmaAllocation allocation)
15639 {
15640  CallParams callParams;
15641  GetBasicParams(callParams);
15642 
15643  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15644  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15645  allocation);
15646  Flush();
15647 }
15648 
15649 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15650  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15651 {
15652  CallParams callParams;
15653  GetBasicParams(callParams);
15654 
15655  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15656  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15657  allocation,
15658  offset,
15659  size);
15660  Flush();
15661 }
15662 
15663 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15664  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15665 {
15666  CallParams callParams;
15667  GetBasicParams(callParams);
15668 
15669  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15670  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15671  allocation,
15672  offset,
15673  size);
15674  Flush();
15675 }
15676 
15677 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15678  const VkBufferCreateInfo& bufCreateInfo,
15679  const VmaAllocationCreateInfo& allocCreateInfo,
15680  VmaAllocation allocation)
15681 {
15682  CallParams callParams;
15683  GetBasicParams(callParams);
15684 
15685  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15686  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15687  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,
15688  bufCreateInfo.flags,
15689  bufCreateInfo.size,
15690  bufCreateInfo.usage,
15691  bufCreateInfo.sharingMode,
15692  allocCreateInfo.flags,
15693  allocCreateInfo.usage,
15694  allocCreateInfo.requiredFlags,
15695  allocCreateInfo.preferredFlags,
15696  allocCreateInfo.memoryTypeBits,
15697  allocCreateInfo.pool,
15698  allocation,
15699  userDataStr.GetString());
15700  Flush();
15701 }
15702 
15703 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15704  const VkImageCreateInfo& imageCreateInfo,
15705  const VmaAllocationCreateInfo& allocCreateInfo,
15706  VmaAllocation allocation)
15707 {
15708  CallParams callParams;
15709  GetBasicParams(callParams);
15710 
15711  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15712  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15713  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,
15714  imageCreateInfo.flags,
15715  imageCreateInfo.imageType,
15716  imageCreateInfo.format,
15717  imageCreateInfo.extent.width,
15718  imageCreateInfo.extent.height,
15719  imageCreateInfo.extent.depth,
15720  imageCreateInfo.mipLevels,
15721  imageCreateInfo.arrayLayers,
15722  imageCreateInfo.samples,
15723  imageCreateInfo.tiling,
15724  imageCreateInfo.usage,
15725  imageCreateInfo.sharingMode,
15726  imageCreateInfo.initialLayout,
15727  allocCreateInfo.flags,
15728  allocCreateInfo.usage,
15729  allocCreateInfo.requiredFlags,
15730  allocCreateInfo.preferredFlags,
15731  allocCreateInfo.memoryTypeBits,
15732  allocCreateInfo.pool,
15733  allocation,
15734  userDataStr.GetString());
15735  Flush();
15736 }
15737 
15738 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15739  VmaAllocation allocation)
15740 {
15741  CallParams callParams;
15742  GetBasicParams(callParams);
15743 
15744  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15745  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15746  allocation);
15747  Flush();
15748 }
15749 
15750 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15751  VmaAllocation allocation)
15752 {
15753  CallParams callParams;
15754  GetBasicParams(callParams);
15755 
15756  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15757  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15758  allocation);
15759  Flush();
15760 }
15761 
15762 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15763  VmaAllocation allocation)
15764 {
15765  CallParams callParams;
15766  GetBasicParams(callParams);
15767 
15768  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15769  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15770  allocation);
15771  Flush();
15772 }
15773 
15774 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15775  VmaAllocation allocation)
15776 {
15777  CallParams callParams;
15778  GetBasicParams(callParams);
15779 
15780  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15781  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15782  allocation);
15783  Flush();
15784 }
15785 
15786 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15787  VmaPool pool)
15788 {
15789  CallParams callParams;
15790  GetBasicParams(callParams);
15791 
15792  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15793  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15794  pool);
15795  Flush();
15796 }
15797 
15798 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15799  const VmaDefragmentationInfo2& info,
15801 {
15802  CallParams callParams;
15803  GetBasicParams(callParams);
15804 
15805  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15806  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15807  info.flags);
15808  PrintPointerList(info.allocationCount, info.pAllocations);
15809  fprintf(m_File, ",");
15810  PrintPointerList(info.poolCount, info.pPools);
15811  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15812  info.maxCpuBytesToMove,
15814  info.maxGpuBytesToMove,
15816  info.commandBuffer,
15817  ctx);
15818  Flush();
15819 }
15820 
15821 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15823 {
15824  CallParams callParams;
15825  GetBasicParams(callParams);
15826 
15827  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15828  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15829  ctx);
15830  Flush();
15831 }
15832 
15833 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15834  VmaPool pool,
15835  const char* name)
15836 {
15837  CallParams callParams;
15838  GetBasicParams(callParams);
15839 
15840  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15841  fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15842  pool, name != VMA_NULL ? name : "");
15843  Flush();
15844 }
15845 
15846 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15847 {
15848  if(pUserData != VMA_NULL)
15849  {
15850  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15851  {
15852  m_Str = (const char*)pUserData;
15853  }
15854  else
15855  {
15856  // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15857  snprintf(m_PtrStr, 17, "%p", pUserData);
15858  m_Str = m_PtrStr;
15859  }
15860  }
15861  else
15862  {
15863  m_Str = "";
15864  }
15865 }
15866 
15867 void VmaRecorder::WriteConfiguration(
15868  const VkPhysicalDeviceProperties& devProps,
15869  const VkPhysicalDeviceMemoryProperties& memProps,
15870  uint32_t vulkanApiVersion,
15871  bool dedicatedAllocationExtensionEnabled,
15872  bool bindMemory2ExtensionEnabled,
15873  bool memoryBudgetExtensionEnabled,
15874  bool deviceCoherentMemoryExtensionEnabled)
15875 {
15876  fprintf(m_File, "Config,Begin\n");
15877 
15878  fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15879 
15880  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15881  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15882  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15883  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15884  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15885  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15886 
15887  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15888  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15889  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15890 
15891  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15892  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15893  {
15894  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15895  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15896  }
15897  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15898  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15899  {
15900  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15901  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15902  }
15903 
15904  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15905  fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15906  fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15907  fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15908 
15909  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15910  fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15911  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15912  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15913  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15914  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15915  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15916  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15917  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15918 
15919  fprintf(m_File, "Config,End\n");
15920 }
15921 
15922 void VmaRecorder::GetBasicParams(CallParams& outParams)
15923 {
15924  #if defined(_WIN32)
15925  outParams.threadId = GetCurrentThreadId();
15926  #else
15927  // Use C++11 features to get thread id and convert it to uint32_t.
15928  // There is room for optimization since sstream is quite slow.
15929  // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15930  std::thread::id thread_id = std::this_thread::get_id();
15931  std::stringstream thread_id_to_string_converter;
15932  thread_id_to_string_converter << thread_id;
15933  std::string thread_id_as_string = thread_id_to_string_converter.str();
15934  outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15935  #endif
15936 
15937  auto current_time = std::chrono::high_resolution_clock::now();
15938 
15939  outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15940 }
15941 
15942 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15943 {
15944  if(count)
15945  {
15946  fprintf(m_File, "%p", pItems[0]);
15947  for(uint64_t i = 1; i < count; ++i)
15948  {
15949  fprintf(m_File, " %p", pItems[i]);
15950  }
15951  }
15952 }
15953 
15954 void VmaRecorder::Flush()
15955 {
15956  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15957  {
15958  fflush(m_File);
15959  }
15960 }
15961 
15962 #endif // #if VMA_RECORDING_ENABLED
15963 
15965 // VmaAllocationObjectAllocator
15966 
15967 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15968  m_Allocator(pAllocationCallbacks, 1024)
15969 {
15970 }
15971 
15972 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15973 {
15974  VmaMutexLock mutexLock(m_Mutex);
15975  return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15976 }
15977 
15978 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15979 {
15980  VmaMutexLock mutexLock(m_Mutex);
15981  m_Allocator.Free(hAlloc);
15982 }
15983 
15985 // VmaAllocator_T
15986 
15987 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15988  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15989  m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15990  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15991  m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15992  m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15993  m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15994  m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15995  m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
15996  m_hDevice(pCreateInfo->device),
15997  m_hInstance(pCreateInfo->instance),
15998  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15999  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
16000  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
16001  m_AllocationObjectAllocator(&m_AllocationCallbacks),
16002  m_HeapSizeLimitMask(0),
16003  m_DeviceMemoryCount(0),
16004  m_PreferredLargeHeapBlockSize(0),
16005  m_PhysicalDevice(pCreateInfo->physicalDevice),
16006  m_CurrentFrameIndex(0),
16007  m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
16008  m_NextPoolId(0),
16009  m_GlobalMemoryTypeBits(UINT32_MAX)
16011  ,m_pRecorder(VMA_NULL)
16012 #endif
16013 {
16014  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16015  {
16016  m_UseKhrDedicatedAllocation = false;
16017  m_UseKhrBindMemory2 = false;
16018  }
16019 
16020  if(VMA_DEBUG_DETECT_CORRUPTION)
16021  {
16022  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
16023  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
16024  }
16025 
16026  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
16027 
16028  if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16029  {
16030 #if !(VMA_DEDICATED_ALLOCATION)
16032  {
16033  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
16034  }
16035 #endif
16036 #if !(VMA_BIND_MEMORY2)
16037  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
16038  {
16039  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
16040  }
16041 #endif
16042  }
16043 #if !(VMA_MEMORY_BUDGET)
16044  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
16045  {
16046  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
16047  }
16048 #endif
16049 #if !(VMA_BUFFER_DEVICE_ADDRESS)
16050  if(m_UseKhrBufferDeviceAddress)
16051  {
16052  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.");
16053  }
16054 #endif
16055 #if VMA_VULKAN_VERSION < 1002000
16056  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
16057  {
16058  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
16059  }
16060 #endif
16061 #if VMA_VULKAN_VERSION < 1001000
16062  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16063  {
16064  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
16065  }
16066 #endif
16067 #if !(VMA_MEMORY_PRIORITY)
16068  if(m_UseExtMemoryPriority)
16069  {
16070  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
16071  }
16072 #endif
16073 
16074  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
16075  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
16076  memset(&m_MemProps, 0, sizeof(m_MemProps));
16077 
16078  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
16079  memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
16080 
16081  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
16082  {
16083  m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
16084  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
16085  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
16086  }
16087 
16088  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
16089 
16090  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
16091  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
16092 
16093  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
16094  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
16095  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
16096  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
16097 
16098  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
16099  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
16100 
16101  m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
16102 
16103  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
16104  {
16105  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
16106  {
16107  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
16108  if(limit != VK_WHOLE_SIZE)
16109  {
16110  m_HeapSizeLimitMask |= 1u << heapIndex;
16111  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
16112  {
16113  m_MemProps.memoryHeaps[heapIndex].size = limit;
16114  }
16115  }
16116  }
16117  }
16118 
16119  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16120  {
16121  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
16122 
16123  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
16124  this,
16125  VK_NULL_HANDLE, // hParentPool
16126  memTypeIndex,
16127  preferredBlockSize,
16128  0,
16129  SIZE_MAX,
16130  GetBufferImageGranularity(),
16131  pCreateInfo->frameInUseCount,
16132  false, // explicitBlockSize
16133  false, // linearAlgorithm
16134  0.5f); // priority (0.5 is the default per Vulkan spec)
16135  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
16136  // becase minBlockCount is 0.
16137  }
16138 }
16139 
16140 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
16141 {
16142  VkResult res = VK_SUCCESS;
16143 
16144  if(pCreateInfo->pRecordSettings != VMA_NULL &&
16145  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
16146  {
16147 #if VMA_RECORDING_ENABLED
16148  m_pRecorder = vma_new(this, VmaRecorder)();
16149  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
16150  if(res != VK_SUCCESS)
16151  {
16152  return res;
16153  }
16154  m_pRecorder->WriteConfiguration(
16155  m_PhysicalDeviceProperties,
16156  m_MemProps,
16157  m_VulkanApiVersion,
16158  m_UseKhrDedicatedAllocation,
16159  m_UseKhrBindMemory2,
16160  m_UseExtMemoryBudget,
16161  m_UseAmdDeviceCoherentMemory);
16162  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
16163 #else
16164  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
16165  return VK_ERROR_FEATURE_NOT_PRESENT;
16166 #endif
16167  }
16168 
16169 #if VMA_MEMORY_BUDGET
16170  if(m_UseExtMemoryBudget)
16171  {
16172  UpdateVulkanBudget();
16173  }
16174 #endif // #if VMA_MEMORY_BUDGET
16175 
16176  return res;
16177 }
16178 
16179 VmaAllocator_T::~VmaAllocator_T()
16180 {
16181 #if VMA_RECORDING_ENABLED
16182  if(m_pRecorder != VMA_NULL)
16183  {
16184  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
16185  vma_delete(this, m_pRecorder);
16186  }
16187 #endif
16188 
16189  VMA_ASSERT(m_Pools.IsEmpty());
16190 
16191  for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
16192  {
16193  if(!m_DedicatedAllocations[memTypeIndex].IsEmpty())
16194  {
16195  VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
16196  }
16197 
16198  vma_delete(this, m_pBlockVectors[memTypeIndex]);
16199  }
16200 }
16201 
16202 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
16203 {
16204 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16205  ImportVulkanFunctions_Static();
16206 #endif
16207 
16208  if(pVulkanFunctions != VMA_NULL)
16209  {
16210  ImportVulkanFunctions_Custom(pVulkanFunctions);
16211  }
16212 
16213 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16214  ImportVulkanFunctions_Dynamic();
16215 #endif
16216 
16217  ValidateVulkanFunctions();
16218 }
16219 
16220 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16221 
16222 void VmaAllocator_T::ImportVulkanFunctions_Static()
16223 {
16224  // Vulkan 1.0
16225  m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
16226  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
16227  m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
16228  m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
16229  m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
16230  m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
16231  m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
16232  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
16233  m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
16234  m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
16235  m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
16236  m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
16237  m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
16238  m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
16239  m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
16240  m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
16241  m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
16242 
16243  // Vulkan 1.1
16244 #if VMA_VULKAN_VERSION >= 1001000
16245  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16246  {
16247  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
16248  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
16249  m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
16250  m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
16251  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
16252  }
16253 #endif
16254 }
16255 
16256 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16257 
16258 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
16259 {
16260  VMA_ASSERT(pVulkanFunctions != VMA_NULL);
16261 
16262 #define VMA_COPY_IF_NOT_NULL(funcName) \
16263  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
16264 
16265  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
16266  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
16267  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
16268  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
16269  VMA_COPY_IF_NOT_NULL(vkMapMemory);
16270  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
16271  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
16272  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
16273  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
16274  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
16275  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
16276  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
16277  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16278  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16279  VMA_COPY_IF_NOT_NULL(vkCreateImage);
16280  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16281  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16282 
16283 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16284  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16285  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16286 #endif
16287 
16288 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16289  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16290  VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16291 #endif
16292 
16293 #if VMA_MEMORY_BUDGET
16294  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16295 #endif
16296 
16297 #undef VMA_COPY_IF_NOT_NULL
16298 }
16299 
16300 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16301 
16302 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16303 {
16304 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16305  if(m_VulkanFunctions.memberName == VMA_NULL) \
16306  m_VulkanFunctions.memberName = \
16307  (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16308 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16309  if(m_VulkanFunctions.memberName == VMA_NULL) \
16310  m_VulkanFunctions.memberName = \
16311  (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16312 
16313  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16314  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16315  VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16316  VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16317  VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16318  VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16319  VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16320  VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16321  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16322  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16323  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16324  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16325  VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16326  VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16327  VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16328  VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16329  VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16330 
16331 #if VMA_VULKAN_VERSION >= 1001000
16332  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16333  {
16334  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
16335  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
16336  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
16337  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
16338  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
16339  }
16340 #endif
16341 
16342 #if VMA_DEDICATED_ALLOCATION
16343  if(m_UseKhrDedicatedAllocation)
16344  {
16345  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16346  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16347  }
16348 #endif
16349 
16350 #if VMA_BIND_MEMORY2
16351  if(m_UseKhrBindMemory2)
16352  {
16353  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16354  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16355  }
16356 #endif // #if VMA_BIND_MEMORY2
16357 
16358 #if VMA_MEMORY_BUDGET
16359  if(m_UseExtMemoryBudget)
16360  {
16361  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16362  }
16363 #endif // #if VMA_MEMORY_BUDGET
16364 
16365 #undef VMA_FETCH_DEVICE_FUNC
16366 #undef VMA_FETCH_INSTANCE_FUNC
16367 }
16368 
16369 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16370 
16371 void VmaAllocator_T::ValidateVulkanFunctions()
16372 {
16373  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16374  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16375  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16376  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16377  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16378  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16379  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16380  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16381  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16382  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16383  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16384  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16385  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16386  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16387  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16388  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16389  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16390 
16391 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16392  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16393  {
16394  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16395  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16396  }
16397 #endif
16398 
16399 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16400  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16401  {
16402  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16403  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16404  }
16405 #endif
16406 
16407 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16408  if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16409  {
16410  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16411  }
16412 #endif
16413 }
16414 
16415 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16416 {
16417  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16418  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16419  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16420  return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16421 }
16422 
16423 VkResult VmaAllocator_T::AllocateMemoryOfType(
16424  VkDeviceSize size,
16425  VkDeviceSize alignment,
16426  bool dedicatedAllocation,
16427  VkBuffer dedicatedBuffer,
16428  VkBufferUsageFlags dedicatedBufferUsage,
16429  VkImage dedicatedImage,
16430  const VmaAllocationCreateInfo& createInfo,
16431  uint32_t memTypeIndex,
16432  VmaSuballocationType suballocType,
16433  size_t allocationCount,
16434  VmaAllocation* pAllocations)
16435 {
16436  VMA_ASSERT(pAllocations != VMA_NULL);
16437  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16438 
16439  VmaAllocationCreateInfo finalCreateInfo = createInfo;
16440 
16441  // If memory type is not HOST_VISIBLE, disable MAPPED.
16442  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16443  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16444  {
16445  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16446  }
16447  // If memory is lazily allocated, it should be always dedicated.
16448  if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16449  {
16451  }
16452 
16453  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16454  VMA_ASSERT(blockVector);
16455 
16456  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16457  bool preferDedicatedMemory =
16458  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16459  dedicatedAllocation ||
16460  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16461  size > preferredBlockSize / 2;
16462 
16463  if(preferDedicatedMemory &&
16464  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16465  finalCreateInfo.pool == VK_NULL_HANDLE)
16466  {
16468  }
16469 
16470  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16471  {
16472  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16473  {
16474  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16475  }
16476  else
16477  {
16478  return AllocateDedicatedMemory(
16479  size,
16480  suballocType,
16481  memTypeIndex,
16482  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16483  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16484  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16485  finalCreateInfo.pUserData,
16486  finalCreateInfo.priority,
16487  dedicatedBuffer,
16488  dedicatedBufferUsage,
16489  dedicatedImage,
16490  allocationCount,
16491  pAllocations);
16492  }
16493  }
16494  else
16495  {
16496  VkResult res = blockVector->Allocate(
16497  m_CurrentFrameIndex.load(),
16498  size,
16499  alignment,
16500  finalCreateInfo,
16501  suballocType,
16502  allocationCount,
16503  pAllocations);
16504  if(res == VK_SUCCESS)
16505  {
16506  return res;
16507  }
16508 
16509  // 5. Try dedicated memory.
16510  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16511  {
16512  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16513  }
16514 
16515  // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
16516  // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
16517  // 3/4 of the maximum allocation count.
16518  if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
16519  {
16520  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16521  }
16522 
16523  res = AllocateDedicatedMemory(
16524  size,
16525  suballocType,
16526  memTypeIndex,
16527  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16528  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16529  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16530  finalCreateInfo.pUserData,
16531  finalCreateInfo.priority,
16532  dedicatedBuffer,
16533  dedicatedBufferUsage,
16534  dedicatedImage,
16535  allocationCount,
16536  pAllocations);
16537  if(res == VK_SUCCESS)
16538  {
16539  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16540  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16541  return VK_SUCCESS;
16542  }
16543  else
16544  {
16545  // Everything failed: Return error code.
16546  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16547  return res;
16548  }
16549  }
16550 }
16551 
16552 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16553  VkDeviceSize size,
16554  VmaSuballocationType suballocType,
16555  uint32_t memTypeIndex,
16556  bool withinBudget,
16557  bool map,
16558  bool isUserDataString,
16559  void* pUserData,
16560  float priority,
16561  VkBuffer dedicatedBuffer,
16562  VkBufferUsageFlags dedicatedBufferUsage,
16563  VkImage dedicatedImage,
16564  size_t allocationCount,
16565  VmaAllocation* pAllocations)
16566 {
16567  VMA_ASSERT(allocationCount > 0 && pAllocations);
16568 
16569  if(withinBudget)
16570  {
16571  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16572  VmaBudget heapBudget = {};
16573  GetBudget(&heapBudget, heapIndex, 1);
16574  if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16575  {
16576  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16577  }
16578  }
16579 
16580  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16581  allocInfo.memoryTypeIndex = memTypeIndex;
16582  allocInfo.allocationSize = size;
16583 
16584 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16585  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16586  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16587  {
16588  if(dedicatedBuffer != VK_NULL_HANDLE)
16589  {
16590  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16591  dedicatedAllocInfo.buffer = dedicatedBuffer;
16592  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16593  }
16594  else if(dedicatedImage != VK_NULL_HANDLE)
16595  {
16596  dedicatedAllocInfo.image = dedicatedImage;
16597  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16598  }
16599  }
16600 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16601 
16602 #if VMA_BUFFER_DEVICE_ADDRESS
16603  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16604  if(m_UseKhrBufferDeviceAddress)
16605  {
16606  bool canContainBufferWithDeviceAddress = true;
16607  if(dedicatedBuffer != VK_NULL_HANDLE)
16608  {
16609  canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16610  (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16611  }
16612  else if(dedicatedImage != VK_NULL_HANDLE)
16613  {
16614  canContainBufferWithDeviceAddress = false;
16615  }
16616  if(canContainBufferWithDeviceAddress)
16617  {
16618  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16619  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16620  }
16621  }
16622 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16623 
16624 #if VMA_MEMORY_PRIORITY
16625  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
16626  if(m_UseExtMemoryPriority)
16627  {
16628  priorityInfo.priority = priority;
16629  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
16630  }
16631 #endif // #if VMA_MEMORY_PRIORITY
16632 
16633  size_t allocIndex;
16634  VkResult res = VK_SUCCESS;
16635  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16636  {
16637  res = AllocateDedicatedMemoryPage(
16638  size,
16639  suballocType,
16640  memTypeIndex,
16641  allocInfo,
16642  map,
16643  isUserDataString,
16644  pUserData,
16645  pAllocations + allocIndex);
16646  if(res != VK_SUCCESS)
16647  {
16648  break;
16649  }
16650  }
16651 
16652  if(res == VK_SUCCESS)
16653  {
16654  // Register them in m_DedicatedAllocations.
16655  {
16656  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16657  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
16658  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16659  {
16660  dedicatedAllocations.PushBack(pAllocations[allocIndex]);
16661  }
16662  }
16663 
16664  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16665  }
16666  else
16667  {
16668  // Free all already created allocations.
16669  while(allocIndex--)
16670  {
16671  VmaAllocation currAlloc = pAllocations[allocIndex];
16672  VkDeviceMemory hMemory = currAlloc->GetMemory();
16673 
16674  /*
16675  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16676  before vkFreeMemory.
16677 
16678  if(currAlloc->GetMappedData() != VMA_NULL)
16679  {
16680  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16681  }
16682  */
16683 
16684  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16685  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16686  currAlloc->SetUserData(this, VMA_NULL);
16687  m_AllocationObjectAllocator.Free(currAlloc);
16688  }
16689 
16690  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16691  }
16692 
16693  return res;
16694 }
16695 
16696 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16697  VkDeviceSize size,
16698  VmaSuballocationType suballocType,
16699  uint32_t memTypeIndex,
16700  const VkMemoryAllocateInfo& allocInfo,
16701  bool map,
16702  bool isUserDataString,
16703  void* pUserData,
16704  VmaAllocation* pAllocation)
16705 {
16706  VkDeviceMemory hMemory = VK_NULL_HANDLE;
16707  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16708  if(res < 0)
16709  {
16710  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16711  return res;
16712  }
16713 
16714  void* pMappedData = VMA_NULL;
16715  if(map)
16716  {
16717  res = (*m_VulkanFunctions.vkMapMemory)(
16718  m_hDevice,
16719  hMemory,
16720  0,
16721  VK_WHOLE_SIZE,
16722  0,
16723  &pMappedData);
16724  if(res < 0)
16725  {
16726  VMA_DEBUG_LOG(" vkMapMemory FAILED");
16727  FreeVulkanMemory(memTypeIndex, size, hMemory);
16728  return res;
16729  }
16730  }
16731 
16732  *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16733  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16734  (*pAllocation)->SetUserData(this, pUserData);
16735  m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16736  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16737  {
16738  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16739  }
16740 
16741  return VK_SUCCESS;
16742 }
16743 
16744 void VmaAllocator_T::GetBufferMemoryRequirements(
16745  VkBuffer hBuffer,
16746  VkMemoryRequirements& memReq,
16747  bool& requiresDedicatedAllocation,
16748  bool& prefersDedicatedAllocation) const
16749 {
16750 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16751  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16752  {
16753  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16754  memReqInfo.buffer = hBuffer;
16755 
16756  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16757 
16758  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16759  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16760 
16761  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16762 
16763  memReq = memReq2.memoryRequirements;
16764  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16765  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16766  }
16767  else
16768 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16769  {
16770  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16771  requiresDedicatedAllocation = false;
16772  prefersDedicatedAllocation = false;
16773  }
16774 }
16775 
16776 void VmaAllocator_T::GetImageMemoryRequirements(
16777  VkImage hImage,
16778  VkMemoryRequirements& memReq,
16779  bool& requiresDedicatedAllocation,
16780  bool& prefersDedicatedAllocation) const
16781 {
16782 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16783  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16784  {
16785  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16786  memReqInfo.image = hImage;
16787 
16788  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16789 
16790  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16791  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16792 
16793  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16794 
16795  memReq = memReq2.memoryRequirements;
16796  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16797  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16798  }
16799  else
16800 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16801  {
16802  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16803  requiresDedicatedAllocation = false;
16804  prefersDedicatedAllocation = false;
16805  }
16806 }
16807 
16808 VkResult VmaAllocator_T::AllocateMemory(
16809  const VkMemoryRequirements& vkMemReq,
16810  bool requiresDedicatedAllocation,
16811  bool prefersDedicatedAllocation,
16812  VkBuffer dedicatedBuffer,
16813  VkBufferUsageFlags dedicatedBufferUsage,
16814  VkImage dedicatedImage,
16815  const VmaAllocationCreateInfo& createInfo,
16816  VmaSuballocationType suballocType,
16817  size_t allocationCount,
16818  VmaAllocation* pAllocations)
16819 {
16820  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16821 
16822  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16823 
16824  if(vkMemReq.size == 0)
16825  {
16826  return VK_ERROR_VALIDATION_FAILED_EXT;
16827  }
16828  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16829  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16830  {
16831  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16832  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16833  }
16834  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16836  {
16837  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16838  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16839  }
16840  if(requiresDedicatedAllocation)
16841  {
16842  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16843  {
16844  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16845  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16846  }
16847  if(createInfo.pool != VK_NULL_HANDLE)
16848  {
16849  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16850  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16851  }
16852  }
16853  if((createInfo.pool != VK_NULL_HANDLE) &&
16854  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16855  {
16856  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16857  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16858  }
16859 
16860  if(createInfo.pool != VK_NULL_HANDLE)
16861  {
16862  const VkDeviceSize alignmentForPool = VMA_MAX(
16863  vkMemReq.alignment,
16864  GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16865 
16866  VmaAllocationCreateInfo createInfoForPool = createInfo;
16867  // If memory type is not HOST_VISIBLE, disable MAPPED.
16868  if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16869  (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16870  {
16871  createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16872  }
16873 
16874  return createInfo.pool->m_BlockVector.Allocate(
16875  m_CurrentFrameIndex.load(),
16876  vkMemReq.size,
16877  alignmentForPool,
16878  createInfoForPool,
16879  suballocType,
16880  allocationCount,
16881  pAllocations);
16882  }
16883  else
16884  {
16885  // Bit mask of memory Vulkan types acceptable for this allocation.
16886  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16887  uint32_t memTypeIndex = UINT32_MAX;
16888  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16889  if(res == VK_SUCCESS)
16890  {
16891  VkDeviceSize alignmentForMemType = VMA_MAX(
16892  vkMemReq.alignment,
16893  GetMemoryTypeMinAlignment(memTypeIndex));
16894 
16895  res = AllocateMemoryOfType(
16896  vkMemReq.size,
16897  alignmentForMemType,
16898  requiresDedicatedAllocation || prefersDedicatedAllocation,
16899  dedicatedBuffer,
16900  dedicatedBufferUsage,
16901  dedicatedImage,
16902  createInfo,
16903  memTypeIndex,
16904  suballocType,
16905  allocationCount,
16906  pAllocations);
16907  // Succeeded on first try.
16908  if(res == VK_SUCCESS)
16909  {
16910  return res;
16911  }
16912  // Allocation from this memory type failed. Try other compatible memory types.
16913  else
16914  {
16915  for(;;)
16916  {
16917  // Remove old memTypeIndex from list of possibilities.
16918  memoryTypeBits &= ~(1u << memTypeIndex);
16919  // Find alternative memTypeIndex.
16920  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16921  if(res == VK_SUCCESS)
16922  {
16923  alignmentForMemType = VMA_MAX(
16924  vkMemReq.alignment,
16925  GetMemoryTypeMinAlignment(memTypeIndex));
16926 
16927  res = AllocateMemoryOfType(
16928  vkMemReq.size,
16929  alignmentForMemType,
16930  requiresDedicatedAllocation || prefersDedicatedAllocation,
16931  dedicatedBuffer,
16932  dedicatedBufferUsage,
16933  dedicatedImage,
16934  createInfo,
16935  memTypeIndex,
16936  suballocType,
16937  allocationCount,
16938  pAllocations);
16939  // Allocation from this alternative memory type succeeded.
16940  if(res == VK_SUCCESS)
16941  {
16942  return res;
16943  }
16944  // else: Allocation from this memory type failed. Try next one - next loop iteration.
16945  }
16946  // No other matching memory type index could be found.
16947  else
16948  {
16949  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16950  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16951  }
16952  }
16953  }
16954  }
16955  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16956  else
16957  return res;
16958  }
16959 }
16960 
16961 void VmaAllocator_T::FreeMemory(
16962  size_t allocationCount,
16963  const VmaAllocation* pAllocations)
16964 {
16965  VMA_ASSERT(pAllocations);
16966 
16967  for(size_t allocIndex = allocationCount; allocIndex--; )
16968  {
16969  VmaAllocation allocation = pAllocations[allocIndex];
16970 
16971  if(allocation != VK_NULL_HANDLE)
16972  {
16973  if(TouchAllocation(allocation))
16974  {
16975  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16976  {
16977  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16978  }
16979 
16980  switch(allocation->GetType())
16981  {
16982  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16983  {
16984  VmaBlockVector* pBlockVector = VMA_NULL;
16985  VmaPool hPool = allocation->GetBlock()->GetParentPool();
16986  if(hPool != VK_NULL_HANDLE)
16987  {
16988  pBlockVector = &hPool->m_BlockVector;
16989  }
16990  else
16991  {
16992  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16993  pBlockVector = m_pBlockVectors[memTypeIndex];
16994  }
16995  pBlockVector->Free(allocation);
16996  }
16997  break;
16998  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16999  FreeDedicatedMemory(allocation);
17000  break;
17001  default:
17002  VMA_ASSERT(0);
17003  }
17004  }
17005 
17006  // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
17007  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
17008  allocation->SetUserData(this, VMA_NULL);
17009  m_AllocationObjectAllocator.Free(allocation);
17010  }
17011  }
17012 }
17013 
17014 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
17015 {
17016  // Initialize.
17017  InitStatInfo(pStats->total);
17018  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
17019  InitStatInfo(pStats->memoryType[i]);
17020  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
17021  InitStatInfo(pStats->memoryHeap[i]);
17022 
17023  // Process default pools.
17024  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17025  {
17026  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17027  VMA_ASSERT(pBlockVector);
17028  pBlockVector->AddStats(pStats);
17029  }
17030 
17031  // Process custom pools.
17032  {
17033  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17034  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17035  {
17036  pool->m_BlockVector.AddStats(pStats);
17037  }
17038  }
17039 
17040  // Process dedicated allocations.
17041  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17042  {
17043  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
17044  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17045  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17046  for(VmaAllocation alloc = dedicatedAllocList.Front();
17047  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
17048  {
17049  VmaStatInfo allocationStatInfo;
17050  alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo);
17051  VmaAddStatInfo(pStats->total, allocationStatInfo);
17052  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
17053  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
17054  }
17055  }
17056 
17057  // Postprocess.
17058  VmaPostprocessCalcStatInfo(pStats->total);
17059  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
17060  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
17061  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
17062  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
17063 }
17064 
17065 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
17066 {
17067 #if VMA_MEMORY_BUDGET
17068  if(m_UseExtMemoryBudget)
17069  {
17070  if(m_Budget.m_OperationsSinceBudgetFetch < 30)
17071  {
17072  VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
17073  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17074  {
17075  const uint32_t heapIndex = firstHeap + i;
17076 
17077  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17078  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17079 
17080  if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
17081  {
17082  outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
17083  outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17084  }
17085  else
17086  {
17087  outBudget->usage = 0;
17088  }
17089 
17090  // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
17091  outBudget->budget = VMA_MIN(
17092  m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
17093  }
17094  }
17095  else
17096  {
17097  UpdateVulkanBudget(); // Outside of mutex lock
17098  GetBudget(outBudget, firstHeap, heapCount); // Recursion
17099  }
17100  }
17101  else
17102 #endif
17103  {
17104  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17105  {
17106  const uint32_t heapIndex = firstHeap + i;
17107 
17108  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17109  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17110 
17111  outBudget->usage = outBudget->blockBytes;
17112  outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17113  }
17114  }
17115 }
17116 
17117 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
17118 
17119 VkResult VmaAllocator_T::DefragmentationBegin(
17120  const VmaDefragmentationInfo2& info,
17121  VmaDefragmentationStats* pStats,
17122  VmaDefragmentationContext* pContext)
17123 {
17124  if(info.pAllocationsChanged != VMA_NULL)
17125  {
17126  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
17127  }
17128 
17129  *pContext = vma_new(this, VmaDefragmentationContext_T)(
17130  this, m_CurrentFrameIndex.load(), info.flags, pStats);
17131 
17132  (*pContext)->AddPools(info.poolCount, info.pPools);
17133  (*pContext)->AddAllocations(
17135 
17136  VkResult res = (*pContext)->Defragment(
17139  info.commandBuffer, pStats, info.flags);
17140 
17141  if(res != VK_NOT_READY)
17142  {
17143  vma_delete(this, *pContext);
17144  *pContext = VMA_NULL;
17145  }
17146 
17147  return res;
17148 }
17149 
17150 VkResult VmaAllocator_T::DefragmentationEnd(
17151  VmaDefragmentationContext context)
17152 {
17153  vma_delete(this, context);
17154  return VK_SUCCESS;
17155 }
17156 
17157 VkResult VmaAllocator_T::DefragmentationPassBegin(
17159  VmaDefragmentationContext context)
17160 {
17161  return context->DefragmentPassBegin(pInfo);
17162 }
17163 VkResult VmaAllocator_T::DefragmentationPassEnd(
17164  VmaDefragmentationContext context)
17165 {
17166  return context->DefragmentPassEnd();
17167 
17168 }
17169 
17170 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17171 {
17172  if(hAllocation->CanBecomeLost())
17173  {
17174  /*
17175  Warning: This is a carefully designed algorithm.
17176  Do not modify unless you really know what you're doing :)
17177  */
17178  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17179  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17180  for(;;)
17181  {
17182  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17183  {
17184  pAllocationInfo->memoryType = UINT32_MAX;
17185  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17186  pAllocationInfo->offset = 0;
17187  pAllocationInfo->size = hAllocation->GetSize();
17188  pAllocationInfo->pMappedData = VMA_NULL;
17189  pAllocationInfo->pUserData = hAllocation->GetUserData();
17190  return;
17191  }
17192  else if(localLastUseFrameIndex == localCurrFrameIndex)
17193  {
17194  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17195  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17196  pAllocationInfo->offset = hAllocation->GetOffset();
17197  pAllocationInfo->size = hAllocation->GetSize();
17198  pAllocationInfo->pMappedData = VMA_NULL;
17199  pAllocationInfo->pUserData = hAllocation->GetUserData();
17200  return;
17201  }
17202  else // Last use time earlier than current time.
17203  {
17204  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17205  {
17206  localLastUseFrameIndex = localCurrFrameIndex;
17207  }
17208  }
17209  }
17210  }
17211  else
17212  {
17213 #if VMA_STATS_STRING_ENABLED
17214  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17215  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17216  for(;;)
17217  {
17218  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17219  if(localLastUseFrameIndex == localCurrFrameIndex)
17220  {
17221  break;
17222  }
17223  else // Last use time earlier than current time.
17224  {
17225  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17226  {
17227  localLastUseFrameIndex = localCurrFrameIndex;
17228  }
17229  }
17230  }
17231 #endif
17232 
17233  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17234  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17235  pAllocationInfo->offset = hAllocation->GetOffset();
17236  pAllocationInfo->size = hAllocation->GetSize();
17237  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17238  pAllocationInfo->pUserData = hAllocation->GetUserData();
17239  }
17240 }
17241 
17242 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17243 {
17244  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17245  if(hAllocation->CanBecomeLost())
17246  {
17247  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17248  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17249  for(;;)
17250  {
17251  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17252  {
17253  return false;
17254  }
17255  else if(localLastUseFrameIndex == localCurrFrameIndex)
17256  {
17257  return true;
17258  }
17259  else // Last use time earlier than current time.
17260  {
17261  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17262  {
17263  localLastUseFrameIndex = localCurrFrameIndex;
17264  }
17265  }
17266  }
17267  }
17268  else
17269  {
17270 #if VMA_STATS_STRING_ENABLED
17271  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17272  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17273  for(;;)
17274  {
17275  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17276  if(localLastUseFrameIndex == localCurrFrameIndex)
17277  {
17278  break;
17279  }
17280  else // Last use time earlier than current time.
17281  {
17282  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17283  {
17284  localLastUseFrameIndex = localCurrFrameIndex;
17285  }
17286  }
17287  }
17288 #endif
17289 
17290  return true;
17291  }
17292 }
17293 
17294 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17295 {
17296  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17297 
17298  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17299 
17300  if(newCreateInfo.maxBlockCount == 0)
17301  {
17302  newCreateInfo.maxBlockCount = SIZE_MAX;
17303  }
17304  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17305  {
17306  return VK_ERROR_INITIALIZATION_FAILED;
17307  }
17308  // Memory type index out of range or forbidden.
17309  if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17310  ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17311  {
17312  return VK_ERROR_FEATURE_NOT_PRESENT;
17313  }
17314 
17315  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17316 
17317  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17318 
17319  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17320  if(res != VK_SUCCESS)
17321  {
17322  vma_delete(this, *pPool);
17323  *pPool = VMA_NULL;
17324  return res;
17325  }
17326 
17327  // Add to m_Pools.
17328  {
17329  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17330  (*pPool)->SetId(m_NextPoolId++);
17331  m_Pools.PushBack(*pPool);
17332  }
17333 
17334  return VK_SUCCESS;
17335 }
17336 
17337 void VmaAllocator_T::DestroyPool(VmaPool pool)
17338 {
17339  // Remove from m_Pools.
17340  {
17341  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17342  m_Pools.Remove(pool);
17343  }
17344 
17345  vma_delete(this, pool);
17346 }
17347 
17348 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17349 {
17350  pool->m_BlockVector.GetPoolStats(pPoolStats);
17351 }
17352 
17353 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17354 {
17355  m_CurrentFrameIndex.store(frameIndex);
17356 
17357 #if VMA_MEMORY_BUDGET
17358  if(m_UseExtMemoryBudget)
17359  {
17360  UpdateVulkanBudget();
17361  }
17362 #endif // #if VMA_MEMORY_BUDGET
17363 }
17364 
17365 void VmaAllocator_T::MakePoolAllocationsLost(
17366  VmaPool hPool,
17367  size_t* pLostAllocationCount)
17368 {
17369  hPool->m_BlockVector.MakePoolAllocationsLost(
17370  m_CurrentFrameIndex.load(),
17371  pLostAllocationCount);
17372 }
17373 
17374 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17375 {
17376  return hPool->m_BlockVector.CheckCorruption();
17377 }
17378 
17379 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17380 {
17381  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17382 
17383  // Process default pools.
17384  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17385  {
17386  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17387  {
17388  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17389  VMA_ASSERT(pBlockVector);
17390  VkResult localRes = pBlockVector->CheckCorruption();
17391  switch(localRes)
17392  {
17393  case VK_ERROR_FEATURE_NOT_PRESENT:
17394  break;
17395  case VK_SUCCESS:
17396  finalRes = VK_SUCCESS;
17397  break;
17398  default:
17399  return localRes;
17400  }
17401  }
17402  }
17403 
17404  // Process custom pools.
17405  {
17406  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17407  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17408  {
17409  if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17410  {
17411  VkResult localRes = pool->m_BlockVector.CheckCorruption();
17412  switch(localRes)
17413  {
17414  case VK_ERROR_FEATURE_NOT_PRESENT:
17415  break;
17416  case VK_SUCCESS:
17417  finalRes = VK_SUCCESS;
17418  break;
17419  default:
17420  return localRes;
17421  }
17422  }
17423  }
17424  }
17425 
17426  return finalRes;
17427 }
17428 
17429 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17430 {
17431  *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17432  (*pAllocation)->InitLost();
17433 }
17434 
17435 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
17436 template<typename T>
17437 struct AtomicTransactionalIncrement
17438 {
17439 public:
17440  typedef std::atomic<T> AtomicT;
17441  ~AtomicTransactionalIncrement()
17442  {
17443  if(m_Atomic)
17444  --(*m_Atomic);
17445  }
17446  T Increment(AtomicT* atomic)
17447  {
17448  m_Atomic = atomic;
17449  return m_Atomic->fetch_add(1);
17450  }
17451  void Commit()
17452  {
17453  m_Atomic = nullptr;
17454  }
17455 
17456 private:
17457  AtomicT* m_Atomic = nullptr;
17458 };
17459 
17460 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17461 {
17462  AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
17463  const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
17464 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
17465  if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
17466  {
17467  return VK_ERROR_TOO_MANY_OBJECTS;
17468  }
17469 #endif
17470 
17471  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17472 
17473  // HeapSizeLimit is in effect for this heap.
17474  if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17475  {
17476  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17477  VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17478  for(;;)
17479  {
17480  const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17481  if(blockBytesAfterAllocation > heapSize)
17482  {
17483  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17484  }
17485  if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17486  {
17487  break;
17488  }
17489  }
17490  }
17491  else
17492  {
17493  m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17494  }
17495 
17496  // VULKAN CALL vkAllocateMemory.
17497  VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17498 
17499  if(res == VK_SUCCESS)
17500  {
17501 #if VMA_MEMORY_BUDGET
17502  ++m_Budget.m_OperationsSinceBudgetFetch;
17503 #endif
17504 
17505  // Informative callback.
17506  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17507  {
17508  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17509  }
17510 
17511  deviceMemoryCountIncrement.Commit();
17512  }
17513  else
17514  {
17515  m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17516  }
17517 
17518  return res;
17519 }
17520 
17521 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17522 {
17523  // Informative callback.
17524  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17525  {
17526  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17527  }
17528 
17529  // VULKAN CALL vkFreeMemory.
17530  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17531 
17532  m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17533 
17534  --m_DeviceMemoryCount;
17535 }
17536 
17537 VkResult VmaAllocator_T::BindVulkanBuffer(
17538  VkDeviceMemory memory,
17539  VkDeviceSize memoryOffset,
17540  VkBuffer buffer,
17541  const void* pNext)
17542 {
17543  if(pNext != VMA_NULL)
17544  {
17545 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17546  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17547  m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17548  {
17549  VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17550  bindBufferMemoryInfo.pNext = pNext;
17551  bindBufferMemoryInfo.buffer = buffer;
17552  bindBufferMemoryInfo.memory = memory;
17553  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17554  return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17555  }
17556  else
17557 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17558  {
17559  return VK_ERROR_EXTENSION_NOT_PRESENT;
17560  }
17561  }
17562  else
17563  {
17564  return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17565  }
17566 }
17567 
17568 VkResult VmaAllocator_T::BindVulkanImage(
17569  VkDeviceMemory memory,
17570  VkDeviceSize memoryOffset,
17571  VkImage image,
17572  const void* pNext)
17573 {
17574  if(pNext != VMA_NULL)
17575  {
17576 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17577  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17578  m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17579  {
17580  VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17581  bindBufferMemoryInfo.pNext = pNext;
17582  bindBufferMemoryInfo.image = image;
17583  bindBufferMemoryInfo.memory = memory;
17584  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17585  return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17586  }
17587  else
17588 #endif // #if VMA_BIND_MEMORY2
17589  {
17590  return VK_ERROR_EXTENSION_NOT_PRESENT;
17591  }
17592  }
17593  else
17594  {
17595  return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17596  }
17597 }
17598 
17599 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17600 {
17601  if(hAllocation->CanBecomeLost())
17602  {
17603  return VK_ERROR_MEMORY_MAP_FAILED;
17604  }
17605 
17606  switch(hAllocation->GetType())
17607  {
17608  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17609  {
17610  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17611  char *pBytes = VMA_NULL;
17612  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17613  if(res == VK_SUCCESS)
17614  {
17615  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17616  hAllocation->BlockAllocMap();
17617  }
17618  return res;
17619  }
17620  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17621  return hAllocation->DedicatedAllocMap(this, ppData);
17622  default:
17623  VMA_ASSERT(0);
17624  return VK_ERROR_MEMORY_MAP_FAILED;
17625  }
17626 }
17627 
17628 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17629 {
17630  switch(hAllocation->GetType())
17631  {
17632  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17633  {
17634  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17635  hAllocation->BlockAllocUnmap();
17636  pBlock->Unmap(this, 1);
17637  }
17638  break;
17639  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17640  hAllocation->DedicatedAllocUnmap(this);
17641  break;
17642  default:
17643  VMA_ASSERT(0);
17644  }
17645 }
17646 
17647 VkResult VmaAllocator_T::BindBufferMemory(
17648  VmaAllocation hAllocation,
17649  VkDeviceSize allocationLocalOffset,
17650  VkBuffer hBuffer,
17651  const void* pNext)
17652 {
17653  VkResult res = VK_SUCCESS;
17654  switch(hAllocation->GetType())
17655  {
17656  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17657  res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17658  break;
17659  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17660  {
17661  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17662  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17663  res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17664  break;
17665  }
17666  default:
17667  VMA_ASSERT(0);
17668  }
17669  return res;
17670 }
17671 
17672 VkResult VmaAllocator_T::BindImageMemory(
17673  VmaAllocation hAllocation,
17674  VkDeviceSize allocationLocalOffset,
17675  VkImage hImage,
17676  const void* pNext)
17677 {
17678  VkResult res = VK_SUCCESS;
17679  switch(hAllocation->GetType())
17680  {
17681  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17682  res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17683  break;
17684  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17685  {
17686  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17687  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17688  res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17689  break;
17690  }
17691  default:
17692  VMA_ASSERT(0);
17693  }
17694  return res;
17695 }
17696 
17697 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17698  VmaAllocation hAllocation,
17699  VkDeviceSize offset, VkDeviceSize size,
17700  VMA_CACHE_OPERATION op)
17701 {
17702  VkResult res = VK_SUCCESS;
17703 
17704  VkMappedMemoryRange memRange = {};
17705  if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17706  {
17707  switch(op)
17708  {
17709  case VMA_CACHE_FLUSH:
17710  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17711  break;
17712  case VMA_CACHE_INVALIDATE:
17713  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17714  break;
17715  default:
17716  VMA_ASSERT(0);
17717  }
17718  }
17719  // else: Just ignore this call.
17720  return res;
17721 }
17722 
17723 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17724  uint32_t allocationCount,
17725  const VmaAllocation* allocations,
17726  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17727  VMA_CACHE_OPERATION op)
17728 {
17729  typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17730  typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17731  RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17732 
17733  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17734  {
17735  const VmaAllocation alloc = allocations[allocIndex];
17736  const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17737  const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17738  VkMappedMemoryRange newRange;
17739  if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17740  {
17741  ranges.push_back(newRange);
17742  }
17743  }
17744 
17745  VkResult res = VK_SUCCESS;
17746  if(!ranges.empty())
17747  {
17748  switch(op)
17749  {
17750  case VMA_CACHE_FLUSH:
17751  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17752  break;
17753  case VMA_CACHE_INVALIDATE:
17754  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17755  break;
17756  default:
17757  VMA_ASSERT(0);
17758  }
17759  }
17760  // else: Just ignore this call.
17761  return res;
17762 }
17763 
17764 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17765 {
17766  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17767 
17768  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17769  {
17770  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17771  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
17772  dedicatedAllocations.Remove(allocation);
17773  }
17774 
17775  VkDeviceMemory hMemory = allocation->GetMemory();
17776 
17777  /*
17778  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17779  before vkFreeMemory.
17780 
17781  if(allocation->GetMappedData() != VMA_NULL)
17782  {
17783  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17784  }
17785  */
17786 
17787  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17788 
17789  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17790 }
17791 
17792 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17793 {
17794  VkBufferCreateInfo dummyBufCreateInfo;
17795  VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17796 
17797  uint32_t memoryTypeBits = 0;
17798 
17799  // Create buffer.
17800  VkBuffer buf = VK_NULL_HANDLE;
17801  VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17802  m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17803  if(res == VK_SUCCESS)
17804  {
17805  // Query for supported memory types.
17806  VkMemoryRequirements memReq;
17807  (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17808  memoryTypeBits = memReq.memoryTypeBits;
17809 
17810  // Destroy buffer.
17811  (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17812  }
17813 
17814  return memoryTypeBits;
17815 }
17816 
17817 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17818 {
17819  // Make sure memory information is already fetched.
17820  VMA_ASSERT(GetMemoryTypeCount() > 0);
17821 
17822  uint32_t memoryTypeBits = UINT32_MAX;
17823 
17824  if(!m_UseAmdDeviceCoherentMemory)
17825  {
17826  // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17827  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17828  {
17829  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17830  {
17831  memoryTypeBits &= ~(1u << memTypeIndex);
17832  }
17833  }
17834  }
17835 
17836  return memoryTypeBits;
17837 }
17838 
17839 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17840  VmaAllocation allocation,
17841  VkDeviceSize offset, VkDeviceSize size,
17842  VkMappedMemoryRange& outRange) const
17843 {
17844  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17845  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17846  {
17847  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17848  const VkDeviceSize allocationSize = allocation->GetSize();
17849  VMA_ASSERT(offset <= allocationSize);
17850 
17851  outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17852  outRange.pNext = VMA_NULL;
17853  outRange.memory = allocation->GetMemory();
17854 
17855  switch(allocation->GetType())
17856  {
17857  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17858  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17859  if(size == VK_WHOLE_SIZE)
17860  {
17861  outRange.size = allocationSize - outRange.offset;
17862  }
17863  else
17864  {
17865  VMA_ASSERT(offset + size <= allocationSize);
17866  outRange.size = VMA_MIN(
17867  VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17868  allocationSize - outRange.offset);
17869  }
17870  break;
17871  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17872  {
17873  // 1. Still within this allocation.
17874  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17875  if(size == VK_WHOLE_SIZE)
17876  {
17877  size = allocationSize - offset;
17878  }
17879  else
17880  {
17881  VMA_ASSERT(offset + size <= allocationSize);
17882  }
17883  outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17884 
17885  // 2. Adjust to whole block.
17886  const VkDeviceSize allocationOffset = allocation->GetOffset();
17887  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17888  const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17889  outRange.offset += allocationOffset;
17890  outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17891 
17892  break;
17893  }
17894  default:
17895  VMA_ASSERT(0);
17896  }
17897  return true;
17898  }
17899  return false;
17900 }
17901 
17902 #if VMA_MEMORY_BUDGET
17903 
17904 void VmaAllocator_T::UpdateVulkanBudget()
17905 {
17906  VMA_ASSERT(m_UseExtMemoryBudget);
17907 
17908  VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17909 
17910  VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17911  VmaPnextChainPushFront(&memProps, &budgetProps);
17912 
17913  GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17914 
17915  {
17916  VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17917 
17918  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17919  {
17920  m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17921  m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17922  m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17923 
17924  // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17925  if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17926  {
17927  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17928  }
17929  else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17930  {
17931  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17932  }
17933  if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17934  {
17935  m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17936  }
17937  }
17938  m_Budget.m_OperationsSinceBudgetFetch = 0;
17939  }
17940 }
17941 
17942 #endif // #if VMA_MEMORY_BUDGET
17943 
17944 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17945 {
17946  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17947  !hAllocation->CanBecomeLost() &&
17948  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17949  {
17950  void* pData = VMA_NULL;
17951  VkResult res = Map(hAllocation, &pData);
17952  if(res == VK_SUCCESS)
17953  {
17954  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17955  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17956  Unmap(hAllocation);
17957  }
17958  else
17959  {
17960  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17961  }
17962  }
17963 }
17964 
17965 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17966 {
17967  uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17968  if(memoryTypeBits == UINT32_MAX)
17969  {
17970  memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17971  m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17972  }
17973  return memoryTypeBits;
17974 }
17975 
17976 #if VMA_STATS_STRING_ENABLED
17977 
17978 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17979 {
17980  bool dedicatedAllocationsStarted = false;
17981  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17982  {
17983  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17984  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17985  if(!dedicatedAllocList.IsEmpty())
17986  {
17987  if(dedicatedAllocationsStarted == false)
17988  {
17989  dedicatedAllocationsStarted = true;
17990  json.WriteString("DedicatedAllocations");
17991  json.BeginObject();
17992  }
17993 
17994  json.BeginString("Type ");
17995  json.ContinueString(memTypeIndex);
17996  json.EndString();
17997 
17998  json.BeginArray();
17999 
18000  for(VmaAllocation alloc = dedicatedAllocList.Front();
18001  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
18002  {
18003  json.BeginObject(true);
18004  alloc->PrintParameters(json);
18005  json.EndObject();
18006  }
18007 
18008  json.EndArray();
18009  }
18010  }
18011  if(dedicatedAllocationsStarted)
18012  {
18013  json.EndObject();
18014  }
18015 
18016  {
18017  bool allocationsStarted = false;
18018  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
18019  {
18020  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
18021  {
18022  if(allocationsStarted == false)
18023  {
18024  allocationsStarted = true;
18025  json.WriteString("DefaultPools");
18026  json.BeginObject();
18027  }
18028 
18029  json.BeginString("Type ");
18030  json.ContinueString(memTypeIndex);
18031  json.EndString();
18032 
18033  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
18034  }
18035  }
18036  if(allocationsStarted)
18037  {
18038  json.EndObject();
18039  }
18040  }
18041 
18042  // Custom pools
18043  {
18044  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
18045  if(!m_Pools.IsEmpty())
18046  {
18047  json.WriteString("Pools");
18048  json.BeginObject();
18049  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
18050  {
18051  json.BeginString();
18052  json.ContinueString(pool->GetId());
18053  json.EndString();
18054 
18055  pool->m_BlockVector.PrintDetailedMap(json);
18056  }
18057  json.EndObject();
18058  }
18059  }
18060 }
18061 
18062 #endif // #if VMA_STATS_STRING_ENABLED
18063 
18065 // Public interface
18066 
18067 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
18068  const VmaAllocatorCreateInfo* pCreateInfo,
18069  VmaAllocator* pAllocator)
18070 {
18071  VMA_ASSERT(pCreateInfo && pAllocator);
18072  VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
18073  (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
18074  VMA_DEBUG_LOG("vmaCreateAllocator");
18075  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
18076  return (*pAllocator)->Init(pCreateInfo);
18077 }
18078 
18079 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
18080  VmaAllocator allocator)
18081 {
18082  if(allocator != VK_NULL_HANDLE)
18083  {
18084  VMA_DEBUG_LOG("vmaDestroyAllocator");
18085  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
18086  vma_delete(&allocationCallbacks, allocator);
18087  }
18088 }
18089 
18090 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
18091 {
18092  VMA_ASSERT(allocator && pAllocatorInfo);
18093  pAllocatorInfo->instance = allocator->m_hInstance;
18094  pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
18095  pAllocatorInfo->device = allocator->m_hDevice;
18096 }
18097 
18098 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
18099  VmaAllocator allocator,
18100  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
18101 {
18102  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
18103  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
18104 }
18105 
18106 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
18107  VmaAllocator allocator,
18108  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
18109 {
18110  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
18111  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
18112 }
18113 
18114 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
18115  VmaAllocator allocator,
18116  uint32_t memoryTypeIndex,
18117  VkMemoryPropertyFlags* pFlags)
18118 {
18119  VMA_ASSERT(allocator && pFlags);
18120  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
18121  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
18122 }
18123 
18124 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
18125  VmaAllocator allocator,
18126  uint32_t frameIndex)
18127 {
18128  VMA_ASSERT(allocator);
18129  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
18130 
18131  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18132 
18133  allocator->SetCurrentFrameIndex(frameIndex);
18134 }
18135 
18136 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
18137  VmaAllocator allocator,
18138  VmaStats* pStats)
18139 {
18140  VMA_ASSERT(allocator && pStats);
18141  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18142  allocator->CalculateStats(pStats);
18143 }
18144 
18145 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
18146  VmaAllocator allocator,
18147  VmaBudget* pBudget)
18148 {
18149  VMA_ASSERT(allocator && pBudget);
18150  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18151  allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
18152 }
18153 
18154 #if VMA_STATS_STRING_ENABLED
18155 
18156 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
18157  VmaAllocator allocator,
18158  char** ppStatsString,
18159  VkBool32 detailedMap)
18160 {
18161  VMA_ASSERT(allocator && ppStatsString);
18162  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18163 
18164  VmaStringBuilder sb(allocator);
18165  {
18166  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
18167  json.BeginObject();
18168 
18169  VmaBudget budget[VK_MAX_MEMORY_HEAPS];
18170  allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
18171 
18172  VmaStats stats;
18173  allocator->CalculateStats(&stats);
18174 
18175  json.WriteString("Total");
18176  VmaPrintStatInfo(json, stats.total);
18177 
18178  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18179  {
18180  json.BeginString("Heap ");
18181  json.ContinueString(heapIndex);
18182  json.EndString();
18183  json.BeginObject();
18184 
18185  json.WriteString("Size");
18186  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18187 
18188  json.WriteString("Flags");
18189  json.BeginArray(true);
18190  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18191  {
18192  json.WriteString("DEVICE_LOCAL");
18193  }
18194  json.EndArray();
18195 
18196  json.WriteString("Budget");
18197  json.BeginObject();
18198  {
18199  json.WriteString("BlockBytes");
18200  json.WriteNumber(budget[heapIndex].blockBytes);
18201  json.WriteString("AllocationBytes");
18202  json.WriteNumber(budget[heapIndex].allocationBytes);
18203  json.WriteString("Usage");
18204  json.WriteNumber(budget[heapIndex].usage);
18205  json.WriteString("Budget");
18206  json.WriteNumber(budget[heapIndex].budget);
18207  }
18208  json.EndObject();
18209 
18210  if(stats.memoryHeap[heapIndex].blockCount > 0)
18211  {
18212  json.WriteString("Stats");
18213  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18214  }
18215 
18216  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18217  {
18218  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18219  {
18220  json.BeginString("Type ");
18221  json.ContinueString(typeIndex);
18222  json.EndString();
18223 
18224  json.BeginObject();
18225 
18226  json.WriteString("Flags");
18227  json.BeginArray(true);
18228  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18229  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18230  {
18231  json.WriteString("DEVICE_LOCAL");
18232  }
18233  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18234  {
18235  json.WriteString("HOST_VISIBLE");
18236  }
18237  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18238  {
18239  json.WriteString("HOST_COHERENT");
18240  }
18241  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18242  {
18243  json.WriteString("HOST_CACHED");
18244  }
18245  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18246  {
18247  json.WriteString("LAZILY_ALLOCATED");
18248  }
18249 #if VMA_VULKAN_VERSION >= 1001000
18250  if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18251  {
18252  json.WriteString("PROTECTED");
18253  }
18254 #endif // #if VMA_VULKAN_VERSION >= 1001000
18255 #if VK_AMD_device_coherent_memory
18256  if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18257  {
18258  json.WriteString("DEVICE_COHERENT");
18259  }
18260  if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18261  {
18262  json.WriteString("DEVICE_UNCACHED");
18263  }
18264 #endif // #if VK_AMD_device_coherent_memory
18265  json.EndArray();
18266 
18267  if(stats.memoryType[typeIndex].blockCount > 0)
18268  {
18269  json.WriteString("Stats");
18270  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18271  }
18272 
18273  json.EndObject();
18274  }
18275  }
18276 
18277  json.EndObject();
18278  }
18279  if(detailedMap == VK_TRUE)
18280  {
18281  allocator->PrintDetailedMap(json);
18282  }
18283 
18284  json.EndObject();
18285  }
18286 
18287  const size_t len = sb.GetLength();
18288  char* const pChars = vma_new_array(allocator, char, len + 1);
18289  if(len > 0)
18290  {
18291  memcpy(pChars, sb.GetData(), len);
18292  }
18293  pChars[len] = '\0';
18294  *ppStatsString = pChars;
18295 }
18296 
18297 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18298  VmaAllocator allocator,
18299  char* pStatsString)
18300 {
18301  if(pStatsString != VMA_NULL)
18302  {
18303  VMA_ASSERT(allocator);
18304  size_t len = strlen(pStatsString);
18305  vma_delete_array(allocator, pStatsString, len + 1);
18306  }
18307 }
18308 
18309 #endif // #if VMA_STATS_STRING_ENABLED
18310 
18311 /*
18312 This function is not protected by any mutex because it just reads immutable data.
18313 */
18314 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18315  VmaAllocator allocator,
18316  uint32_t memoryTypeBits,
18317  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18318  uint32_t* pMemoryTypeIndex)
18319 {
18320  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18321  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18322  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18323 
18324  memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18325 
18326  if(pAllocationCreateInfo->memoryTypeBits != 0)
18327  {
18328  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18329  }
18330 
18331  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18332  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18333  uint32_t notPreferredFlags = 0;
18334 
18335  // Convert usage to requiredFlags and preferredFlags.
18336  switch(pAllocationCreateInfo->usage)
18337  {
18339  break;
18341  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18342  {
18343  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18344  }
18345  break;
18347  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18348  break;
18350  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18351  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18352  {
18353  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18354  }
18355  break;
18357  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18358  preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18359  break;
18361  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18362  break;
18364  requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18365  break;
18366  default:
18367  VMA_ASSERT(0);
18368  break;
18369  }
18370 
18371  // Avoid DEVICE_COHERENT unless explicitly requested.
18372  if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18373  (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18374  {
18375  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18376  }
18377 
18378  *pMemoryTypeIndex = UINT32_MAX;
18379  uint32_t minCost = UINT32_MAX;
18380  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18381  memTypeIndex < allocator->GetMemoryTypeCount();
18382  ++memTypeIndex, memTypeBit <<= 1)
18383  {
18384  // This memory type is acceptable according to memoryTypeBits bitmask.
18385  if((memTypeBit & memoryTypeBits) != 0)
18386  {
18387  const VkMemoryPropertyFlags currFlags =
18388  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18389  // This memory type contains requiredFlags.
18390  if((requiredFlags & ~currFlags) == 0)
18391  {
18392  // Calculate cost as number of bits from preferredFlags not present in this memory type.
18393  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18394  VmaCountBitsSet(currFlags & notPreferredFlags);
18395  // Remember memory type with lowest cost.
18396  if(currCost < minCost)
18397  {
18398  *pMemoryTypeIndex = memTypeIndex;
18399  if(currCost == 0)
18400  {
18401  return VK_SUCCESS;
18402  }
18403  minCost = currCost;
18404  }
18405  }
18406  }
18407  }
18408  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18409 }
18410 
18411 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18412  VmaAllocator allocator,
18413  const VkBufferCreateInfo* pBufferCreateInfo,
18414  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18415  uint32_t* pMemoryTypeIndex)
18416 {
18417  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18418  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18419  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18420  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18421 
18422  const VkDevice hDev = allocator->m_hDevice;
18423  VkBuffer hBuffer = VK_NULL_HANDLE;
18424  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18425  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18426  if(res == VK_SUCCESS)
18427  {
18428  VkMemoryRequirements memReq = {};
18429  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18430  hDev, hBuffer, &memReq);
18431 
18432  res = vmaFindMemoryTypeIndex(
18433  allocator,
18434  memReq.memoryTypeBits,
18435  pAllocationCreateInfo,
18436  pMemoryTypeIndex);
18437 
18438  allocator->GetVulkanFunctions().vkDestroyBuffer(
18439  hDev, hBuffer, allocator->GetAllocationCallbacks());
18440  }
18441  return res;
18442 }
18443 
18444 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18445  VmaAllocator allocator,
18446  const VkImageCreateInfo* pImageCreateInfo,
18447  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18448  uint32_t* pMemoryTypeIndex)
18449 {
18450  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18451  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18452  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18453  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18454 
18455  const VkDevice hDev = allocator->m_hDevice;
18456  VkImage hImage = VK_NULL_HANDLE;
18457  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18458  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18459  if(res == VK_SUCCESS)
18460  {
18461  VkMemoryRequirements memReq = {};
18462  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18463  hDev, hImage, &memReq);
18464 
18465  res = vmaFindMemoryTypeIndex(
18466  allocator,
18467  memReq.memoryTypeBits,
18468  pAllocationCreateInfo,
18469  pMemoryTypeIndex);
18470 
18471  allocator->GetVulkanFunctions().vkDestroyImage(
18472  hDev, hImage, allocator->GetAllocationCallbacks());
18473  }
18474  return res;
18475 }
18476 
18477 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18478  VmaAllocator allocator,
18479  const VmaPoolCreateInfo* pCreateInfo,
18480  VmaPool* pPool)
18481 {
18482  VMA_ASSERT(allocator && pCreateInfo && pPool);
18483 
18484  VMA_DEBUG_LOG("vmaCreatePool");
18485 
18486  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18487 
18488  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18489 
18490 #if VMA_RECORDING_ENABLED
18491  if(allocator->GetRecorder() != VMA_NULL)
18492  {
18493  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18494  }
18495 #endif
18496 
18497  return res;
18498 }
18499 
18500 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18501  VmaAllocator allocator,
18502  VmaPool pool)
18503 {
18504  VMA_ASSERT(allocator);
18505 
18506  if(pool == VK_NULL_HANDLE)
18507  {
18508  return;
18509  }
18510 
18511  VMA_DEBUG_LOG("vmaDestroyPool");
18512 
18513  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18514 
18515 #if VMA_RECORDING_ENABLED
18516  if(allocator->GetRecorder() != VMA_NULL)
18517  {
18518  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18519  }
18520 #endif
18521 
18522  allocator->DestroyPool(pool);
18523 }
18524 
18525 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18526  VmaAllocator allocator,
18527  VmaPool pool,
18528  VmaPoolStats* pPoolStats)
18529 {
18530  VMA_ASSERT(allocator && pool && pPoolStats);
18531 
18532  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18533 
18534  allocator->GetPoolStats(pool, pPoolStats);
18535 }
18536 
18537 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18538  VmaAllocator allocator,
18539  VmaPool pool,
18540  size_t* pLostAllocationCount)
18541 {
18542  VMA_ASSERT(allocator && pool);
18543 
18544  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18545 
18546 #if VMA_RECORDING_ENABLED
18547  if(allocator->GetRecorder() != VMA_NULL)
18548  {
18549  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18550  }
18551 #endif
18552 
18553  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18554 }
18555 
18556 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18557 {
18558  VMA_ASSERT(allocator && pool);
18559 
18560  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18561 
18562  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18563 
18564  return allocator->CheckPoolCorruption(pool);
18565 }
18566 
18567 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18568  VmaAllocator allocator,
18569  VmaPool pool,
18570  const char** ppName)
18571 {
18572  VMA_ASSERT(allocator && pool && ppName);
18573 
18574  VMA_DEBUG_LOG("vmaGetPoolName");
18575 
18576  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18577 
18578  *ppName = pool->GetName();
18579 }
18580 
18581 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18582  VmaAllocator allocator,
18583  VmaPool pool,
18584  const char* pName)
18585 {
18586  VMA_ASSERT(allocator && pool);
18587 
18588  VMA_DEBUG_LOG("vmaSetPoolName");
18589 
18590  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18591 
18592  pool->SetName(pName);
18593 
18594 #if VMA_RECORDING_ENABLED
18595  if(allocator->GetRecorder() != VMA_NULL)
18596  {
18597  allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18598  }
18599 #endif
18600 }
18601 
18602 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18603  VmaAllocator allocator,
18604  const VkMemoryRequirements* pVkMemoryRequirements,
18605  const VmaAllocationCreateInfo* pCreateInfo,
18606  VmaAllocation* pAllocation,
18607  VmaAllocationInfo* pAllocationInfo)
18608 {
18609  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18610 
18611  VMA_DEBUG_LOG("vmaAllocateMemory");
18612 
18613  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18614 
18615  VkResult result = allocator->AllocateMemory(
18616  *pVkMemoryRequirements,
18617  false, // requiresDedicatedAllocation
18618  false, // prefersDedicatedAllocation
18619  VK_NULL_HANDLE, // dedicatedBuffer
18620  UINT32_MAX, // dedicatedBufferUsage
18621  VK_NULL_HANDLE, // dedicatedImage
18622  *pCreateInfo,
18623  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18624  1, // allocationCount
18625  pAllocation);
18626 
18627 #if VMA_RECORDING_ENABLED
18628  if(allocator->GetRecorder() != VMA_NULL)
18629  {
18630  allocator->GetRecorder()->RecordAllocateMemory(
18631  allocator->GetCurrentFrameIndex(),
18632  *pVkMemoryRequirements,
18633  *pCreateInfo,
18634  *pAllocation);
18635  }
18636 #endif
18637 
18638  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18639  {
18640  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18641  }
18642 
18643  return result;
18644 }
18645 
18646 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18647  VmaAllocator allocator,
18648  const VkMemoryRequirements* pVkMemoryRequirements,
18649  const VmaAllocationCreateInfo* pCreateInfo,
18650  size_t allocationCount,
18651  VmaAllocation* pAllocations,
18652  VmaAllocationInfo* pAllocationInfo)
18653 {
18654  if(allocationCount == 0)
18655  {
18656  return VK_SUCCESS;
18657  }
18658 
18659  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18660 
18661  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18662 
18663  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18664 
18665  VkResult result = allocator->AllocateMemory(
18666  *pVkMemoryRequirements,
18667  false, // requiresDedicatedAllocation
18668  false, // prefersDedicatedAllocation
18669  VK_NULL_HANDLE, // dedicatedBuffer
18670  UINT32_MAX, // dedicatedBufferUsage
18671  VK_NULL_HANDLE, // dedicatedImage
18672  *pCreateInfo,
18673  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18674  allocationCount,
18675  pAllocations);
18676 
18677 #if VMA_RECORDING_ENABLED
18678  if(allocator->GetRecorder() != VMA_NULL)
18679  {
18680  allocator->GetRecorder()->RecordAllocateMemoryPages(
18681  allocator->GetCurrentFrameIndex(),
18682  *pVkMemoryRequirements,
18683  *pCreateInfo,
18684  (uint64_t)allocationCount,
18685  pAllocations);
18686  }
18687 #endif
18688 
18689  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18690  {
18691  for(size_t i = 0; i < allocationCount; ++i)
18692  {
18693  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18694  }
18695  }
18696 
18697  return result;
18698 }
18699 
18700 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18701  VmaAllocator allocator,
18702  VkBuffer buffer,
18703  const VmaAllocationCreateInfo* pCreateInfo,
18704  VmaAllocation* pAllocation,
18705  VmaAllocationInfo* pAllocationInfo)
18706 {
18707  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18708 
18709  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18710 
18711  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18712 
18713  VkMemoryRequirements vkMemReq = {};
18714  bool requiresDedicatedAllocation = false;
18715  bool prefersDedicatedAllocation = false;
18716  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18717  requiresDedicatedAllocation,
18718  prefersDedicatedAllocation);
18719 
18720  VkResult result = allocator->AllocateMemory(
18721  vkMemReq,
18722  requiresDedicatedAllocation,
18723  prefersDedicatedAllocation,
18724  buffer, // dedicatedBuffer
18725  UINT32_MAX, // dedicatedBufferUsage
18726  VK_NULL_HANDLE, // dedicatedImage
18727  *pCreateInfo,
18728  VMA_SUBALLOCATION_TYPE_BUFFER,
18729  1, // allocationCount
18730  pAllocation);
18731 
18732 #if VMA_RECORDING_ENABLED
18733  if(allocator->GetRecorder() != VMA_NULL)
18734  {
18735  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18736  allocator->GetCurrentFrameIndex(),
18737  vkMemReq,
18738  requiresDedicatedAllocation,
18739  prefersDedicatedAllocation,
18740  *pCreateInfo,
18741  *pAllocation);
18742  }
18743 #endif
18744 
18745  if(pAllocationInfo && result == VK_SUCCESS)
18746  {
18747  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18748  }
18749 
18750  return result;
18751 }
18752 
18753 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18754  VmaAllocator allocator,
18755  VkImage image,
18756  const VmaAllocationCreateInfo* pCreateInfo,
18757  VmaAllocation* pAllocation,
18758  VmaAllocationInfo* pAllocationInfo)
18759 {
18760  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18761 
18762  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18763 
18764  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18765 
18766  VkMemoryRequirements vkMemReq = {};
18767  bool requiresDedicatedAllocation = false;
18768  bool prefersDedicatedAllocation = false;
18769  allocator->GetImageMemoryRequirements(image, vkMemReq,
18770  requiresDedicatedAllocation, prefersDedicatedAllocation);
18771 
18772  VkResult result = allocator->AllocateMemory(
18773  vkMemReq,
18774  requiresDedicatedAllocation,
18775  prefersDedicatedAllocation,
18776  VK_NULL_HANDLE, // dedicatedBuffer
18777  UINT32_MAX, // dedicatedBufferUsage
18778  image, // dedicatedImage
18779  *pCreateInfo,
18780  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18781  1, // allocationCount
18782  pAllocation);
18783 
18784 #if VMA_RECORDING_ENABLED
18785  if(allocator->GetRecorder() != VMA_NULL)
18786  {
18787  allocator->GetRecorder()->RecordAllocateMemoryForImage(
18788  allocator->GetCurrentFrameIndex(),
18789  vkMemReq,
18790  requiresDedicatedAllocation,
18791  prefersDedicatedAllocation,
18792  *pCreateInfo,
18793  *pAllocation);
18794  }
18795 #endif
18796 
18797  if(pAllocationInfo && result == VK_SUCCESS)
18798  {
18799  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18800  }
18801 
18802  return result;
18803 }
18804 
18805 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18806  VmaAllocator allocator,
18807  VmaAllocation allocation)
18808 {
18809  VMA_ASSERT(allocator);
18810 
18811  if(allocation == VK_NULL_HANDLE)
18812  {
18813  return;
18814  }
18815 
18816  VMA_DEBUG_LOG("vmaFreeMemory");
18817 
18818  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18819 
18820 #if VMA_RECORDING_ENABLED
18821  if(allocator->GetRecorder() != VMA_NULL)
18822  {
18823  allocator->GetRecorder()->RecordFreeMemory(
18824  allocator->GetCurrentFrameIndex(),
18825  allocation);
18826  }
18827 #endif
18828 
18829  allocator->FreeMemory(
18830  1, // allocationCount
18831  &allocation);
18832 }
18833 
18834 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18835  VmaAllocator allocator,
18836  size_t allocationCount,
18837  const VmaAllocation* pAllocations)
18838 {
18839  if(allocationCount == 0)
18840  {
18841  return;
18842  }
18843 
18844  VMA_ASSERT(allocator);
18845 
18846  VMA_DEBUG_LOG("vmaFreeMemoryPages");
18847 
18848  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18849 
18850 #if VMA_RECORDING_ENABLED
18851  if(allocator->GetRecorder() != VMA_NULL)
18852  {
18853  allocator->GetRecorder()->RecordFreeMemoryPages(
18854  allocator->GetCurrentFrameIndex(),
18855  (uint64_t)allocationCount,
18856  pAllocations);
18857  }
18858 #endif
18859 
18860  allocator->FreeMemory(allocationCount, pAllocations);
18861 }
18862 
18863 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18864  VmaAllocator allocator,
18865  VmaAllocation allocation,
18866  VmaAllocationInfo* pAllocationInfo)
18867 {
18868  VMA_ASSERT(allocator && allocation && pAllocationInfo);
18869 
18870  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18871 
18872 #if VMA_RECORDING_ENABLED
18873  if(allocator->GetRecorder() != VMA_NULL)
18874  {
18875  allocator->GetRecorder()->RecordGetAllocationInfo(
18876  allocator->GetCurrentFrameIndex(),
18877  allocation);
18878  }
18879 #endif
18880 
18881  allocator->GetAllocationInfo(allocation, pAllocationInfo);
18882 }
18883 
18884 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18885  VmaAllocator allocator,
18886  VmaAllocation allocation)
18887 {
18888  VMA_ASSERT(allocator && allocation);
18889 
18890  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18891 
18892 #if VMA_RECORDING_ENABLED
18893  if(allocator->GetRecorder() != VMA_NULL)
18894  {
18895  allocator->GetRecorder()->RecordTouchAllocation(
18896  allocator->GetCurrentFrameIndex(),
18897  allocation);
18898  }
18899 #endif
18900 
18901  return allocator->TouchAllocation(allocation);
18902 }
18903 
18904 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18905  VmaAllocator allocator,
18906  VmaAllocation allocation,
18907  void* pUserData)
18908 {
18909  VMA_ASSERT(allocator && allocation);
18910 
18911  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18912 
18913  allocation->SetUserData(allocator, pUserData);
18914 
18915 #if VMA_RECORDING_ENABLED
18916  if(allocator->GetRecorder() != VMA_NULL)
18917  {
18918  allocator->GetRecorder()->RecordSetAllocationUserData(
18919  allocator->GetCurrentFrameIndex(),
18920  allocation,
18921  pUserData);
18922  }
18923 #endif
18924 }
18925 
18926 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18927  VmaAllocator allocator,
18928  VmaAllocation* pAllocation)
18929 {
18930  VMA_ASSERT(allocator && pAllocation);
18931 
18932  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18933 
18934  allocator->CreateLostAllocation(pAllocation);
18935 
18936 #if VMA_RECORDING_ENABLED
18937  if(allocator->GetRecorder() != VMA_NULL)
18938  {
18939  allocator->GetRecorder()->RecordCreateLostAllocation(
18940  allocator->GetCurrentFrameIndex(),
18941  *pAllocation);
18942  }
18943 #endif
18944 }
18945 
18946 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18947  VmaAllocator allocator,
18948  VmaAllocation allocation,
18949  void** ppData)
18950 {
18951  VMA_ASSERT(allocator && allocation && ppData);
18952 
18953  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18954 
18955  VkResult res = allocator->Map(allocation, ppData);
18956 
18957 #if VMA_RECORDING_ENABLED
18958  if(allocator->GetRecorder() != VMA_NULL)
18959  {
18960  allocator->GetRecorder()->RecordMapMemory(
18961  allocator->GetCurrentFrameIndex(),
18962  allocation);
18963  }
18964 #endif
18965 
18966  return res;
18967 }
18968 
18969 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18970  VmaAllocator allocator,
18971  VmaAllocation allocation)
18972 {
18973  VMA_ASSERT(allocator && allocation);
18974 
18975  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18976 
18977 #if VMA_RECORDING_ENABLED
18978  if(allocator->GetRecorder() != VMA_NULL)
18979  {
18980  allocator->GetRecorder()->RecordUnmapMemory(
18981  allocator->GetCurrentFrameIndex(),
18982  allocation);
18983  }
18984 #endif
18985 
18986  allocator->Unmap(allocation);
18987 }
18988 
18989 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18990 {
18991  VMA_ASSERT(allocator && allocation);
18992 
18993  VMA_DEBUG_LOG("vmaFlushAllocation");
18994 
18995  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18996 
18997  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18998 
18999 #if VMA_RECORDING_ENABLED
19000  if(allocator->GetRecorder() != VMA_NULL)
19001  {
19002  allocator->GetRecorder()->RecordFlushAllocation(
19003  allocator->GetCurrentFrameIndex(),
19004  allocation, offset, size);
19005  }
19006 #endif
19007 
19008  return res;
19009 }
19010 
19011 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
19012 {
19013  VMA_ASSERT(allocator && allocation);
19014 
19015  VMA_DEBUG_LOG("vmaInvalidateAllocation");
19016 
19017  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19018 
19019  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
19020 
19021 #if VMA_RECORDING_ENABLED
19022  if(allocator->GetRecorder() != VMA_NULL)
19023  {
19024  allocator->GetRecorder()->RecordInvalidateAllocation(
19025  allocator->GetCurrentFrameIndex(),
19026  allocation, offset, size);
19027  }
19028 #endif
19029 
19030  return res;
19031 }
19032 
19033 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
19034  VmaAllocator allocator,
19035  uint32_t allocationCount,
19036  const VmaAllocation* allocations,
19037  const VkDeviceSize* offsets,
19038  const VkDeviceSize* sizes)
19039 {
19040  VMA_ASSERT(allocator);
19041 
19042  if(allocationCount == 0)
19043  {
19044  return VK_SUCCESS;
19045  }
19046 
19047  VMA_ASSERT(allocations);
19048 
19049  VMA_DEBUG_LOG("vmaFlushAllocations");
19050 
19051  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19052 
19053  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
19054 
19055 #if VMA_RECORDING_ENABLED
19056  if(allocator->GetRecorder() != VMA_NULL)
19057  {
19058  //TODO
19059  }
19060 #endif
19061 
19062  return res;
19063 }
19064 
19065 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19066  VmaAllocator allocator,
19067  uint32_t allocationCount,
19068  const VmaAllocation* allocations,
19069  const VkDeviceSize* offsets,
19070  const VkDeviceSize* sizes)
19071 {
19072  VMA_ASSERT(allocator);
19073 
19074  if(allocationCount == 0)
19075  {
19076  return VK_SUCCESS;
19077  }
19078 
19079  VMA_ASSERT(allocations);
19080 
19081  VMA_DEBUG_LOG("vmaInvalidateAllocations");
19082 
19083  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19084 
19085  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19086 
19087 #if VMA_RECORDING_ENABLED
19088  if(allocator->GetRecorder() != VMA_NULL)
19089  {
19090  //TODO
19091  }
19092 #endif
19093 
19094  return res;
19095 }
19096 
19097 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19098 {
19099  VMA_ASSERT(allocator);
19100 
19101  VMA_DEBUG_LOG("vmaCheckCorruption");
19102 
19103  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19104 
19105  return allocator->CheckCorruption(memoryTypeBits);
19106 }
19107 
19108 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19109  VmaAllocator allocator,
19110  const VmaAllocation* pAllocations,
19111  size_t allocationCount,
19112  VkBool32* pAllocationsChanged,
19113  const VmaDefragmentationInfo *pDefragmentationInfo,
19114  VmaDefragmentationStats* pDefragmentationStats)
19115 {
19116  // Deprecated interface, reimplemented using new one.
19117 
19118  VmaDefragmentationInfo2 info2 = {};
19119  info2.allocationCount = (uint32_t)allocationCount;
19120  info2.pAllocations = pAllocations;
19121  info2.pAllocationsChanged = pAllocationsChanged;
19122  if(pDefragmentationInfo != VMA_NULL)
19123  {
19124  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19125  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19126  }
19127  else
19128  {
19129  info2.maxCpuAllocationsToMove = UINT32_MAX;
19130  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19131  }
19132  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19133 
19135  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19136  if(res == VK_NOT_READY)
19137  {
19138  res = vmaDefragmentationEnd( allocator, ctx);
19139  }
19140  return res;
19141 }
19142 
19143 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19144  VmaAllocator allocator,
19145  const VmaDefragmentationInfo2* pInfo,
19146  VmaDefragmentationStats* pStats,
19147  VmaDefragmentationContext *pContext)
19148 {
19149  VMA_ASSERT(allocator && pInfo && pContext);
19150 
19151  // Degenerate case: Nothing to defragment.
19152  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19153  {
19154  return VK_SUCCESS;
19155  }
19156 
19157  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19158  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19159  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19160  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19161 
19162  VMA_DEBUG_LOG("vmaDefragmentationBegin");
19163 
19164  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19165 
19166  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19167 
19168 #if VMA_RECORDING_ENABLED
19169  if(allocator->GetRecorder() != VMA_NULL)
19170  {
19171  allocator->GetRecorder()->RecordDefragmentationBegin(
19172  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19173  }
19174 #endif
19175 
19176  return res;
19177 }
19178 
19179 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19180  VmaAllocator allocator,
19181  VmaDefragmentationContext context)
19182 {
19183  VMA_ASSERT(allocator);
19184 
19185  VMA_DEBUG_LOG("vmaDefragmentationEnd");
19186 
19187  if(context != VK_NULL_HANDLE)
19188  {
19189  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19190 
19191 #if VMA_RECORDING_ENABLED
19192  if(allocator->GetRecorder() != VMA_NULL)
19193  {
19194  allocator->GetRecorder()->RecordDefragmentationEnd(
19195  allocator->GetCurrentFrameIndex(), context);
19196  }
19197 #endif
19198 
19199  return allocator->DefragmentationEnd(context);
19200  }
19201  else
19202  {
19203  return VK_SUCCESS;
19204  }
19205 }
19206 
19207 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19208  VmaAllocator allocator,
19209  VmaDefragmentationContext context,
19211  )
19212 {
19213  VMA_ASSERT(allocator);
19214  VMA_ASSERT(pInfo);
19215 
19216  VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19217 
19218  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19219 
19220  if(context == VK_NULL_HANDLE)
19221  {
19222  pInfo->moveCount = 0;
19223  return VK_SUCCESS;
19224  }
19225 
19226  return allocator->DefragmentationPassBegin(pInfo, context);
19227 }
19228 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19229  VmaAllocator allocator,
19230  VmaDefragmentationContext context)
19231 {
19232  VMA_ASSERT(allocator);
19233 
19234  VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19235  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19236 
19237  if(context == VK_NULL_HANDLE)
19238  return VK_SUCCESS;
19239 
19240  return allocator->DefragmentationPassEnd(context);
19241 }
19242 
19243 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19244  VmaAllocator allocator,
19245  VmaAllocation allocation,
19246  VkBuffer buffer)
19247 {
19248  VMA_ASSERT(allocator && allocation && buffer);
19249 
19250  VMA_DEBUG_LOG("vmaBindBufferMemory");
19251 
19252  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19253 
19254  return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19255 }
19256 
19257 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19258  VmaAllocator allocator,
19259  VmaAllocation allocation,
19260  VkDeviceSize allocationLocalOffset,
19261  VkBuffer buffer,
19262  const void* pNext)
19263 {
19264  VMA_ASSERT(allocator && allocation && buffer);
19265 
19266  VMA_DEBUG_LOG("vmaBindBufferMemory2");
19267 
19268  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19269 
19270  return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19271 }
19272 
19273 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19274  VmaAllocator allocator,
19275  VmaAllocation allocation,
19276  VkImage image)
19277 {
19278  VMA_ASSERT(allocator && allocation && image);
19279 
19280  VMA_DEBUG_LOG("vmaBindImageMemory");
19281 
19282  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19283 
19284  return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19285 }
19286 
19287 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19288  VmaAllocator allocator,
19289  VmaAllocation allocation,
19290  VkDeviceSize allocationLocalOffset,
19291  VkImage image,
19292  const void* pNext)
19293 {
19294  VMA_ASSERT(allocator && allocation && image);
19295 
19296  VMA_DEBUG_LOG("vmaBindImageMemory2");
19297 
19298  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19299 
19300  return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19301 }
19302 
19303 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19304  VmaAllocator allocator,
19305  const VkBufferCreateInfo* pBufferCreateInfo,
19306  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19307  VkBuffer* pBuffer,
19308  VmaAllocation* pAllocation,
19309  VmaAllocationInfo* pAllocationInfo)
19310 {
19311  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19312 
19313  if(pBufferCreateInfo->size == 0)
19314  {
19315  return VK_ERROR_VALIDATION_FAILED_EXT;
19316  }
19317  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19318  !allocator->m_UseKhrBufferDeviceAddress)
19319  {
19320  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.");
19321  return VK_ERROR_VALIDATION_FAILED_EXT;
19322  }
19323 
19324  VMA_DEBUG_LOG("vmaCreateBuffer");
19325 
19326  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19327 
19328  *pBuffer = VK_NULL_HANDLE;
19329  *pAllocation = VK_NULL_HANDLE;
19330 
19331  // 1. Create VkBuffer.
19332  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19333  allocator->m_hDevice,
19334  pBufferCreateInfo,
19335  allocator->GetAllocationCallbacks(),
19336  pBuffer);
19337  if(res >= 0)
19338  {
19339  // 2. vkGetBufferMemoryRequirements.
19340  VkMemoryRequirements vkMemReq = {};
19341  bool requiresDedicatedAllocation = false;
19342  bool prefersDedicatedAllocation = false;
19343  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19344  requiresDedicatedAllocation, prefersDedicatedAllocation);
19345 
19346  // 3. Allocate memory using allocator.
19347  res = allocator->AllocateMemory(
19348  vkMemReq,
19349  requiresDedicatedAllocation,
19350  prefersDedicatedAllocation,
19351  *pBuffer, // dedicatedBuffer
19352  pBufferCreateInfo->usage, // dedicatedBufferUsage
19353  VK_NULL_HANDLE, // dedicatedImage
19354  *pAllocationCreateInfo,
19355  VMA_SUBALLOCATION_TYPE_BUFFER,
19356  1, // allocationCount
19357  pAllocation);
19358 
19359 #if VMA_RECORDING_ENABLED
19360  if(allocator->GetRecorder() != VMA_NULL)
19361  {
19362  allocator->GetRecorder()->RecordCreateBuffer(
19363  allocator->GetCurrentFrameIndex(),
19364  *pBufferCreateInfo,
19365  *pAllocationCreateInfo,
19366  *pAllocation);
19367  }
19368 #endif
19369 
19370  if(res >= 0)
19371  {
19372  // 3. Bind buffer with memory.
19373  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19374  {
19375  res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19376  }
19377  if(res >= 0)
19378  {
19379  // All steps succeeded.
19380  #if VMA_STATS_STRING_ENABLED
19381  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19382  #endif
19383  if(pAllocationInfo != VMA_NULL)
19384  {
19385  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19386  }
19387 
19388  return VK_SUCCESS;
19389  }
19390  allocator->FreeMemory(
19391  1, // allocationCount
19392  pAllocation);
19393  *pAllocation = VK_NULL_HANDLE;
19394  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19395  *pBuffer = VK_NULL_HANDLE;
19396  return res;
19397  }
19398  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19399  *pBuffer = VK_NULL_HANDLE;
19400  return res;
19401  }
19402  return res;
19403 }
19404 
19405 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19406  VmaAllocator allocator,
19407  VkBuffer buffer,
19408  VmaAllocation allocation)
19409 {
19410  VMA_ASSERT(allocator);
19411 
19412  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19413  {
19414  return;
19415  }
19416 
19417  VMA_DEBUG_LOG("vmaDestroyBuffer");
19418 
19419  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19420 
19421 #if VMA_RECORDING_ENABLED
19422  if(allocator->GetRecorder() != VMA_NULL)
19423  {
19424  allocator->GetRecorder()->RecordDestroyBuffer(
19425  allocator->GetCurrentFrameIndex(),
19426  allocation);
19427  }
19428 #endif
19429 
19430  if(buffer != VK_NULL_HANDLE)
19431  {
19432  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19433  }
19434 
19435  if(allocation != VK_NULL_HANDLE)
19436  {
19437  allocator->FreeMemory(
19438  1, // allocationCount
19439  &allocation);
19440  }
19441 }
19442 
19443 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19444  VmaAllocator allocator,
19445  const VkImageCreateInfo* pImageCreateInfo,
19446  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19447  VkImage* pImage,
19448  VmaAllocation* pAllocation,
19449  VmaAllocationInfo* pAllocationInfo)
19450 {
19451  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19452 
19453  if(pImageCreateInfo->extent.width == 0 ||
19454  pImageCreateInfo->extent.height == 0 ||
19455  pImageCreateInfo->extent.depth == 0 ||
19456  pImageCreateInfo->mipLevels == 0 ||
19457  pImageCreateInfo->arrayLayers == 0)
19458  {
19459  return VK_ERROR_VALIDATION_FAILED_EXT;
19460  }
19461 
19462  VMA_DEBUG_LOG("vmaCreateImage");
19463 
19464  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19465 
19466  *pImage = VK_NULL_HANDLE;
19467  *pAllocation = VK_NULL_HANDLE;
19468 
19469  // 1. Create VkImage.
19470  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19471  allocator->m_hDevice,
19472  pImageCreateInfo,
19473  allocator->GetAllocationCallbacks(),
19474  pImage);
19475  if(res >= 0)
19476  {
19477  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19478  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19479  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19480 
19481  // 2. Allocate memory using allocator.
19482  VkMemoryRequirements vkMemReq = {};
19483  bool requiresDedicatedAllocation = false;
19484  bool prefersDedicatedAllocation = false;
19485  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19486  requiresDedicatedAllocation, prefersDedicatedAllocation);
19487 
19488  res = allocator->AllocateMemory(
19489  vkMemReq,
19490  requiresDedicatedAllocation,
19491  prefersDedicatedAllocation,
19492  VK_NULL_HANDLE, // dedicatedBuffer
19493  UINT32_MAX, // dedicatedBufferUsage
19494  *pImage, // dedicatedImage
19495  *pAllocationCreateInfo,
19496  suballocType,
19497  1, // allocationCount
19498  pAllocation);
19499 
19500 #if VMA_RECORDING_ENABLED
19501  if(allocator->GetRecorder() != VMA_NULL)
19502  {
19503  allocator->GetRecorder()->RecordCreateImage(
19504  allocator->GetCurrentFrameIndex(),
19505  *pImageCreateInfo,
19506  *pAllocationCreateInfo,
19507  *pAllocation);
19508  }
19509 #endif
19510 
19511  if(res >= 0)
19512  {
19513  // 3. Bind image with memory.
19514  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19515  {
19516  res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19517  }
19518  if(res >= 0)
19519  {
19520  // All steps succeeded.
19521  #if VMA_STATS_STRING_ENABLED
19522  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19523  #endif
19524  if(pAllocationInfo != VMA_NULL)
19525  {
19526  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19527  }
19528 
19529  return VK_SUCCESS;
19530  }
19531  allocator->FreeMemory(
19532  1, // allocationCount
19533  pAllocation);
19534  *pAllocation = VK_NULL_HANDLE;
19535  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19536  *pImage = VK_NULL_HANDLE;
19537  return res;
19538  }
19539  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19540  *pImage = VK_NULL_HANDLE;
19541  return res;
19542  }
19543  return res;
19544 }
19545 
19546 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19547  VmaAllocator allocator,
19548  VkImage image,
19549  VmaAllocation allocation)
19550 {
19551  VMA_ASSERT(allocator);
19552 
19553  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19554  {
19555  return;
19556  }
19557 
19558  VMA_DEBUG_LOG("vmaDestroyImage");
19559 
19560  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19561 
19562 #if VMA_RECORDING_ENABLED
19563  if(allocator->GetRecorder() != VMA_NULL)
19564  {
19565  allocator->GetRecorder()->RecordDestroyImage(
19566  allocator->GetCurrentFrameIndex(),
19567  allocation);
19568  }
19569 #endif
19570 
19571  if(image != VK_NULL_HANDLE)
19572  {
19573  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19574  }
19575  if(allocation != VK_NULL_HANDLE)
19576  {
19577  allocator->FreeMemory(
19578  1, // allocationCount
19579  &allocation);
19580  }
19581 }
19582 
19583 #endif // #ifdef VMA_IMPLEMENTATION
Definition: vk_mem_alloc.h:2879
uint32_t memoryTypeBits
Bitmask containing one bit set for every memory type acceptable for this allocation.
Definition: vk_mem_alloc.h:2905
VmaPool pool
Pool that this allocation should be created in.
Definition: vk_mem_alloc.h:2911
VkMemoryPropertyFlags preferredFlags
Flags that preferably should be set in a memory type chosen for an allocation.
Definition: vk_mem_alloc.h:2897
void * pUserData
Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
Definition: vk_mem_alloc.h:2918
VkMemoryPropertyFlags requiredFlags
Flags that must be set in a Memory Type chosen for an allocation.
Definition: vk_mem_alloc.h:2892
float priority
A floating-point value between 0 and 1, indicating the priority of the allocation relative to other m...
Definition: vk_mem_alloc.h:2925
VmaMemoryUsage usage
Intended usage of memory.
Definition: vk_mem_alloc.h:2887
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2881
Represents single memory allocation.
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
Definition: vk_mem_alloc.h:3229
VkDeviceSize offset
Offset in VkDeviceMemory object to the beginning of this allocation, in bytes. (deviceMemory,...
Definition: vk_mem_alloc.h:3253
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:3273
uint32_t memoryType
Memory type index that this allocation was allocated from.
Definition: vk_mem_alloc.h:3234
VkDeviceSize size
Size of this allocation, in bytes.
Definition: vk_mem_alloc.h:3264
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:3278
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:3243
Description of a Allocator to be created.
Definition: vk_mem_alloc.h:2413
VkPhysicalDevice physicalDevice
Vulkan physical device.
Definition: vk_mem_alloc.h:2418
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:2444
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:2469
VmaAllocatorCreateFlags flags
Flags for created allocator. Use VmaAllocatorCreateFlagBits enum.
Definition: vk_mem_alloc.h:2415
const VmaVulkanFunctions * pVulkanFunctions
Pointers to Vulkan functions. Can be null.
Definition: vk_mem_alloc.h:2475
const VkAllocationCallbacks * pAllocationCallbacks
Custom CPU memory allocation callbacks. Optional.
Definition: vk_mem_alloc.h:2427
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2487
VkDeviceSize preferredLargeHeapBlockSize
Preferred size of a single VkDeviceMemory block to be allocated from large heaps > 1 GiB....
Definition: vk_mem_alloc.h:2424
const VmaRecordSettings * pRecordSettings
Parameters for recording of VMA calls. Can be null.
Definition: vk_mem_alloc.h:2482
VkDevice device
Vulkan device.
Definition: vk_mem_alloc.h:2421
uint32_t vulkanApiVersion
Optional. The highest version of Vulkan that the application is designed to use.
Definition: vk_mem_alloc.h:2496
const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
Informative callbacks for vkAllocateMemory, vkFreeMemory. Optional.
Definition: vk_mem_alloc.h:2430
Represents main object of this library initialized.
Information about existing VmaAllocator object.
Definition: vk_mem_alloc.h:2511
VkDevice device
Handle to Vulkan device object.
Definition: vk_mem_alloc.h:2526
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2516
VkPhysicalDevice physicalDevice
Handle to Vulkan physical device object.
Definition: vk_mem_alloc.h:2521
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
Definition: vk_mem_alloc.h:2617
VkDeviceSize blockBytes
Sum size of all VkDeviceMemory blocks allocated from particular heap, in bytes.
Definition: vk_mem_alloc.h:2620
VkDeviceSize allocationBytes
Sum size of all allocations created in particular heap, in bytes.
Definition: vk_mem_alloc.h:2631
VkDeviceSize usage
Estimated current memory usage of the program, in bytes.
Definition: vk_mem_alloc.h:2641
VkDeviceSize budget
Estimated amount of memory available to the program, in bytes.
Definition: vk_mem_alloc.h:2652
Represents Opaque object that represents started defragmentation process.
Parameters for defragmentation.
Definition: vk_mem_alloc.h:3628
const VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:3668
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:3634
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:3688
VkDeviceSize maxGpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3683
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:3631
VkBool32 * pAllocationsChanged
Optional, output. Pointer to array that will be filled with information whether the allocation at cer...
Definition: vk_mem_alloc.h:3649
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:3652
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:3697
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:3678
const VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:3643
VkDeviceSize maxCpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3673
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:3719
uint32_t maxAllocationsToMove
Maximum number of allocations that can be moved to different place.
Definition: vk_mem_alloc.h:3729
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3724
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:3710
uint32_t moveCount
Definition: vk_mem_alloc.h:3711
VmaDefragmentationPassMoveInfo * pMoves
Definition: vk_mem_alloc.h:3712
Definition: vk_mem_alloc.h:3700
VkDeviceMemory memory
Definition: vk_mem_alloc.h:3702
VkDeviceSize offset
Definition: vk_mem_alloc.h:3703
VmaAllocation allocation
Definition: vk_mem_alloc.h:3701
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:3733
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:3741
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3735
VkDeviceSize bytesFreed
Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects.
Definition: vk_mem_alloc.h:3737
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:3739
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
Definition: vk_mem_alloc.h:2222
void * pUserData
Optional, can be null.
Definition: vk_mem_alloc.h:2228
PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
Optional, can be null.
Definition: vk_mem_alloc.h:2224
PFN_vmaFreeDeviceMemoryFunction pfnFree
Optional, can be null.
Definition: vk_mem_alloc.h:2226
Describes parameter of created VmaPool.
Definition: vk_mem_alloc.h:3047
float priority
A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relat...
Definition: vk_mem_alloc.h:3095
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition: vk_mem_alloc.h:3050
VmaPoolCreateFlags flags
Use combination of VmaPoolCreateFlagBits.
Definition: vk_mem_alloc.h:3053
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:3089
VkDeviceSize blockSize
Size of a single VkDeviceMemory block to be allocated as part of this pool, in bytes....
Definition: vk_mem_alloc.h:3062
size_t minBlockCount
Minimum number of blocks to be always allocated in this pool, even if they stay empty.
Definition: vk_mem_alloc.h:3067
size_t maxBlockCount
Maximum number of blocks that can be allocated in this pool. Optional.
Definition: vk_mem_alloc.h:3075
Represents custom memory pool.
Describes parameter of existing VmaPool.
Definition: vk_mem_alloc.h:3100
VkDeviceSize size
Total amount of VkDeviceMemory allocated from Vulkan for this pool, in bytes.
Definition: vk_mem_alloc.h:3103
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:3122
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:3119
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost.
Definition: vk_mem_alloc.h:3109
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3106
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3112
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
Definition: vk_mem_alloc.h:2398
const char * pFilePath
Path to the file that should be written by the recording.
Definition: vk_mem_alloc.h:2408
VmaRecordFlags flags
Flags for recording. Use VmaRecordFlagBits enum.
Definition: vk_mem_alloc.h:2400
Calculated statistics of memory usage in entire allocator.
Definition: vk_mem_alloc.h:2578
VkDeviceSize allocationSizeAvg
Definition: vk_mem_alloc.h:2589
VkDeviceSize allocationSizeMax
Definition: vk_mem_alloc.h:2589
VkDeviceSize unusedBytes
Total number of bytes occupied by unused ranges.
Definition: vk_mem_alloc.h:2588
VkDeviceSize unusedRangeSizeAvg
Definition: vk_mem_alloc.h:2590
uint32_t allocationCount
Number of VmaAllocation allocation objects allocated.
Definition: vk_mem_alloc.h:2582
VkDeviceSize unusedRangeSizeMax
Definition: vk_mem_alloc.h:2590
VkDeviceSize usedBytes
Total number of bytes occupied by all allocations.
Definition: vk_mem_alloc.h:2586
uint32_t blockCount
Number of VkDeviceMemory Vulkan memory blocks allocated.
Definition: vk_mem_alloc.h:2580
VkDeviceSize allocationSizeMin
Definition: vk_mem_alloc.h:2589
uint32_t unusedRangeCount
Number of free ranges of memory between allocations.
Definition: vk_mem_alloc.h:2584
VkDeviceSize unusedRangeSizeMin
Definition: vk_mem_alloc.h:2590
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:2595
VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
Definition: vk_mem_alloc.h:2597
VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
Definition: vk_mem_alloc.h:2596
VmaStatInfo total
Definition: vk_mem_alloc.h:2598
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:2352
PFN_vkBindImageMemory vkBindImageMemory
Definition: vk_mem_alloc.h:2362
PFN_vkCreateImage vkCreateImage
Definition: vk_mem_alloc.h:2367
PFN_vkAllocateMemory vkAllocateMemory
Definition: vk_mem_alloc.h:2355
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges
Definition: vk_mem_alloc.h:2359
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements
Definition: vk_mem_alloc.h:2364
PFN_vkFreeMemory vkFreeMemory
Definition: vk_mem_alloc.h:2356
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements
Definition: vk_mem_alloc.h:2363
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges
Definition: vk_mem_alloc.h:2360
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties
Definition: vk_mem_alloc.h:2354
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties
Definition: vk_mem_alloc.h:2353
PFN_vkDestroyBuffer vkDestroyBuffer
Definition: vk_mem_alloc.h:2366
PFN_vkDestroyImage vkDestroyImage
Definition: vk_mem_alloc.h:2368
PFN_vkBindBufferMemory vkBindBufferMemory
Definition: vk_mem_alloc.h:2361
PFN_vkMapMemory vkMapMemory
Definition: vk_mem_alloc.h:2357
PFN_vkUnmapMemory vkUnmapMemory
Definition: vk_mem_alloc.h:2358
PFN_vkCmdCopyBuffer vkCmdCopyBuffer
Definition: vk_mem_alloc.h:2369
PFN_vkCreateBuffer vkCreateBuffer
Definition: vk_mem_alloc.h:2365
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaCreateBuffer().
VkResult vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
VkResult vmaAllocateMemoryForImage(VmaAllocator allocator, VkImage image, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaAllocateMemoryForBuffer().
struct VmaPoolCreateInfo VmaPoolCreateInfo
Describes parameter of created VmaPool.
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:2208
struct VmaRecordSettings VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
struct VmaAllocatorInfo VmaAllocatorInfo
Information about existing VmaAllocator object.
VkResult vmaEndDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context)
struct VmaAllocationInfo VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
#define VMA_RECORDING_ENABLED
Definition: vk_mem_alloc.h:2029
VkResult vmaCreateAllocator(const VmaAllocatorCreateInfo *pCreateInfo, VmaAllocator *pAllocator)
Creates Allocator object.
struct VmaStats VmaStats
General statistics from current state of Allocator.
VkFlags VmaPoolCreateFlags
Definition: vk_mem_alloc.h:3043
struct VmaDefragmentationInfo VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
VkResult vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Flushes memory of given allocation.
void vmaFreeStatsString(VmaAllocator allocator, char *pStatsString)
void vmaCalculateStats(VmaAllocator allocator, VmaStats *pStats)
Retrieves statistics from current state of the Allocator.
VkResult vmaDefragmentationBegin(VmaAllocator allocator, const VmaDefragmentationInfo2 *pInfo, VmaDefragmentationStats *pStats, VmaDefragmentationContext *pContext)
Begins defragmentation process.
struct VmaAllocationCreateInfo VmaAllocationCreateInfo
VkResult vmaBindImageMemory(VmaAllocator allocator, VmaAllocation allocation, VkImage image)
Binds image to allocation.
VkBool32 vmaTouchAllocation(VmaAllocator allocator, VmaAllocation allocation)
Returns VK_TRUE if allocation is not lost and atomically marks it as used in current frame.
struct VmaPoolStats VmaPoolStats
Describes parameter of existing VmaPool.
VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
Checks magic number in margins around all allocations in given memory types (in both default and cust...
VmaRecordFlagBits
Flags to be used in VmaRecordSettings::flags.
Definition: vk_mem_alloc.h:2384
@ VMA_RECORD_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2392
@ VMA_RECORD_FLUSH_AFTER_CALL_BIT
Enables flush after recording every function call.
Definition: vk_mem_alloc.h:2390
VmaAllocatorCreateFlagBits
Flags for created VmaAllocator.
Definition: vk_mem_alloc.h:2232
@ VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
Definition: vk_mem_alloc.h:2307
@ 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:2237
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
Definition: vk_mem_alloc.h:2289
@ VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
Definition: vk_mem_alloc.h:2325
@ VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
Definition: vk_mem_alloc.h:2277
@ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
Enables usage of VK_KHR_dedicated_allocation extension.
Definition: vk_mem_alloc.h:2262
@ VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2344
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
Definition: vk_mem_alloc.h:2342
VkFlags VmaAllocationCreateFlags
Definition: vk_mem_alloc.h:2876
void vmaDestroyPool(VmaAllocator allocator, VmaPool pool)
Destroys VmaPool object and frees Vulkan device memory.
VkResult vmaCreatePool(VmaAllocator allocator, const VmaPoolCreateInfo *pCreateInfo, VmaPool *pPool)
Allocates Vulkan device memory and creates VmaPool object.
void vmaFreeMemory(VmaAllocator allocator, const VmaAllocation allocation)
Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(),...
VmaDefragmentationFlagBits
Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
Definition: vk_mem_alloc.h:3618
@ VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
Definition: vk_mem_alloc.h:3619
@ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3620
VkResult vmaBindBufferMemory(VmaAllocator allocator, VmaAllocation allocation, VkBuffer buffer)
Binds buffer to allocation.
struct VmaDefragmentationPassInfo VmaDefragmentationPassInfo
Parameters for incremental defragmentation steps.
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...
struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
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:2201
VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
void vmaFreeMemoryPages(VmaAllocator allocator, size_t allocationCount, const VmaAllocation *pAllocations)
Frees memory and destroys multiple allocations.
void vmaGetAllocationInfo(VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
Returns current information about specified allocation and atomically marks it as used in current fra...
void vmaGetMemoryTypeProperties(VmaAllocator allocator, uint32_t memoryTypeIndex, VkMemoryPropertyFlags *pFlags)
Given Memory Type Index, returns Property Flags of this memory type.
VkResult vmaDefragmentationEnd(VmaAllocator allocator, VmaDefragmentationContext context)
Ends defragmentation process.
VkFlags VmaDefragmentationFlags
Definition: vk_mem_alloc.h:3622
VkResult vmaBindBufferMemory2(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize allocationLocalOffset, VkBuffer buffer, const void *pNext)
Binds buffer to allocation with additional parameters.
VmaPoolCreateFlagBits
Flags to be passed as VmaPoolCreateInfo::flags.
Definition: vk_mem_alloc.h:2987
@ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
Enables alternative, linear allocation algorithm in this pool.
Definition: vk_mem_alloc.h:3022
@ VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3041
@ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
Enables alternative, buddy allocation algorithm in this pool.
Definition: vk_mem_alloc.h:3033
@ 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:3005
@ VMA_POOL_CREATE_ALGORITHM_MASK
Definition: vk_mem_alloc.h:3037
void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
VkResult vmaDefragment(VmaAllocator allocator, const VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
Deprecated. Compacts memory by moving allocations.
struct VmaBudget VmaBudget
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
void vmaBuildStatsString(VmaAllocator allocator, char **ppStatsString, VkBool32 detailedMap)
Builds and returns statistics as string in JSON format.
VmaMemoryUsage
Definition: vk_mem_alloc.h:2700
@ VMA_MEMORY_USAGE_MAX_ENUM
Definition: vk_mem_alloc.h:2763
@ VMA_MEMORY_USAGE_CPU_ONLY
Definition: vk_mem_alloc.h:2731
@ VMA_MEMORY_USAGE_CPU_COPY
Definition: vk_mem_alloc.h:2753
@ VMA_MEMORY_USAGE_GPU_TO_CPU
Definition: vk_mem_alloc.h:2747
@ VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
Definition: vk_mem_alloc.h:2761
@ VMA_MEMORY_USAGE_CPU_TO_GPU
Definition: vk_mem_alloc.h:2738
@ VMA_MEMORY_USAGE_GPU_ONLY
Definition: vk_mem_alloc.h:2721
@ VMA_MEMORY_USAGE_UNKNOWN
Definition: vk_mem_alloc.h:2704
VkResult vmaBindImageMemory2(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize allocationLocalOffset, VkImage image, const void *pNext)
Binds image to allocation with additional parameters.
void vmaDestroyAllocator(VmaAllocator allocator)
Destroys allocator object.
VkResult vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Invalidates memory of given allocation.
VkResult vmaInvalidateAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Invalidates memory of given set of allocations.
void vmaGetMemoryProperties(VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
struct VmaVulkanFunctions VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
VkResult vmaAllocateMemory(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
General purpose memory allocation.
VkResult vmaBeginDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationPassInfo *pInfo)
VkResult vmaFlushAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Flushes memory of given set of allocations.
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
VkFlags VmaAllocatorCreateFlags
Definition: vk_mem_alloc.h:2346
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.
VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
Checks magic number in margins around all allocations in given memory pool in search for corruptions.
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void **ppData)
Maps memory represented by given allocation and returns pointer to it.
struct VmaDefragmentationPassMoveInfo VmaDefragmentationPassMoveInfo
struct VmaDefragmentationInfo2 VmaDefragmentationInfo2
Parameters for defragmentation.
struct VmaDefragmentationStats VmaDefragmentationStats
Statistics returned by function vmaDefragment().
VmaAllocationCreateFlagBits
Flags to be passed as VmaAllocationCreateInfo::flags.
Definition: vk_mem_alloc.h:2767
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
Definition: vk_mem_alloc.h:2862
@ 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:2798
@ VMA_ALLOCATION_CREATE_DONT_BIND_BIT
Definition: vk_mem_alloc.h:2835
@ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
Definition: vk_mem_alloc.h:2855
@ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2774
@ VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
Definition: vk_mem_alloc.h:2829
@ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
Definition: vk_mem_alloc.h:2811
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
Definition: vk_mem_alloc.h:2865
@ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
Definition: vk_mem_alloc.h:2818
@ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
Definition: vk_mem_alloc.h:2844
@ 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:2785
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
Definition: vk_mem_alloc.h:2859
@ VMA_ALLOCATION_CREATE_STRATEGY_MASK
Definition: vk_mem_alloc.h:2869
@ VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
Definition: vk_mem_alloc.h:2824
@ VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
Definition: vk_mem_alloc.h:2839
@ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
Definition: vk_mem_alloc.h:2848
@ VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2874
void vmaSetPoolName(VmaAllocator allocator, VmaPool pool, const char *pName)
Sets name of a custom pool.
void vmaSetCurrentFrameIndex(VmaAllocator allocator, uint32_t frameIndex)
Sets index of the current frame.
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation)
Destroys Vulkan image and frees allocated memory.
void vmaCreateLostAllocation(VmaAllocator allocator, VmaAllocation *pAllocation)
Creates new allocation that is in lost state from the beginning.
VkResult vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
void vmaGetPoolStats(VmaAllocator allocator, VmaPool pool, VmaPoolStats *pPoolStats)
Retrieves statistics of existing VmaPool object.
void vmaGetBudget(VmaAllocator allocator, VmaBudget *pBudget)
Retrieves information about current memory budget for all memory heaps.
struct VmaStatInfo VmaStatInfo
Calculated statistics of memory usage in entire allocator.
void vmaGetPhysicalDeviceProperties(VmaAllocator allocator, const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
VkResult vmaFindMemoryTypeIndex(VmaAllocator allocator, uint32_t memoryTypeBits, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
void vmaGetPoolName(VmaAllocator allocator, VmaPool pool, const char **ppName)
Retrieves name of a custom pool.
VkFlags VmaRecordFlags
Definition: vk_mem_alloc.h:2394
void vmaSetAllocationUserData(VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
Sets pUserData in given allocation to new value.
void vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo *pAllocatorInfo)
Returns information about existing VmaAllocator object - handle to Vulkan device etc.