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;
3104 
3107 typedef struct VmaPoolStats {
3110  VkDeviceSize size;
3113  VkDeviceSize unusedSize;
3126  VkDeviceSize unusedRangeSizeMax;
3129  size_t blockCount;
3131 
3138 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
3139  VmaAllocator VMA_NOT_NULL allocator,
3140  const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
3141  VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
3142 
3145 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
3146  VmaAllocator VMA_NOT_NULL allocator,
3147  VmaPool VMA_NULLABLE pool);
3148 
3155 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3156  VmaAllocator VMA_NOT_NULL allocator,
3157  VmaPool VMA_NOT_NULL pool,
3158  VmaPoolStats* VMA_NOT_NULL pPoolStats);
3159 
3166 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3167  VmaAllocator VMA_NOT_NULL allocator,
3168  VmaPool VMA_NOT_NULL pool,
3169  size_t* VMA_NULLABLE pLostAllocationCount);
3170 
3185 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3186 
3193 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3194  VmaAllocator VMA_NOT_NULL allocator,
3195  VmaPool VMA_NOT_NULL pool,
3196  const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3197 
3203 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3204  VmaAllocator VMA_NOT_NULL allocator,
3205  VmaPool VMA_NOT_NULL pool,
3206  const char* VMA_NULLABLE pName);
3207 
3232 VK_DEFINE_HANDLE(VmaAllocation)
3233 
3234 
3236 typedef struct VmaAllocationInfo {
3241  uint32_t memoryType;
3250  VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3260  VkDeviceSize offset;
3271  VkDeviceSize size;
3280  void* VMA_NULLABLE pMappedData;
3285  void* VMA_NULLABLE pUserData;
3287 
3298 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3299  VmaAllocator VMA_NOT_NULL allocator,
3300  const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3301  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3302  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3303  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3304 
3324 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3325  VmaAllocator VMA_NOT_NULL allocator,
3326  const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3327  const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3328  size_t allocationCount,
3329  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3330  VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3331 
3338 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3339  VmaAllocator VMA_NOT_NULL allocator,
3340  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3341  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3342  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3343  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3344 
3346 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3347  VmaAllocator VMA_NOT_NULL allocator,
3348  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3349  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3350  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3351  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3352 
3357 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3358  VmaAllocator VMA_NOT_NULL allocator,
3359  const VmaAllocation VMA_NULLABLE allocation);
3360 
3371 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3372  VmaAllocator VMA_NOT_NULL allocator,
3373  size_t allocationCount,
3374  const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3375 
3392 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3393  VmaAllocator VMA_NOT_NULL allocator,
3394  VmaAllocation VMA_NOT_NULL allocation,
3395  VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3396 
3411 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3412  VmaAllocator VMA_NOT_NULL allocator,
3413  VmaAllocation VMA_NOT_NULL allocation);
3414 
3428 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3429  VmaAllocator VMA_NOT_NULL allocator,
3430  VmaAllocation VMA_NOT_NULL allocation,
3431  void* VMA_NULLABLE pUserData);
3432 
3443 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3444  VmaAllocator VMA_NOT_NULL allocator,
3445  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3446 
3485 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3486  VmaAllocator VMA_NOT_NULL allocator,
3487  VmaAllocation VMA_NOT_NULL allocation,
3488  void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3489 
3498 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3499  VmaAllocator VMA_NOT_NULL allocator,
3500  VmaAllocation VMA_NOT_NULL allocation);
3501 
3523 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3524  VmaAllocator VMA_NOT_NULL allocator,
3525  VmaAllocation VMA_NOT_NULL allocation,
3526  VkDeviceSize offset,
3527  VkDeviceSize size);
3528 
3550 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3551  VmaAllocator VMA_NOT_NULL allocator,
3552  VmaAllocation VMA_NOT_NULL allocation,
3553  VkDeviceSize offset,
3554  VkDeviceSize size);
3555 
3570 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3571  VmaAllocator VMA_NOT_NULL allocator,
3572  uint32_t allocationCount,
3573  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3574  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3575  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3576 
3591 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3592  VmaAllocator VMA_NOT_NULL allocator,
3593  uint32_t allocationCount,
3594  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3595  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3596  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3597 
3614 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3615 
3622 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3623 
3624 typedef enum VmaDefragmentationFlagBits {
3629 typedef VkFlags VmaDefragmentationFlags;
3630 
3635 typedef struct VmaDefragmentationInfo2 {
3650  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3656  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3659  uint32_t poolCount;
3675  const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3680  VkDeviceSize maxCpuBytesToMove;
3690  VkDeviceSize maxGpuBytesToMove;
3704  VkCommandBuffer VMA_NULLABLE commandBuffer;
3706 
3709  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3710  VkDeviceSize offset;
3712 
3718  uint32_t moveCount;
3719  VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3721 
3726 typedef struct VmaDefragmentationInfo {
3731  VkDeviceSize maxBytesToMove;
3738 
3740 typedef struct VmaDefragmentationStats {
3742  VkDeviceSize bytesMoved;
3744  VkDeviceSize bytesFreed;
3750 
3780 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3781  VmaAllocator VMA_NOT_NULL allocator,
3782  const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3783  VmaDefragmentationStats* VMA_NULLABLE pStats,
3784  VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3785 
3791 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3792  VmaAllocator VMA_NOT_NULL allocator,
3793  VmaDefragmentationContext VMA_NULLABLE context);
3794 
3795 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3796  VmaAllocator VMA_NOT_NULL allocator,
3797  VmaDefragmentationContext VMA_NULLABLE context,
3798  VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3799 );
3800 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3801  VmaAllocator VMA_NOT_NULL allocator,
3802  VmaDefragmentationContext VMA_NULLABLE context
3803 );
3804 
3845 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3846  VmaAllocator VMA_NOT_NULL allocator,
3847  const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3848  size_t allocationCount,
3849  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3850  const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3851  VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3852 
3865 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3866  VmaAllocator VMA_NOT_NULL allocator,
3867  VmaAllocation VMA_NOT_NULL allocation,
3868  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3869 
3880 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3881  VmaAllocator VMA_NOT_NULL allocator,
3882  VmaAllocation VMA_NOT_NULL allocation,
3883  VkDeviceSize allocationLocalOffset,
3884  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3885  const void* VMA_NULLABLE pNext);
3886 
3899 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3900  VmaAllocator VMA_NOT_NULL allocator,
3901  VmaAllocation VMA_NOT_NULL allocation,
3902  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3903 
3914 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3915  VmaAllocator VMA_NOT_NULL allocator,
3916  VmaAllocation VMA_NOT_NULL allocation,
3917  VkDeviceSize allocationLocalOffset,
3918  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3919  const void* VMA_NULLABLE pNext);
3920 
3951 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3952  VmaAllocator VMA_NOT_NULL allocator,
3953  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3954  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3955  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3956  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3957  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3958 
3970 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3971  VmaAllocator VMA_NOT_NULL allocator,
3972  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3973  VmaAllocation VMA_NULLABLE allocation);
3974 
3976 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3977  VmaAllocator VMA_NOT_NULL allocator,
3978  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3979  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3980  VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3981  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3982  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3983 
3995 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3996  VmaAllocator VMA_NOT_NULL allocator,
3997  VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3998  VmaAllocation VMA_NULLABLE allocation);
3999 
4000 #ifdef __cplusplus
4001 }
4002 #endif
4003 
4004 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
4005 
4006 // For Visual Studio IntelliSense.
4007 #if defined(__cplusplus) && defined(__INTELLISENSE__)
4008 #define VMA_IMPLEMENTATION
4009 #endif
4010 
4011 #ifdef VMA_IMPLEMENTATION
4012 #undef VMA_IMPLEMENTATION
4013 
4014 #include <cstdint>
4015 #include <cstdlib>
4016 #include <cstring>
4017 #include <utility>
4018 
4019 #if VMA_RECORDING_ENABLED
4020  #include <chrono>
4021  #if defined(_WIN32)
4022  #include <windows.h>
4023  #else
4024  #include <sstream>
4025  #include <thread>
4026  #endif
4027 #endif
4028 
4029 /*******************************************************************************
4030 CONFIGURATION SECTION
4031 
4032 Define some of these macros before each #include of this header or change them
4033 here if you need other then default behavior depending on your environment.
4034 */
4035 
4036 /*
4037 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4038 internally, like:
4039 
4040  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
4041 */
4042 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
4043  #define VMA_STATIC_VULKAN_FUNCTIONS 1
4044 #endif
4045 
4046 /*
4047 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4048 internally, like:
4049 
4050  vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
4051 */
4052 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
4053  #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
4054  #if defined(VK_NO_PROTOTYPES)
4055  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
4056  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
4057  #endif
4058 #endif
4059 
4060 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
4061 //#define VMA_USE_STL_CONTAINERS 1
4062 
4063 /* Set this macro to 1 to make the library including and using STL containers:
4064 std::pair, std::vector, std::list, std::unordered_map.
4065 
4066 Set it to 0 or undefined to make the library using its own implementation of
4067 the containers.
4068 */
4069 #if VMA_USE_STL_CONTAINERS
4070  #define VMA_USE_STL_VECTOR 1
4071  #define VMA_USE_STL_UNORDERED_MAP 1
4072  #define VMA_USE_STL_LIST 1
4073 #endif
4074 
4075 #ifndef VMA_USE_STL_SHARED_MUTEX
4076  // Compiler conforms to C++17.
4077  #if __cplusplus >= 201703L
4078  #define VMA_USE_STL_SHARED_MUTEX 1
4079  // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
4080  // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
4081  // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
4082  #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
4083  #define VMA_USE_STL_SHARED_MUTEX 1
4084  #else
4085  #define VMA_USE_STL_SHARED_MUTEX 0
4086  #endif
4087 #endif
4088 
4089 /*
4090 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
4091 Library has its own container implementation.
4092 */
4093 #if VMA_USE_STL_VECTOR
4094  #include <vector>
4095 #endif
4096 
4097 #if VMA_USE_STL_UNORDERED_MAP
4098  #include <unordered_map>
4099 #endif
4100 
4101 #if VMA_USE_STL_LIST
4102  #include <list>
4103 #endif
4104 
4105 /*
4106 Following headers are used in this CONFIGURATION section only, so feel free to
4107 remove them if not needed.
4108 */
4109 #include <cassert> // for assert
4110 #include <algorithm> // for min, max
4111 #include <mutex>
4112 
4113 #ifndef VMA_NULL
4114  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
4115  #define VMA_NULL nullptr
4116 #endif
4117 
4118 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
4119 #include <cstdlib>
4120 static void* vma_aligned_alloc(size_t alignment, size_t size)
4121 {
4122  // alignment must be >= sizeof(void*)
4123  if(alignment < sizeof(void*))
4124  {
4125  alignment = sizeof(void*);
4126  }
4127 
4128  return memalign(alignment, size);
4129 }
4130 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
4131 #include <cstdlib>
4132 
4133 #if defined(__APPLE__)
4134 #include <AvailabilityMacros.h>
4135 #endif
4136 
4137 static void* vma_aligned_alloc(size_t alignment, size_t size)
4138 {
4139 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
4140 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
4141  // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
4142  // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
4143  // MAC_OS_X_VERSION_10_16), even though the function is marked
4144  // availabe for 10.15. That's why the preprocessor checks for 10.16 but
4145  // the __builtin_available checks for 10.15.
4146  // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
4147  if (__builtin_available(macOS 10.15, iOS 13, *))
4148  return aligned_alloc(alignment, size);
4149 #endif
4150 #endif
4151  // alignment must be >= sizeof(void*)
4152  if(alignment < sizeof(void*))
4153  {
4154  alignment = sizeof(void*);
4155  }
4156 
4157  void *pointer;
4158  if(posix_memalign(&pointer, alignment, size) == 0)
4159  return pointer;
4160  return VMA_NULL;
4161 }
4162 #elif defined(_WIN32)
4163 static void* vma_aligned_alloc(size_t alignment, size_t size)
4164 {
4165  return _aligned_malloc(size, alignment);
4166 }
4167 #else
4168 static void* vma_aligned_alloc(size_t alignment, size_t size)
4169 {
4170  return aligned_alloc(alignment, size);
4171 }
4172 #endif
4173 
4174 #if defined(_WIN32)
4175 static void vma_aligned_free(void* ptr)
4176 {
4177  _aligned_free(ptr);
4178 }
4179 #else
4180 static void vma_aligned_free(void* VMA_NULLABLE ptr)
4181 {
4182  free(ptr);
4183 }
4184 #endif
4185 
4186 // If your compiler is not compatible with C++11 and definition of
4187 // aligned_alloc() function is missing, uncommeting following line may help:
4188 
4189 //#include <malloc.h>
4190 
4191 // Normal assert to check for programmer's errors, especially in Debug configuration.
4192 #ifndef VMA_ASSERT
4193  #ifdef NDEBUG
4194  #define VMA_ASSERT(expr)
4195  #else
4196  #define VMA_ASSERT(expr) assert(expr)
4197  #endif
4198 #endif
4199 
4200 // Assert that will be called very often, like inside data structures e.g. operator[].
4201 // Making it non-empty can make program slow.
4202 #ifndef VMA_HEAVY_ASSERT
4203  #ifdef NDEBUG
4204  #define VMA_HEAVY_ASSERT(expr)
4205  #else
4206  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
4207  #endif
4208 #endif
4209 
4210 #ifndef VMA_ALIGN_OF
4211  #define VMA_ALIGN_OF(type) (__alignof(type))
4212 #endif
4213 
4214 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4215  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
4216 #endif
4217 
4218 #ifndef VMA_SYSTEM_ALIGNED_FREE
4219  // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
4220  #if defined(VMA_SYSTEM_FREE)
4221  #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
4222  #else
4223  #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
4224  #endif
4225 #endif
4226 
4227 #ifndef VMA_MIN
4228  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4229 #endif
4230 
4231 #ifndef VMA_MAX
4232  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4233 #endif
4234 
4235 #ifndef VMA_SWAP
4236  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4237 #endif
4238 
4239 #ifndef VMA_SORT
4240  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4241 #endif
4242 
4243 #ifndef VMA_DEBUG_LOG
4244  #define VMA_DEBUG_LOG(format, ...)
4245  /*
4246  #define VMA_DEBUG_LOG(format, ...) do { \
4247  printf(format, __VA_ARGS__); \
4248  printf("\n"); \
4249  } while(false)
4250  */
4251 #endif
4252 
4253 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4254 #if VMA_STATS_STRING_ENABLED
4255  static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
4256  {
4257  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4258  }
4259  static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
4260  {
4261  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4262  }
4263  static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
4264  {
4265  snprintf(outStr, strLen, "%p", ptr);
4266  }
4267 #endif
4268 
4269 #ifndef VMA_MUTEX
4270  class VmaMutex
4271  {
4272  public:
4273  void Lock() { m_Mutex.lock(); }
4274  void Unlock() { m_Mutex.unlock(); }
4275  bool TryLock() { return m_Mutex.try_lock(); }
4276  private:
4277  std::mutex m_Mutex;
4278  };
4279  #define VMA_MUTEX VmaMutex
4280 #endif
4281 
4282 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4283 #ifndef VMA_RW_MUTEX
4284  #if VMA_USE_STL_SHARED_MUTEX
4285  // Use std::shared_mutex from C++17.
4286  #include <shared_mutex>
4287  class VmaRWMutex
4288  {
4289  public:
4290  void LockRead() { m_Mutex.lock_shared(); }
4291  void UnlockRead() { m_Mutex.unlock_shared(); }
4292  bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4293  void LockWrite() { m_Mutex.lock(); }
4294  void UnlockWrite() { m_Mutex.unlock(); }
4295  bool TryLockWrite() { return m_Mutex.try_lock(); }
4296  private:
4297  std::shared_mutex m_Mutex;
4298  };
4299  #define VMA_RW_MUTEX VmaRWMutex
4300  #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4301  // Use SRWLOCK from WinAPI.
4302  // Minimum supported client = Windows Vista, server = Windows Server 2008.
4303  class VmaRWMutex
4304  {
4305  public:
4306  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4307  void LockRead() { AcquireSRWLockShared(&m_Lock); }
4308  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4309  bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4310  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4311  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4312  bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4313  private:
4314  SRWLOCK m_Lock;
4315  };
4316  #define VMA_RW_MUTEX VmaRWMutex
4317  #else
4318  // Less efficient fallback: Use normal mutex.
4319  class VmaRWMutex
4320  {
4321  public:
4322  void LockRead() { m_Mutex.Lock(); }
4323  void UnlockRead() { m_Mutex.Unlock(); }
4324  bool TryLockRead() { return m_Mutex.TryLock(); }
4325  void LockWrite() { m_Mutex.Lock(); }
4326  void UnlockWrite() { m_Mutex.Unlock(); }
4327  bool TryLockWrite() { return m_Mutex.TryLock(); }
4328  private:
4329  VMA_MUTEX m_Mutex;
4330  };
4331  #define VMA_RW_MUTEX VmaRWMutex
4332  #endif // #if VMA_USE_STL_SHARED_MUTEX
4333 #endif // #ifndef VMA_RW_MUTEX
4334 
4335 /*
4336 If providing your own implementation, you need to implement a subset of std::atomic.
4337 */
4338 #ifndef VMA_ATOMIC_UINT32
4339  #include <atomic>
4340  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4341 #endif
4342 
4343 #ifndef VMA_ATOMIC_UINT64
4344  #include <atomic>
4345  #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4346 #endif
4347 
4348 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4353  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4354 #endif
4355 
4356 #ifndef VMA_MIN_ALIGNMENT
4361  #ifdef VMA_DEBUG_ALIGNMENT // Old name
4362  #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
4363  #else
4364  #define VMA_MIN_ALIGNMENT (1)
4365  #endif
4366 #endif
4367 
4368 #ifndef VMA_DEBUG_MARGIN
4373  #define VMA_DEBUG_MARGIN (0)
4374 #endif
4375 
4376 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4381  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4382 #endif
4383 
4384 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4390  #define VMA_DEBUG_DETECT_CORRUPTION (0)
4391 #endif
4392 
4393 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4398  #define VMA_DEBUG_GLOBAL_MUTEX (0)
4399 #endif
4400 
4401 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4406  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4407 #endif
4408 
4409 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
4410  /*
4411  Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
4412  and return error instead of leaving up to Vulkan implementation what to do in such cases.
4413  */
4414  #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
4415 #endif
4416 
4417 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4419  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4420 #endif
4421 
4422 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4424  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4425 #endif
4426 
4427 #ifndef VMA_CLASS_NO_COPY
4428  #define VMA_CLASS_NO_COPY(className) \
4429  private: \
4430  className(const className&) = delete; \
4431  className& operator=(const className&) = delete;
4432 #endif
4433 
4434 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4435 
4436 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4437 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4438 
4439 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4440 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4441 
4442 /*******************************************************************************
4443 END OF CONFIGURATION
4444 */
4445 
4446 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4447 
4448 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4449 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4450 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4451 
4452 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4453 
4454 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4455  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4456 
4457 // Returns number of bits set to 1 in (v).
4458 static inline uint32_t VmaCountBitsSet(uint32_t v)
4459 {
4460  uint32_t c = v - ((v >> 1) & 0x55555555);
4461  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4462  c = ((c >> 4) + c) & 0x0F0F0F0F;
4463  c = ((c >> 8) + c) & 0x00FF00FF;
4464  c = ((c >> 16) + c) & 0x0000FFFF;
4465  return c;
4466 }
4467 
4468 /*
4469 Returns true if given number is a power of two.
4470 T must be unsigned integer number or signed integer but always nonnegative.
4471 For 0 returns true.
4472 */
4473 template <typename T>
4474 inline bool VmaIsPow2(T x)
4475 {
4476  return (x & (x-1)) == 0;
4477 }
4478 
4479 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4480 // Use types like uint32_t, uint64_t as T.
4481 template <typename T>
4482 static inline T VmaAlignUp(T val, T alignment)
4483 {
4484  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4485  return (val + alignment - 1) & ~(alignment - 1);
4486 }
4487 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4488 // Use types like uint32_t, uint64_t as T.
4489 template <typename T>
4490 static inline T VmaAlignDown(T val, T alignment)
4491 {
4492  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4493  return val & ~(alignment - 1);
4494 }
4495 
4496 // Division with mathematical rounding to nearest number.
4497 template <typename T>
4498 static inline T VmaRoundDiv(T x, T y)
4499 {
4500  return (x + (y / (T)2)) / y;
4501 }
4502 
4503 // Returns smallest power of 2 greater or equal to v.
4504 static inline uint32_t VmaNextPow2(uint32_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++;
4513  return v;
4514 }
4515 static inline uint64_t VmaNextPow2(uint64_t v)
4516 {
4517  v--;
4518  v |= v >> 1;
4519  v |= v >> 2;
4520  v |= v >> 4;
4521  v |= v >> 8;
4522  v |= v >> 16;
4523  v |= v >> 32;
4524  v++;
4525  return v;
4526 }
4527 
4528 // Returns largest power of 2 less or equal to v.
4529 static inline uint32_t VmaPrevPow2(uint32_t v)
4530 {
4531  v |= v >> 1;
4532  v |= v >> 2;
4533  v |= v >> 4;
4534  v |= v >> 8;
4535  v |= v >> 16;
4536  v = v ^ (v >> 1);
4537  return v;
4538 }
4539 static inline uint64_t VmaPrevPow2(uint64_t v)
4540 {
4541  v |= v >> 1;
4542  v |= v >> 2;
4543  v |= v >> 4;
4544  v |= v >> 8;
4545  v |= v >> 16;
4546  v |= v >> 32;
4547  v = v ^ (v >> 1);
4548  return v;
4549 }
4550 
4551 static inline bool VmaStrIsEmpty(const char* pStr)
4552 {
4553  return pStr == VMA_NULL || *pStr == '\0';
4554 }
4555 
4556 #if VMA_STATS_STRING_ENABLED
4557 
4558 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4559 {
4560  switch(algorithm)
4561  {
4563  return "Linear";
4565  return "Buddy";
4566  case 0:
4567  return "Default";
4568  default:
4569  VMA_ASSERT(0);
4570  return "";
4571  }
4572 }
4573 
4574 #endif // #if VMA_STATS_STRING_ENABLED
4575 
4576 #ifndef VMA_SORT
4577 
4578 template<typename Iterator, typename Compare>
4579 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4580 {
4581  Iterator centerValue = end; --centerValue;
4582  Iterator insertIndex = beg;
4583  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4584  {
4585  if(cmp(*memTypeIndex, *centerValue))
4586  {
4587  if(insertIndex != memTypeIndex)
4588  {
4589  VMA_SWAP(*memTypeIndex, *insertIndex);
4590  }
4591  ++insertIndex;
4592  }
4593  }
4594  if(insertIndex != centerValue)
4595  {
4596  VMA_SWAP(*insertIndex, *centerValue);
4597  }
4598  return insertIndex;
4599 }
4600 
4601 template<typename Iterator, typename Compare>
4602 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4603 {
4604  if(beg < end)
4605  {
4606  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4607  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4608  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4609  }
4610 }
4611 
4612 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4613 
4614 #endif // #ifndef VMA_SORT
4615 
4616 /*
4617 Returns true if two memory blocks occupy overlapping pages.
4618 ResourceA must be in less memory offset than ResourceB.
4619 
4620 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4621 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4622 */
4623 static inline bool VmaBlocksOnSamePage(
4624  VkDeviceSize resourceAOffset,
4625  VkDeviceSize resourceASize,
4626  VkDeviceSize resourceBOffset,
4627  VkDeviceSize pageSize)
4628 {
4629  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4630  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4631  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4632  VkDeviceSize resourceBStart = resourceBOffset;
4633  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4634  return resourceAEndPage == resourceBStartPage;
4635 }
4636 
4637 enum VmaSuballocationType
4638 {
4639  VMA_SUBALLOCATION_TYPE_FREE = 0,
4640  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4641  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4642  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4643  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4644  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4645  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4646 };
4647 
4648 /*
4649 Returns true if given suballocation types could conflict and must respect
4650 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4651 or linear image and another one is optimal image. If type is unknown, behave
4652 conservatively.
4653 */
4654 static inline bool VmaIsBufferImageGranularityConflict(
4655  VmaSuballocationType suballocType1,
4656  VmaSuballocationType suballocType2)
4657 {
4658  if(suballocType1 > suballocType2)
4659  {
4660  VMA_SWAP(suballocType1, suballocType2);
4661  }
4662 
4663  switch(suballocType1)
4664  {
4665  case VMA_SUBALLOCATION_TYPE_FREE:
4666  return false;
4667  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4668  return true;
4669  case VMA_SUBALLOCATION_TYPE_BUFFER:
4670  return
4671  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4672  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4673  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4674  return
4675  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4676  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4677  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4678  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4679  return
4680  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4681  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4682  return false;
4683  default:
4684  VMA_ASSERT(0);
4685  return true;
4686  }
4687 }
4688 
4689 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4690 {
4691 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4692  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4693  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4694  for(size_t i = 0; i < numberCount; ++i, ++pDst)
4695  {
4696  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4697  }
4698 #else
4699  // no-op
4700 #endif
4701 }
4702 
4703 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4704 {
4705 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4706  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4707  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4708  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4709  {
4710  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4711  {
4712  return false;
4713  }
4714  }
4715 #endif
4716  return true;
4717 }
4718 
4719 /*
4720 Fills structure with parameters of an example buffer to be used for transfers
4721 during GPU memory defragmentation.
4722 */
4723 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4724 {
4725  memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4726  outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4727  outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4728  outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4729 }
4730 
4731 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4732 struct VmaMutexLock
4733 {
4734  VMA_CLASS_NO_COPY(VmaMutexLock)
4735 public:
4736  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4737  m_pMutex(useMutex ? &mutex : VMA_NULL)
4738  { if(m_pMutex) { m_pMutex->Lock(); } }
4739  ~VmaMutexLock()
4740  { if(m_pMutex) { m_pMutex->Unlock(); } }
4741 private:
4742  VMA_MUTEX* m_pMutex;
4743 };
4744 
4745 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4746 struct VmaMutexLockRead
4747 {
4748  VMA_CLASS_NO_COPY(VmaMutexLockRead)
4749 public:
4750  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4751  m_pMutex(useMutex ? &mutex : VMA_NULL)
4752  { if(m_pMutex) { m_pMutex->LockRead(); } }
4753  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4754 private:
4755  VMA_RW_MUTEX* m_pMutex;
4756 };
4757 
4758 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4759 struct VmaMutexLockWrite
4760 {
4761  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4762 public:
4763  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4764  m_pMutex(useMutex ? &mutex : VMA_NULL)
4765  { if(m_pMutex) { m_pMutex->LockWrite(); } }
4766  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4767 private:
4768  VMA_RW_MUTEX* m_pMutex;
4769 };
4770 
4771 #if VMA_DEBUG_GLOBAL_MUTEX
4772  static VMA_MUTEX gDebugGlobalMutex;
4773  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4774 #else
4775  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4776 #endif
4777 
4778 // Minimum size of a free suballocation to register it in the free suballocation collection.
4779 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4780 
4781 /*
4782 Performs binary search and returns iterator to first element that is greater or
4783 equal to (key), according to comparison (cmp).
4784 
4785 Cmp should return true if first argument is less than second argument.
4786 
4787 Returned value is the found element, if present in the collection or place where
4788 new element with value (key) should be inserted.
4789 */
4790 template <typename CmpLess, typename IterT, typename KeyT>
4791 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4792 {
4793  size_t down = 0, up = (end - beg);
4794  while(down < up)
4795  {
4796  const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
4797  if(cmp(*(beg+mid), key))
4798  {
4799  down = mid + 1;
4800  }
4801  else
4802  {
4803  up = mid;
4804  }
4805  }
4806  return beg + down;
4807 }
4808 
4809 template<typename CmpLess, typename IterT, typename KeyT>
4810 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4811 {
4812  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4813  beg, end, value, cmp);
4814  if(it == end ||
4815  (!cmp(*it, value) && !cmp(value, *it)))
4816  {
4817  return it;
4818  }
4819  return end;
4820 }
4821 
4822 /*
4823 Returns true if all pointers in the array are not-null and unique.
4824 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4825 T must be pointer type, e.g. VmaAllocation, VmaPool.
4826 */
4827 template<typename T>
4828 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4829 {
4830  for(uint32_t i = 0; i < count; ++i)
4831  {
4832  const T iPtr = arr[i];
4833  if(iPtr == VMA_NULL)
4834  {
4835  return false;
4836  }
4837  for(uint32_t j = i + 1; j < count; ++j)
4838  {
4839  if(iPtr == arr[j])
4840  {
4841  return false;
4842  }
4843  }
4844  }
4845  return true;
4846 }
4847 
4848 template<typename MainT, typename NewT>
4849 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4850 {
4851  newStruct->pNext = mainStruct->pNext;
4852  mainStruct->pNext = newStruct;
4853 }
4854 
4856 // Memory allocation
4857 
4858 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4859 {
4860  void* result = VMA_NULL;
4861  if((pAllocationCallbacks != VMA_NULL) &&
4862  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4863  {
4864  result = (*pAllocationCallbacks->pfnAllocation)(
4865  pAllocationCallbacks->pUserData,
4866  size,
4867  alignment,
4868  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4869  }
4870  else
4871  {
4872  result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4873  }
4874  VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4875  return result;
4876 }
4877 
4878 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4879 {
4880  if((pAllocationCallbacks != VMA_NULL) &&
4881  (pAllocationCallbacks->pfnFree != VMA_NULL))
4882  {
4883  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4884  }
4885  else
4886  {
4887  VMA_SYSTEM_ALIGNED_FREE(ptr);
4888  }
4889 }
4890 
4891 template<typename T>
4892 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4893 {
4894  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4895 }
4896 
4897 template<typename T>
4898 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4899 {
4900  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4901 }
4902 
4903 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4904 
4905 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4906 
4907 template<typename T>
4908 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4909 {
4910  ptr->~T();
4911  VmaFree(pAllocationCallbacks, ptr);
4912 }
4913 
4914 template<typename T>
4915 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4916 {
4917  if(ptr != VMA_NULL)
4918  {
4919  for(size_t i = count; i--; )
4920  {
4921  ptr[i].~T();
4922  }
4923  VmaFree(pAllocationCallbacks, ptr);
4924  }
4925 }
4926 
4927 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4928 {
4929  if(srcStr != VMA_NULL)
4930  {
4931  const size_t len = strlen(srcStr);
4932  char* const result = vma_new_array(allocs, char, len + 1);
4933  memcpy(result, srcStr, len + 1);
4934  return result;
4935  }
4936  else
4937  {
4938  return VMA_NULL;
4939  }
4940 }
4941 
4942 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4943 {
4944  if(str != VMA_NULL)
4945  {
4946  const size_t len = strlen(str);
4947  vma_delete_array(allocs, str, len + 1);
4948  }
4949 }
4950 
4951 // STL-compatible allocator.
4952 template<typename T>
4953 class VmaStlAllocator
4954 {
4955 public:
4956  const VkAllocationCallbacks* const m_pCallbacks;
4957  typedef T value_type;
4958 
4959  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4960  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4961 
4962  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4963  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4964 
4965  template<typename U>
4966  bool operator==(const VmaStlAllocator<U>& rhs) const
4967  {
4968  return m_pCallbacks == rhs.m_pCallbacks;
4969  }
4970  template<typename U>
4971  bool operator!=(const VmaStlAllocator<U>& rhs) const
4972  {
4973  return m_pCallbacks != rhs.m_pCallbacks;
4974  }
4975 
4976  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4977  VmaStlAllocator(const VmaStlAllocator&) = default;
4978 };
4979 
4980 #if VMA_USE_STL_VECTOR
4981 
4982 #define VmaVector std::vector
4983 
4984 template<typename T, typename allocatorT>
4985 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4986 {
4987  vec.insert(vec.begin() + index, item);
4988 }
4989 
4990 template<typename T, typename allocatorT>
4991 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4992 {
4993  vec.erase(vec.begin() + index);
4994 }
4995 
4996 #else // #if VMA_USE_STL_VECTOR
4997 
4998 /* Class with interface compatible with subset of std::vector.
4999 T must be POD because constructors and destructors are not called and memcpy is
5000 used for these objects. */
5001 template<typename T, typename AllocatorT>
5002 class VmaVector
5003 {
5004 public:
5005  typedef T value_type;
5006 
5007  VmaVector(const AllocatorT& allocator) :
5008  m_Allocator(allocator),
5009  m_pArray(VMA_NULL),
5010  m_Count(0),
5011  m_Capacity(0)
5012  {
5013  }
5014 
5015  VmaVector(size_t count, const AllocatorT& allocator) :
5016  m_Allocator(allocator),
5017  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
5018  m_Count(count),
5019  m_Capacity(count)
5020  {
5021  }
5022 
5023  // This version of the constructor is here for compatibility with pre-C++14 std::vector.
5024  // value is unused.
5025  VmaVector(size_t count, const T& value, const AllocatorT& allocator)
5026  : VmaVector(count, allocator) {}
5027 
5028  VmaVector(const VmaVector<T, AllocatorT>& src) :
5029  m_Allocator(src.m_Allocator),
5030  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
5031  m_Count(src.m_Count),
5032  m_Capacity(src.m_Count)
5033  {
5034  if(m_Count != 0)
5035  {
5036  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
5037  }
5038  }
5039 
5040  ~VmaVector()
5041  {
5042  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5043  }
5044 
5045  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
5046  {
5047  if(&rhs != this)
5048  {
5049  resize(rhs.m_Count);
5050  if(m_Count != 0)
5051  {
5052  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
5053  }
5054  }
5055  return *this;
5056  }
5057 
5058  bool empty() const { return m_Count == 0; }
5059  size_t size() const { return m_Count; }
5060  T* data() { return m_pArray; }
5061  const T* data() const { return m_pArray; }
5062 
5063  T& operator[](size_t index)
5064  {
5065  VMA_HEAVY_ASSERT(index < m_Count);
5066  return m_pArray[index];
5067  }
5068  const T& operator[](size_t index) const
5069  {
5070  VMA_HEAVY_ASSERT(index < m_Count);
5071  return m_pArray[index];
5072  }
5073 
5074  T& front()
5075  {
5076  VMA_HEAVY_ASSERT(m_Count > 0);
5077  return m_pArray[0];
5078  }
5079  const T& front() const
5080  {
5081  VMA_HEAVY_ASSERT(m_Count > 0);
5082  return m_pArray[0];
5083  }
5084  T& back()
5085  {
5086  VMA_HEAVY_ASSERT(m_Count > 0);
5087  return m_pArray[m_Count - 1];
5088  }
5089  const T& back() const
5090  {
5091  VMA_HEAVY_ASSERT(m_Count > 0);
5092  return m_pArray[m_Count - 1];
5093  }
5094 
5095  void reserve(size_t newCapacity, bool freeMemory = false)
5096  {
5097  newCapacity = VMA_MAX(newCapacity, m_Count);
5098 
5099  if((newCapacity < m_Capacity) && !freeMemory)
5100  {
5101  newCapacity = m_Capacity;
5102  }
5103 
5104  if(newCapacity != m_Capacity)
5105  {
5106  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
5107  if(m_Count != 0)
5108  {
5109  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5110  }
5111  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5112  m_Capacity = newCapacity;
5113  m_pArray = newArray;
5114  }
5115  }
5116 
5117  void resize(size_t newCount)
5118  {
5119  size_t newCapacity = m_Capacity;
5120  if(newCount > m_Capacity)
5121  {
5122  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
5123  }
5124 
5125  if(newCapacity != m_Capacity)
5126  {
5127  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
5128  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
5129  if(elementsToCopy != 0)
5130  {
5131  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
5132  }
5133  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5134  m_Capacity = newCapacity;
5135  m_pArray = newArray;
5136  }
5137 
5138  m_Count = newCount;
5139  }
5140 
5141  void clear()
5142  {
5143  resize(0);
5144  }
5145 
5146  void shrink_to_fit()
5147  {
5148  if(m_Capacity > m_Count)
5149  {
5150  T* newArray = VMA_NULL;
5151  if(m_Count > 0)
5152  {
5153  newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
5154  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5155  }
5156  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5157  m_Capacity = m_Count;
5158  m_pArray = newArray;
5159  }
5160  }
5161 
5162  void insert(size_t index, const T& src)
5163  {
5164  VMA_HEAVY_ASSERT(index <= m_Count);
5165  const size_t oldCount = size();
5166  resize(oldCount + 1);
5167  if(index < oldCount)
5168  {
5169  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
5170  }
5171  m_pArray[index] = src;
5172  }
5173 
5174  void remove(size_t index)
5175  {
5176  VMA_HEAVY_ASSERT(index < m_Count);
5177  const size_t oldCount = size();
5178  if(index < oldCount - 1)
5179  {
5180  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
5181  }
5182  resize(oldCount - 1);
5183  }
5184 
5185  void push_back(const T& src)
5186  {
5187  const size_t newIndex = size();
5188  resize(newIndex + 1);
5189  m_pArray[newIndex] = src;
5190  }
5191 
5192  void pop_back()
5193  {
5194  VMA_HEAVY_ASSERT(m_Count > 0);
5195  resize(size() - 1);
5196  }
5197 
5198  void push_front(const T& src)
5199  {
5200  insert(0, src);
5201  }
5202 
5203  void pop_front()
5204  {
5205  VMA_HEAVY_ASSERT(m_Count > 0);
5206  remove(0);
5207  }
5208 
5209  typedef T* iterator;
5210 
5211  iterator begin() { return m_pArray; }
5212  iterator end() { return m_pArray + m_Count; }
5213 
5214 private:
5215  AllocatorT m_Allocator;
5216  T* m_pArray;
5217  size_t m_Count;
5218  size_t m_Capacity;
5219 };
5220 
5221 template<typename T, typename allocatorT>
5222 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5223 {
5224  vec.insert(index, item);
5225 }
5226 
5227 template<typename T, typename allocatorT>
5228 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5229 {
5230  vec.remove(index);
5231 }
5232 
5233 #endif // #if VMA_USE_STL_VECTOR
5234 
5235 template<typename CmpLess, typename VectorT>
5236 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5237 {
5238  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5239  vector.data(),
5240  vector.data() + vector.size(),
5241  value,
5242  CmpLess()) - vector.data();
5243  VmaVectorInsert(vector, indexToInsert, value);
5244  return indexToInsert;
5245 }
5246 
5247 template<typename CmpLess, typename VectorT>
5248 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5249 {
5250  CmpLess comparator;
5251  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5252  vector.begin(),
5253  vector.end(),
5254  value,
5255  comparator);
5256  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5257  {
5258  size_t indexToRemove = it - vector.begin();
5259  VmaVectorRemove(vector, indexToRemove);
5260  return true;
5261  }
5262  return false;
5263 }
5264 
5266 // class VmaSmallVector
5267 
5268 /*
5269 This is a vector (a variable-sized array), optimized for the case when the array is small.
5270 
5271 It contains some number of elements in-place, which allows it to avoid heap allocation
5272 when the actual number of elements is below that threshold. This allows normal "small"
5273 cases to be fast without losing generality for large inputs.
5274 */
5275 
5276 template<typename T, typename AllocatorT, size_t N>
5277 class VmaSmallVector
5278 {
5279 public:
5280  typedef T value_type;
5281 
5282  VmaSmallVector(const AllocatorT& allocator) :
5283  m_Count(0),
5284  m_DynamicArray(allocator)
5285  {
5286  }
5287  VmaSmallVector(size_t count, const AllocatorT& allocator) :
5288  m_Count(count),
5289  m_DynamicArray(count > N ? count : 0, allocator)
5290  {
5291  }
5292  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5293  VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5294  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5295  VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5296 
5297  bool empty() const { return m_Count == 0; }
5298  size_t size() const { return m_Count; }
5299  T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5300  const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5301 
5302  T& operator[](size_t index)
5303  {
5304  VMA_HEAVY_ASSERT(index < m_Count);
5305  return data()[index];
5306  }
5307  const T& operator[](size_t index) const
5308  {
5309  VMA_HEAVY_ASSERT(index < m_Count);
5310  return data()[index];
5311  }
5312 
5313  T& front()
5314  {
5315  VMA_HEAVY_ASSERT(m_Count > 0);
5316  return data()[0];
5317  }
5318  const T& front() const
5319  {
5320  VMA_HEAVY_ASSERT(m_Count > 0);
5321  return data()[0];
5322  }
5323  T& back()
5324  {
5325  VMA_HEAVY_ASSERT(m_Count > 0);
5326  return data()[m_Count - 1];
5327  }
5328  const T& back() const
5329  {
5330  VMA_HEAVY_ASSERT(m_Count > 0);
5331  return data()[m_Count - 1];
5332  }
5333 
5334  void resize(size_t newCount, bool freeMemory = false)
5335  {
5336  if(newCount > N && m_Count > N)
5337  {
5338  // Any direction, staying in m_DynamicArray
5339  m_DynamicArray.resize(newCount);
5340  if(freeMemory)
5341  {
5342  m_DynamicArray.shrink_to_fit();
5343  }
5344  }
5345  else if(newCount > N && m_Count <= N)
5346  {
5347  // Growing, moving from m_StaticArray to m_DynamicArray
5348  m_DynamicArray.resize(newCount);
5349  if(m_Count > 0)
5350  {
5351  memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5352  }
5353  }
5354  else if(newCount <= N && m_Count > N)
5355  {
5356  // Shrinking, moving from m_DynamicArray to m_StaticArray
5357  if(newCount > 0)
5358  {
5359  memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5360  }
5361  m_DynamicArray.resize(0);
5362  if(freeMemory)
5363  {
5364  m_DynamicArray.shrink_to_fit();
5365  }
5366  }
5367  else
5368  {
5369  // Any direction, staying in m_StaticArray - nothing to do here
5370  }
5371  m_Count = newCount;
5372  }
5373 
5374  void clear(bool freeMemory = false)
5375  {
5376  m_DynamicArray.clear();
5377  if(freeMemory)
5378  {
5379  m_DynamicArray.shrink_to_fit();
5380  }
5381  m_Count = 0;
5382  }
5383 
5384  void insert(size_t index, const T& src)
5385  {
5386  VMA_HEAVY_ASSERT(index <= m_Count);
5387  const size_t oldCount = size();
5388  resize(oldCount + 1);
5389  T* const dataPtr = data();
5390  if(index < oldCount)
5391  {
5392  // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5393  memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5394  }
5395  dataPtr[index] = src;
5396  }
5397 
5398  void remove(size_t index)
5399  {
5400  VMA_HEAVY_ASSERT(index < m_Count);
5401  const size_t oldCount = size();
5402  if(index < oldCount - 1)
5403  {
5404  // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5405  T* const dataPtr = data();
5406  memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5407  }
5408  resize(oldCount - 1);
5409  }
5410 
5411  void push_back(const T& src)
5412  {
5413  const size_t newIndex = size();
5414  resize(newIndex + 1);
5415  data()[newIndex] = src;
5416  }
5417 
5418  void pop_back()
5419  {
5420  VMA_HEAVY_ASSERT(m_Count > 0);
5421  resize(size() - 1);
5422  }
5423 
5424  void push_front(const T& src)
5425  {
5426  insert(0, src);
5427  }
5428 
5429  void pop_front()
5430  {
5431  VMA_HEAVY_ASSERT(m_Count > 0);
5432  remove(0);
5433  }
5434 
5435  typedef T* iterator;
5436 
5437  iterator begin() { return data(); }
5438  iterator end() { return data() + m_Count; }
5439 
5440 private:
5441  size_t m_Count;
5442  T m_StaticArray[N]; // Used when m_Size <= N
5443  VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5444 };
5445 
5447 // class VmaPoolAllocator
5448 
5449 /*
5450 Allocator for objects of type T using a list of arrays (pools) to speed up
5451 allocation. Number of elements that can be allocated is not bounded because
5452 allocator can create multiple blocks.
5453 */
5454 template<typename T>
5455 class VmaPoolAllocator
5456 {
5457  VMA_CLASS_NO_COPY(VmaPoolAllocator)
5458 public:
5459  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5460  ~VmaPoolAllocator();
5461  template<typename... Types> T* Alloc(Types... args);
5462  void Free(T* ptr);
5463 
5464 private:
5465  union Item
5466  {
5467  uint32_t NextFreeIndex;
5468  alignas(T) char Value[sizeof(T)];
5469  };
5470 
5471  struct ItemBlock
5472  {
5473  Item* pItems;
5474  uint32_t Capacity;
5475  uint32_t FirstFreeIndex;
5476  };
5477 
5478  const VkAllocationCallbacks* m_pAllocationCallbacks;
5479  const uint32_t m_FirstBlockCapacity;
5480  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5481 
5482  ItemBlock& CreateNewBlock();
5483 };
5484 
5485 template<typename T>
5486 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5487  m_pAllocationCallbacks(pAllocationCallbacks),
5488  m_FirstBlockCapacity(firstBlockCapacity),
5489  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5490 {
5491  VMA_ASSERT(m_FirstBlockCapacity > 1);
5492 }
5493 
5494 template<typename T>
5495 VmaPoolAllocator<T>::~VmaPoolAllocator()
5496 {
5497  for(size_t i = m_ItemBlocks.size(); i--; )
5498  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5499  m_ItemBlocks.clear();
5500 }
5501 
5502 template<typename T>
5503 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5504 {
5505  for(size_t i = m_ItemBlocks.size(); i--; )
5506  {
5507  ItemBlock& block = m_ItemBlocks[i];
5508  // This block has some free items: Use first one.
5509  if(block.FirstFreeIndex != UINT32_MAX)
5510  {
5511  Item* const pItem = &block.pItems[block.FirstFreeIndex];
5512  block.FirstFreeIndex = pItem->NextFreeIndex;
5513  T* result = (T*)&pItem->Value;
5514  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5515  return result;
5516  }
5517  }
5518 
5519  // No block has free item: Create new one and use it.
5520  ItemBlock& newBlock = CreateNewBlock();
5521  Item* const pItem = &newBlock.pItems[0];
5522  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5523  T* result = (T*)&pItem->Value;
5524  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5525  return result;
5526 }
5527 
5528 template<typename T>
5529 void VmaPoolAllocator<T>::Free(T* ptr)
5530 {
5531  // Search all memory blocks to find ptr.
5532  for(size_t i = m_ItemBlocks.size(); i--; )
5533  {
5534  ItemBlock& block = m_ItemBlocks[i];
5535 
5536  // Casting to union.
5537  Item* pItemPtr;
5538  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5539 
5540  // Check if pItemPtr is in address range of this block.
5541  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5542  {
5543  ptr->~T(); // Explicit destructor call.
5544  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5545  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5546  block.FirstFreeIndex = index;
5547  return;
5548  }
5549  }
5550  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5551 }
5552 
5553 template<typename T>
5554 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5555 {
5556  const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5557  m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5558 
5559  const ItemBlock newBlock = {
5560  vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5561  newBlockCapacity,
5562  0 };
5563 
5564  m_ItemBlocks.push_back(newBlock);
5565 
5566  // Setup singly-linked list of all free items in this block.
5567  for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5568  newBlock.pItems[i].NextFreeIndex = i + 1;
5569  newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5570  return m_ItemBlocks.back();
5571 }
5572 
5574 // class VmaRawList, VmaList
5575 
5576 #if VMA_USE_STL_LIST
5577 
5578 #define VmaList std::list
5579 
5580 #else // #if VMA_USE_STL_LIST
5581 
5582 template<typename T>
5583 struct VmaListItem
5584 {
5585  VmaListItem* pPrev;
5586  VmaListItem* pNext;
5587  T Value;
5588 };
5589 
5590 // Doubly linked list.
5591 template<typename T>
5592 class VmaRawList
5593 {
5594  VMA_CLASS_NO_COPY(VmaRawList)
5595 public:
5596  typedef VmaListItem<T> ItemType;
5597 
5598  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5599  ~VmaRawList();
5600  void Clear();
5601 
5602  size_t GetCount() const { return m_Count; }
5603  bool IsEmpty() const { return m_Count == 0; }
5604 
5605  ItemType* Front() { return m_pFront; }
5606  const ItemType* Front() const { return m_pFront; }
5607  ItemType* Back() { return m_pBack; }
5608  const ItemType* Back() const { return m_pBack; }
5609 
5610  ItemType* PushBack();
5611  ItemType* PushFront();
5612  ItemType* PushBack(const T& value);
5613  ItemType* PushFront(const T& value);
5614  void PopBack();
5615  void PopFront();
5616 
5617  // Item can be null - it means PushBack.
5618  ItemType* InsertBefore(ItemType* pItem);
5619  // Item can be null - it means PushFront.
5620  ItemType* InsertAfter(ItemType* pItem);
5621 
5622  ItemType* InsertBefore(ItemType* pItem, const T& value);
5623  ItemType* InsertAfter(ItemType* pItem, const T& value);
5624 
5625  void Remove(ItemType* pItem);
5626 
5627 private:
5628  const VkAllocationCallbacks* const m_pAllocationCallbacks;
5629  VmaPoolAllocator<ItemType> m_ItemAllocator;
5630  ItemType* m_pFront;
5631  ItemType* m_pBack;
5632  size_t m_Count;
5633 };
5634 
5635 template<typename T>
5636 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5637  m_pAllocationCallbacks(pAllocationCallbacks),
5638  m_ItemAllocator(pAllocationCallbacks, 128),
5639  m_pFront(VMA_NULL),
5640  m_pBack(VMA_NULL),
5641  m_Count(0)
5642 {
5643 }
5644 
5645 template<typename T>
5646 VmaRawList<T>::~VmaRawList()
5647 {
5648  // Intentionally not calling Clear, because that would be unnecessary
5649  // computations to return all items to m_ItemAllocator as free.
5650 }
5651 
5652 template<typename T>
5653 void VmaRawList<T>::Clear()
5654 {
5655  if(IsEmpty() == false)
5656  {
5657  ItemType* pItem = m_pBack;
5658  while(pItem != VMA_NULL)
5659  {
5660  ItemType* const pPrevItem = pItem->pPrev;
5661  m_ItemAllocator.Free(pItem);
5662  pItem = pPrevItem;
5663  }
5664  m_pFront = VMA_NULL;
5665  m_pBack = VMA_NULL;
5666  m_Count = 0;
5667  }
5668 }
5669 
5670 template<typename T>
5671 VmaListItem<T>* VmaRawList<T>::PushBack()
5672 {
5673  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5674  pNewItem->pNext = VMA_NULL;
5675  if(IsEmpty())
5676  {
5677  pNewItem->pPrev = VMA_NULL;
5678  m_pFront = pNewItem;
5679  m_pBack = pNewItem;
5680  m_Count = 1;
5681  }
5682  else
5683  {
5684  pNewItem->pPrev = m_pBack;
5685  m_pBack->pNext = pNewItem;
5686  m_pBack = pNewItem;
5687  ++m_Count;
5688  }
5689  return pNewItem;
5690 }
5691 
5692 template<typename T>
5693 VmaListItem<T>* VmaRawList<T>::PushFront()
5694 {
5695  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5696  pNewItem->pPrev = VMA_NULL;
5697  if(IsEmpty())
5698  {
5699  pNewItem->pNext = VMA_NULL;
5700  m_pFront = pNewItem;
5701  m_pBack = pNewItem;
5702  m_Count = 1;
5703  }
5704  else
5705  {
5706  pNewItem->pNext = m_pFront;
5707  m_pFront->pPrev = pNewItem;
5708  m_pFront = pNewItem;
5709  ++m_Count;
5710  }
5711  return pNewItem;
5712 }
5713 
5714 template<typename T>
5715 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5716 {
5717  ItemType* const pNewItem = PushBack();
5718  pNewItem->Value = value;
5719  return pNewItem;
5720 }
5721 
5722 template<typename T>
5723 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5724 {
5725  ItemType* const pNewItem = PushFront();
5726  pNewItem->Value = value;
5727  return pNewItem;
5728 }
5729 
5730 template<typename T>
5731 void VmaRawList<T>::PopBack()
5732 {
5733  VMA_HEAVY_ASSERT(m_Count > 0);
5734  ItemType* const pBackItem = m_pBack;
5735  ItemType* const pPrevItem = pBackItem->pPrev;
5736  if(pPrevItem != VMA_NULL)
5737  {
5738  pPrevItem->pNext = VMA_NULL;
5739  }
5740  m_pBack = pPrevItem;
5741  m_ItemAllocator.Free(pBackItem);
5742  --m_Count;
5743 }
5744 
5745 template<typename T>
5746 void VmaRawList<T>::PopFront()
5747 {
5748  VMA_HEAVY_ASSERT(m_Count > 0);
5749  ItemType* const pFrontItem = m_pFront;
5750  ItemType* const pNextItem = pFrontItem->pNext;
5751  if(pNextItem != VMA_NULL)
5752  {
5753  pNextItem->pPrev = VMA_NULL;
5754  }
5755  m_pFront = pNextItem;
5756  m_ItemAllocator.Free(pFrontItem);
5757  --m_Count;
5758 }
5759 
5760 template<typename T>
5761 void VmaRawList<T>::Remove(ItemType* pItem)
5762 {
5763  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5764  VMA_HEAVY_ASSERT(m_Count > 0);
5765 
5766  if(pItem->pPrev != VMA_NULL)
5767  {
5768  pItem->pPrev->pNext = pItem->pNext;
5769  }
5770  else
5771  {
5772  VMA_HEAVY_ASSERT(m_pFront == pItem);
5773  m_pFront = pItem->pNext;
5774  }
5775 
5776  if(pItem->pNext != VMA_NULL)
5777  {
5778  pItem->pNext->pPrev = pItem->pPrev;
5779  }
5780  else
5781  {
5782  VMA_HEAVY_ASSERT(m_pBack == pItem);
5783  m_pBack = pItem->pPrev;
5784  }
5785 
5786  m_ItemAllocator.Free(pItem);
5787  --m_Count;
5788 }
5789 
5790 template<typename T>
5791 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5792 {
5793  if(pItem != VMA_NULL)
5794  {
5795  ItemType* const prevItem = pItem->pPrev;
5796  ItemType* const newItem = m_ItemAllocator.Alloc();
5797  newItem->pPrev = prevItem;
5798  newItem->pNext = pItem;
5799  pItem->pPrev = newItem;
5800  if(prevItem != VMA_NULL)
5801  {
5802  prevItem->pNext = newItem;
5803  }
5804  else
5805  {
5806  VMA_HEAVY_ASSERT(m_pFront == pItem);
5807  m_pFront = newItem;
5808  }
5809  ++m_Count;
5810  return newItem;
5811  }
5812  else
5813  return PushBack();
5814 }
5815 
5816 template<typename T>
5817 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5818 {
5819  if(pItem != VMA_NULL)
5820  {
5821  ItemType* const nextItem = pItem->pNext;
5822  ItemType* const newItem = m_ItemAllocator.Alloc();
5823  newItem->pNext = nextItem;
5824  newItem->pPrev = pItem;
5825  pItem->pNext = newItem;
5826  if(nextItem != VMA_NULL)
5827  {
5828  nextItem->pPrev = newItem;
5829  }
5830  else
5831  {
5832  VMA_HEAVY_ASSERT(m_pBack == pItem);
5833  m_pBack = newItem;
5834  }
5835  ++m_Count;
5836  return newItem;
5837  }
5838  else
5839  return PushFront();
5840 }
5841 
5842 template<typename T>
5843 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5844 {
5845  ItemType* const newItem = InsertBefore(pItem);
5846  newItem->Value = value;
5847  return newItem;
5848 }
5849 
5850 template<typename T>
5851 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5852 {
5853  ItemType* const newItem = InsertAfter(pItem);
5854  newItem->Value = value;
5855  return newItem;
5856 }
5857 
5858 template<typename T, typename AllocatorT>
5859 class VmaList
5860 {
5861  VMA_CLASS_NO_COPY(VmaList)
5862 public:
5863  class iterator
5864  {
5865  public:
5866  iterator() :
5867  m_pList(VMA_NULL),
5868  m_pItem(VMA_NULL)
5869  {
5870  }
5871 
5872  T& operator*() const
5873  {
5874  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5875  return m_pItem->Value;
5876  }
5877  T* operator->() const
5878  {
5879  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5880  return &m_pItem->Value;
5881  }
5882 
5883  iterator& operator++()
5884  {
5885  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5886  m_pItem = m_pItem->pNext;
5887  return *this;
5888  }
5889  iterator& operator--()
5890  {
5891  if(m_pItem != VMA_NULL)
5892  {
5893  m_pItem = m_pItem->pPrev;
5894  }
5895  else
5896  {
5897  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5898  m_pItem = m_pList->Back();
5899  }
5900  return *this;
5901  }
5902 
5903  iterator operator++(int)
5904  {
5905  iterator result = *this;
5906  ++*this;
5907  return result;
5908  }
5909  iterator operator--(int)
5910  {
5911  iterator result = *this;
5912  --*this;
5913  return result;
5914  }
5915 
5916  bool operator==(const iterator& rhs) const
5917  {
5918  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5919  return m_pItem == rhs.m_pItem;
5920  }
5921  bool operator!=(const iterator& rhs) const
5922  {
5923  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5924  return m_pItem != rhs.m_pItem;
5925  }
5926 
5927  private:
5928  VmaRawList<T>* m_pList;
5929  VmaListItem<T>* m_pItem;
5930 
5931  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5932  m_pList(pList),
5933  m_pItem(pItem)
5934  {
5935  }
5936 
5937  friend class VmaList<T, AllocatorT>;
5938  };
5939 
5940  class const_iterator
5941  {
5942  public:
5943  const_iterator() :
5944  m_pList(VMA_NULL),
5945  m_pItem(VMA_NULL)
5946  {
5947  }
5948 
5949  const_iterator(const iterator& src) :
5950  m_pList(src.m_pList),
5951  m_pItem(src.m_pItem)
5952  {
5953  }
5954 
5955  const T& operator*() const
5956  {
5957  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5958  return m_pItem->Value;
5959  }
5960  const T* operator->() const
5961  {
5962  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5963  return &m_pItem->Value;
5964  }
5965 
5966  const_iterator& operator++()
5967  {
5968  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5969  m_pItem = m_pItem->pNext;
5970  return *this;
5971  }
5972  const_iterator& operator--()
5973  {
5974  if(m_pItem != VMA_NULL)
5975  {
5976  m_pItem = m_pItem->pPrev;
5977  }
5978  else
5979  {
5980  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5981  m_pItem = m_pList->Back();
5982  }
5983  return *this;
5984  }
5985 
5986  const_iterator operator++(int)
5987  {
5988  const_iterator result = *this;
5989  ++*this;
5990  return result;
5991  }
5992  const_iterator operator--(int)
5993  {
5994  const_iterator result = *this;
5995  --*this;
5996  return result;
5997  }
5998 
5999  bool operator==(const const_iterator& rhs) const
6000  {
6001  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
6002  return m_pItem == rhs.m_pItem;
6003  }
6004  bool operator!=(const const_iterator& rhs) const
6005  {
6006  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
6007  return m_pItem != rhs.m_pItem;
6008  }
6009 
6010  private:
6011  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
6012  m_pList(pList),
6013  m_pItem(pItem)
6014  {
6015  }
6016 
6017  const VmaRawList<T>* m_pList;
6018  const VmaListItem<T>* m_pItem;
6019 
6020  friend class VmaList<T, AllocatorT>;
6021  };
6022 
6023  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
6024 
6025  bool empty() const { return m_RawList.IsEmpty(); }
6026  size_t size() const { return m_RawList.GetCount(); }
6027 
6028  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
6029  iterator end() { return iterator(&m_RawList, VMA_NULL); }
6030 
6031  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
6032  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
6033 
6034  void clear() { m_RawList.Clear(); }
6035  void push_back(const T& value) { m_RawList.PushBack(value); }
6036  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
6037  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
6038 
6039 private:
6040  VmaRawList<T> m_RawList;
6041 };
6042 
6043 #endif // #if VMA_USE_STL_LIST
6044 
6046 // class VmaIntrusiveLinkedList
6047 
6048 /*
6049 Expected interface of ItemTypeTraits:
6050 struct MyItemTypeTraits
6051 {
6052  typedef MyItem ItemType;
6053  static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
6054  static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
6055  static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
6056  static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
6057 };
6058 */
6059 template<typename ItemTypeTraits>
6060 class VmaIntrusiveLinkedList
6061 {
6062 public:
6063  typedef typename ItemTypeTraits::ItemType ItemType;
6064  static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
6065  static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
6066  // Movable, not copyable.
6067  VmaIntrusiveLinkedList() { }
6068  VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6069  VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) :
6070  m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
6071  {
6072  src.m_Front = src.m_Back = VMA_NULL;
6073  src.m_Count = 0;
6074  }
6075  ~VmaIntrusiveLinkedList()
6076  {
6077  VMA_HEAVY_ASSERT(IsEmpty());
6078  }
6079  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6080  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src)
6081  {
6082  if(&src != this)
6083  {
6084  VMA_HEAVY_ASSERT(IsEmpty());
6085  m_Front = src.m_Front;
6086  m_Back = src.m_Back;
6087  m_Count = src.m_Count;
6088  src.m_Front = src.m_Back = VMA_NULL;
6089  src.m_Count = 0;
6090  }
6091  return *this;
6092  }
6093  void RemoveAll()
6094  {
6095  if(!IsEmpty())
6096  {
6097  ItemType* item = m_Back;
6098  while(item != VMA_NULL)
6099  {
6100  ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
6101  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6102  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6103  item = prevItem;
6104  }
6105  m_Front = VMA_NULL;
6106  m_Back = VMA_NULL;
6107  m_Count = 0;
6108  }
6109  }
6110  size_t GetCount() const { return m_Count; }
6111  bool IsEmpty() const { return m_Count == 0; }
6112  ItemType* Front() { return m_Front; }
6113  const ItemType* Front() const { return m_Front; }
6114  ItemType* Back() { return m_Back; }
6115  const ItemType* Back() const { return m_Back; }
6116  void PushBack(ItemType* item)
6117  {
6118  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6119  if(IsEmpty())
6120  {
6121  m_Front = item;
6122  m_Back = item;
6123  m_Count = 1;
6124  }
6125  else
6126  {
6127  ItemTypeTraits::AccessPrev(item) = m_Back;
6128  ItemTypeTraits::AccessNext(m_Back) = item;
6129  m_Back = item;
6130  ++m_Count;
6131  }
6132  }
6133  void PushFront(ItemType* item)
6134  {
6135  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6136  if(IsEmpty())
6137  {
6138  m_Front = item;
6139  m_Back = item;
6140  m_Count = 1;
6141  }
6142  else
6143  {
6144  ItemTypeTraits::AccessNext(item) = m_Front;
6145  ItemTypeTraits::AccessPrev(m_Front) = item;
6146  m_Front = item;
6147  ++m_Count;
6148  }
6149  }
6150  ItemType* PopBack()
6151  {
6152  VMA_HEAVY_ASSERT(m_Count > 0);
6153  ItemType* const backItem = m_Back;
6154  ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
6155  if(prevItem != VMA_NULL)
6156  {
6157  ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
6158  }
6159  m_Back = prevItem;
6160  --m_Count;
6161  ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
6162  ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
6163  return backItem;
6164  }
6165  ItemType* PopFront()
6166  {
6167  VMA_HEAVY_ASSERT(m_Count > 0);
6168  ItemType* const frontItem = m_Front;
6169  ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
6170  if(nextItem != VMA_NULL)
6171  {
6172  ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
6173  }
6174  m_Front = nextItem;
6175  --m_Count;
6176  ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
6177  ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
6178  return frontItem;
6179  }
6180 
6181  // MyItem can be null - it means PushBack.
6182  void InsertBefore(ItemType* existingItem, ItemType* newItem)
6183  {
6184  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6185  if(existingItem != VMA_NULL)
6186  {
6187  ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
6188  ItemTypeTraits::AccessPrev(newItem) = prevItem;
6189  ItemTypeTraits::AccessNext(newItem) = existingItem;
6190  ItemTypeTraits::AccessPrev(existingItem) = newItem;
6191  if(prevItem != VMA_NULL)
6192  {
6193  ItemTypeTraits::AccessNext(prevItem) = newItem;
6194  }
6195  else
6196  {
6197  VMA_HEAVY_ASSERT(m_Front == existingItem);
6198  m_Front = newItem;
6199  }
6200  ++m_Count;
6201  }
6202  else
6203  PushBack(newItem);
6204  }
6205  // MyItem can be null - it means PushFront.
6206  void InsertAfter(ItemType* existingItem, ItemType* newItem)
6207  {
6208  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6209  if(existingItem != VMA_NULL)
6210  {
6211  ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
6212  ItemTypeTraits::AccessNext(newItem) = nextItem;
6213  ItemTypeTraits::AccessPrev(newItem) = existingItem;
6214  ItemTypeTraits::AccessNext(existingItem) = newItem;
6215  if(nextItem != VMA_NULL)
6216  {
6217  ItemTypeTraits::AccessPrev(nextItem) = newItem;
6218  }
6219  else
6220  {
6221  VMA_HEAVY_ASSERT(m_Back == existingItem);
6222  m_Back = newItem;
6223  }
6224  ++m_Count;
6225  }
6226  else
6227  return PushFront(newItem);
6228  }
6229  void Remove(ItemType* item)
6230  {
6231  VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
6232  if(ItemTypeTraits::GetPrev(item) != VMA_NULL)
6233  {
6234  ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
6235  }
6236  else
6237  {
6238  VMA_HEAVY_ASSERT(m_Front == item);
6239  m_Front = ItemTypeTraits::GetNext(item);
6240  }
6241 
6242  if(ItemTypeTraits::GetNext(item) != VMA_NULL)
6243  {
6244  ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
6245  }
6246  else
6247  {
6248  VMA_HEAVY_ASSERT(m_Back == item);
6249  m_Back = ItemTypeTraits::GetPrev(item);
6250  }
6251  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6252  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6253  --m_Count;
6254  }
6255 private:
6256  ItemType* m_Front = VMA_NULL;
6257  ItemType* m_Back = VMA_NULL;
6258  size_t m_Count = 0;
6259 };
6260 
6262 // class VmaMap
6263 
6264 // Unused in this version.
6265 #if 0
6266 
6267 #if VMA_USE_STL_UNORDERED_MAP
6268 
6269 #define VmaPair std::pair
6270 
6271 #define VMA_MAP_TYPE(KeyT, ValueT) \
6272  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
6273 
6274 #else // #if VMA_USE_STL_UNORDERED_MAP
6275 
6276 template<typename T1, typename T2>
6277 struct VmaPair
6278 {
6279  T1 first;
6280  T2 second;
6281 
6282  VmaPair() : first(), second() { }
6283  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
6284 };
6285 
6286 /* Class compatible with subset of interface of std::unordered_map.
6287 KeyT, ValueT must be POD because they will be stored in VmaVector.
6288 */
6289 template<typename KeyT, typename ValueT>
6290 class VmaMap
6291 {
6292 public:
6293  typedef VmaPair<KeyT, ValueT> PairType;
6294  typedef PairType* iterator;
6295 
6296  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
6297 
6298  iterator begin() { return m_Vector.begin(); }
6299  iterator end() { return m_Vector.end(); }
6300 
6301  void insert(const PairType& pair);
6302  iterator find(const KeyT& key);
6303  void erase(iterator it);
6304 
6305 private:
6306  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
6307 };
6308 
6309 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
6310 
6311 template<typename FirstT, typename SecondT>
6312 struct VmaPairFirstLess
6313 {
6314  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
6315  {
6316  return lhs.first < rhs.first;
6317  }
6318  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
6319  {
6320  return lhs.first < rhsFirst;
6321  }
6322 };
6323 
6324 template<typename KeyT, typename ValueT>
6325 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
6326 {
6327  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
6328  m_Vector.data(),
6329  m_Vector.data() + m_Vector.size(),
6330  pair,
6331  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
6332  VmaVectorInsert(m_Vector, indexToInsert, pair);
6333 }
6334 
6335 template<typename KeyT, typename ValueT>
6336 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
6337 {
6338  PairType* it = VmaBinaryFindFirstNotLess(
6339  m_Vector.data(),
6340  m_Vector.data() + m_Vector.size(),
6341  key,
6342  VmaPairFirstLess<KeyT, ValueT>());
6343  if((it != m_Vector.end()) && (it->first == key))
6344  {
6345  return it;
6346  }
6347  else
6348  {
6349  return m_Vector.end();
6350  }
6351 }
6352 
6353 template<typename KeyT, typename ValueT>
6354 void VmaMap<KeyT, ValueT>::erase(iterator it)
6355 {
6356  VmaVectorRemove(m_Vector, it - m_Vector.begin());
6357 }
6358 
6359 #endif // #if VMA_USE_STL_UNORDERED_MAP
6360 
6361 #endif // #if 0
6362 
6364 
6365 class VmaDeviceMemoryBlock;
6366 
6367 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
6368 
6369 struct VmaAllocation_T
6370 {
6371 private:
6372  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
6373 
6374  enum FLAGS
6375  {
6376  FLAG_USER_DATA_STRING = 0x01,
6377  };
6378 
6379 public:
6380  enum ALLOCATION_TYPE
6381  {
6382  ALLOCATION_TYPE_NONE,
6383  ALLOCATION_TYPE_BLOCK,
6384  ALLOCATION_TYPE_DEDICATED,
6385  };
6386 
6387  /*
6388  This struct is allocated using VmaPoolAllocator.
6389  */
6390 
6391  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
6392  m_Alignment{1},
6393  m_Size{0},
6394  m_pUserData{VMA_NULL},
6395  m_LastUseFrameIndex{currentFrameIndex},
6396  m_MemoryTypeIndex{0},
6397  m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
6398  m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
6399  m_MapCount{0},
6400  m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
6401  {
6402 #if VMA_STATS_STRING_ENABLED
6403  m_CreationFrameIndex = currentFrameIndex;
6404  m_BufferImageUsage = 0;
6405 #endif
6406  }
6407 
6408  ~VmaAllocation_T()
6409  {
6410  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
6411 
6412  // Check if owned string was freed.
6413  VMA_ASSERT(m_pUserData == VMA_NULL);
6414  }
6415 
6416  void InitBlockAllocation(
6417  VmaDeviceMemoryBlock* block,
6418  VkDeviceSize offset,
6419  VkDeviceSize alignment,
6420  VkDeviceSize size,
6421  uint32_t memoryTypeIndex,
6422  VmaSuballocationType suballocationType,
6423  bool mapped,
6424  bool canBecomeLost)
6425  {
6426  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6427  VMA_ASSERT(block != VMA_NULL);
6428  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6429  m_Alignment = alignment;
6430  m_Size = size;
6431  m_MemoryTypeIndex = memoryTypeIndex;
6432  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6433  m_SuballocationType = (uint8_t)suballocationType;
6434  m_BlockAllocation.m_Block = block;
6435  m_BlockAllocation.m_Offset = offset;
6436  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6437  }
6438 
6439  void InitLost()
6440  {
6441  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6442  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6443  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6444  m_MemoryTypeIndex = 0;
6445  m_BlockAllocation.m_Block = VMA_NULL;
6446  m_BlockAllocation.m_Offset = 0;
6447  m_BlockAllocation.m_CanBecomeLost = true;
6448  }
6449 
6450  void ChangeBlockAllocation(
6451  VmaAllocator hAllocator,
6452  VmaDeviceMemoryBlock* block,
6453  VkDeviceSize offset);
6454 
6455  void ChangeOffset(VkDeviceSize newOffset);
6456 
6457  // pMappedData not null means allocation is created with MAPPED flag.
6458  void InitDedicatedAllocation(
6459  uint32_t memoryTypeIndex,
6460  VkDeviceMemory hMemory,
6461  VmaSuballocationType suballocationType,
6462  void* pMappedData,
6463  VkDeviceSize size)
6464  {
6465  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6466  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6467  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6468  m_Alignment = 0;
6469  m_Size = size;
6470  m_MemoryTypeIndex = memoryTypeIndex;
6471  m_SuballocationType = (uint8_t)suballocationType;
6472  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6473  m_DedicatedAllocation.m_hMemory = hMemory;
6474  m_DedicatedAllocation.m_pMappedData = pMappedData;
6475  m_DedicatedAllocation.m_Prev = VMA_NULL;
6476  m_DedicatedAllocation.m_Next = VMA_NULL;
6477  }
6478 
6479  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6480  VkDeviceSize GetAlignment() const { return m_Alignment; }
6481  VkDeviceSize GetSize() const { return m_Size; }
6482  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6483  void* GetUserData() const { return m_pUserData; }
6484  void SetUserData(VmaAllocator hAllocator, void* pUserData);
6485  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6486 
6487  VmaDeviceMemoryBlock* GetBlock() const
6488  {
6489  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6490  return m_BlockAllocation.m_Block;
6491  }
6492  VkDeviceSize GetOffset() const;
6493  VkDeviceMemory GetMemory() const;
6494  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6495  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6496  void* GetMappedData() const;
6497  bool CanBecomeLost() const;
6498 
6499  uint32_t GetLastUseFrameIndex() const
6500  {
6501  return m_LastUseFrameIndex.load();
6502  }
6503  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6504  {
6505  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6506  }
6507  /*
6508  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6509  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6510  - Else, returns false.
6511 
6512  If hAllocation is already lost, assert - you should not call it then.
6513  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6514  */
6515  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6516 
6517  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6518  {
6519  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6520  outInfo.blockCount = 1;
6521  outInfo.allocationCount = 1;
6522  outInfo.unusedRangeCount = 0;
6523  outInfo.usedBytes = m_Size;
6524  outInfo.unusedBytes = 0;
6525  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6526  outInfo.unusedRangeSizeMin = UINT64_MAX;
6527  outInfo.unusedRangeSizeMax = 0;
6528  }
6529 
6530  void BlockAllocMap();
6531  void BlockAllocUnmap();
6532  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6533  void DedicatedAllocUnmap(VmaAllocator hAllocator);
6534 
6535 #if VMA_STATS_STRING_ENABLED
6536  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6537  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6538 
6539  void InitBufferImageUsage(uint32_t bufferImageUsage)
6540  {
6541  VMA_ASSERT(m_BufferImageUsage == 0);
6542  m_BufferImageUsage = bufferImageUsage;
6543  }
6544 
6545  void PrintParameters(class VmaJsonWriter& json) const;
6546 #endif
6547 
6548 private:
6549  VkDeviceSize m_Alignment;
6550  VkDeviceSize m_Size;
6551  void* m_pUserData;
6552  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6553  uint32_t m_MemoryTypeIndex;
6554  uint8_t m_Type; // ALLOCATION_TYPE
6555  uint8_t m_SuballocationType; // VmaSuballocationType
6556  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6557  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6558  uint8_t m_MapCount;
6559  uint8_t m_Flags; // enum FLAGS
6560 
6561  // Allocation out of VmaDeviceMemoryBlock.
6562  struct BlockAllocation
6563  {
6564  VmaDeviceMemoryBlock* m_Block;
6565  VkDeviceSize m_Offset;
6566  bool m_CanBecomeLost;
6567  };
6568 
6569  // Allocation for an object that has its own private VkDeviceMemory.
6570  struct DedicatedAllocation
6571  {
6572  VkDeviceMemory m_hMemory;
6573  void* m_pMappedData; // Not null means memory is mapped.
6574  VmaAllocation_T* m_Prev;
6575  VmaAllocation_T* m_Next;
6576  };
6577 
6578  union
6579  {
6580  // Allocation out of VmaDeviceMemoryBlock.
6581  BlockAllocation m_BlockAllocation;
6582  // Allocation for an object that has its own private VkDeviceMemory.
6583  DedicatedAllocation m_DedicatedAllocation;
6584  };
6585 
6586 #if VMA_STATS_STRING_ENABLED
6587  uint32_t m_CreationFrameIndex;
6588  uint32_t m_BufferImageUsage; // 0 if unknown.
6589 #endif
6590 
6591  void FreeUserDataString(VmaAllocator hAllocator);
6592 
6593  friend struct VmaDedicatedAllocationListItemTraits;
6594 };
6595 
6596 struct VmaDedicatedAllocationListItemTraits
6597 {
6598  typedef VmaAllocation_T ItemType;
6599  static ItemType* GetPrev(const ItemType* item)
6600  {
6601  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6602  return item->m_DedicatedAllocation.m_Prev;
6603  }
6604  static ItemType* GetNext(const ItemType* item)
6605  {
6606  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6607  return item->m_DedicatedAllocation.m_Next;
6608  }
6609  static ItemType*& AccessPrev(ItemType* item)
6610  {
6611  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6612  return item->m_DedicatedAllocation.m_Prev;
6613  }
6614  static ItemType*& AccessNext(ItemType* item){
6615  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6616  return item->m_DedicatedAllocation.m_Next;
6617  }
6618 };
6619 
6620 /*
6621 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6622 allocated memory block or free.
6623 */
6624 struct VmaSuballocation
6625 {
6626  VkDeviceSize offset;
6627  VkDeviceSize size;
6628  VmaAllocation hAllocation;
6629  VmaSuballocationType type;
6630 };
6631 
6632 // Comparator for offsets.
6633 struct VmaSuballocationOffsetLess
6634 {
6635  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6636  {
6637  return lhs.offset < rhs.offset;
6638  }
6639 };
6640 struct VmaSuballocationOffsetGreater
6641 {
6642  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6643  {
6644  return lhs.offset > rhs.offset;
6645  }
6646 };
6647 
6648 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6649 
6650 // Cost of one additional allocation lost, as equivalent in bytes.
6651 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6652 
6653 enum class VmaAllocationRequestType
6654 {
6655  Normal,
6656  // Used by "Linear" algorithm.
6657  UpperAddress,
6658  EndOf1st,
6659  EndOf2nd,
6660 };
6661 
6662 /*
6663 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6664 
6665 If canMakeOtherLost was false:
6666 - item points to a FREE suballocation.
6667 - itemsToMakeLostCount is 0.
6668 
6669 If canMakeOtherLost was true:
6670 - item points to first of sequence of suballocations, which are either FREE,
6671  or point to VmaAllocations that can become lost.
6672 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6673  the requested allocation to succeed.
6674 */
6675 struct VmaAllocationRequest
6676 {
6677  VkDeviceSize offset;
6678  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6679  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6680  VmaSuballocationList::iterator item;
6681  size_t itemsToMakeLostCount;
6682  void* customData;
6683  VmaAllocationRequestType type;
6684 
6685  VkDeviceSize CalcCost() const
6686  {
6687  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6688  }
6689 };
6690 
6691 /*
6692 Data structure used for bookkeeping of allocations and unused ranges of memory
6693 in a single VkDeviceMemory block.
6694 */
6695 class VmaBlockMetadata
6696 {
6697 public:
6698  VmaBlockMetadata(VmaAllocator hAllocator);
6699  virtual ~VmaBlockMetadata() { }
6700  virtual void Init(VkDeviceSize size) { m_Size = size; }
6701 
6702  // Validates all data structures inside this object. If not valid, returns false.
6703  virtual bool Validate() const = 0;
6704  VkDeviceSize GetSize() const { return m_Size; }
6705  virtual size_t GetAllocationCount() const = 0;
6706  virtual VkDeviceSize GetSumFreeSize() const = 0;
6707  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6708  // Returns true if this block is empty - contains only single free suballocation.
6709  virtual bool IsEmpty() const = 0;
6710 
6711  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6712  // Shouldn't modify blockCount.
6713  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6714 
6715 #if VMA_STATS_STRING_ENABLED
6716  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6717 #endif
6718 
6719  // Tries to find a place for suballocation with given parameters inside this block.
6720  // If succeeded, fills pAllocationRequest and returns true.
6721  // If failed, returns false.
6722  virtual bool CreateAllocationRequest(
6723  uint32_t currentFrameIndex,
6724  uint32_t frameInUseCount,
6725  VkDeviceSize bufferImageGranularity,
6726  VkDeviceSize allocSize,
6727  VkDeviceSize allocAlignment,
6728  bool upperAddress,
6729  VmaSuballocationType allocType,
6730  bool canMakeOtherLost,
6731  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6732  uint32_t strategy,
6733  VmaAllocationRequest* pAllocationRequest) = 0;
6734 
6735  virtual bool MakeRequestedAllocationsLost(
6736  uint32_t currentFrameIndex,
6737  uint32_t frameInUseCount,
6738  VmaAllocationRequest* pAllocationRequest) = 0;
6739 
6740  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6741 
6742  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6743 
6744  // Makes actual allocation based on request. Request must already be checked and valid.
6745  virtual void Alloc(
6746  const VmaAllocationRequest& request,
6747  VmaSuballocationType type,
6748  VkDeviceSize allocSize,
6749  VmaAllocation hAllocation) = 0;
6750 
6751  // Frees suballocation assigned to given memory region.
6752  virtual void Free(const VmaAllocation allocation) = 0;
6753  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6754 
6755 protected:
6756  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6757 
6758 #if VMA_STATS_STRING_ENABLED
6759  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6760  VkDeviceSize unusedBytes,
6761  size_t allocationCount,
6762  size_t unusedRangeCount) const;
6763  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6764  VkDeviceSize offset,
6765  VmaAllocation hAllocation) const;
6766  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6767  VkDeviceSize offset,
6768  VkDeviceSize size) const;
6769  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6770 #endif
6771 
6772 private:
6773  VkDeviceSize m_Size;
6774  const VkAllocationCallbacks* m_pAllocationCallbacks;
6775 };
6776 
6777 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6778  VMA_ASSERT(0 && "Validation failed: " #cond); \
6779  return false; \
6780  } } while(false)
6781 
6782 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6783 {
6784  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6785 public:
6786  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6787  virtual ~VmaBlockMetadata_Generic();
6788  virtual void Init(VkDeviceSize size);
6789 
6790  virtual bool Validate() const;
6791  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6792  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6793  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6794  virtual bool IsEmpty() const;
6795 
6796  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6797  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6798 
6799 #if VMA_STATS_STRING_ENABLED
6800  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6801 #endif
6802 
6803  virtual bool CreateAllocationRequest(
6804  uint32_t currentFrameIndex,
6805  uint32_t frameInUseCount,
6806  VkDeviceSize bufferImageGranularity,
6807  VkDeviceSize allocSize,
6808  VkDeviceSize allocAlignment,
6809  bool upperAddress,
6810  VmaSuballocationType allocType,
6811  bool canMakeOtherLost,
6812  uint32_t strategy,
6813  VmaAllocationRequest* pAllocationRequest);
6814 
6815  virtual bool MakeRequestedAllocationsLost(
6816  uint32_t currentFrameIndex,
6817  uint32_t frameInUseCount,
6818  VmaAllocationRequest* pAllocationRequest);
6819 
6820  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6821 
6822  virtual VkResult CheckCorruption(const void* pBlockData);
6823 
6824  virtual void Alloc(
6825  const VmaAllocationRequest& request,
6826  VmaSuballocationType type,
6827  VkDeviceSize allocSize,
6828  VmaAllocation hAllocation);
6829 
6830  virtual void Free(const VmaAllocation allocation);
6831  virtual void FreeAtOffset(VkDeviceSize offset);
6832 
6834  // For defragmentation
6835 
6836  bool IsBufferImageGranularityConflictPossible(
6837  VkDeviceSize bufferImageGranularity,
6838  VmaSuballocationType& inOutPrevSuballocType) const;
6839 
6840 private:
6841  friend class VmaDefragmentationAlgorithm_Generic;
6842  friend class VmaDefragmentationAlgorithm_Fast;
6843 
6844  uint32_t m_FreeCount;
6845  VkDeviceSize m_SumFreeSize;
6846  VmaSuballocationList m_Suballocations;
6847  // Suballocations that are free and have size greater than certain threshold.
6848  // Sorted by size, ascending.
6849  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6850 
6851  bool ValidateFreeSuballocationList() const;
6852 
6853  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6854  // If yes, fills pOffset and returns true. If no, returns false.
6855  bool CheckAllocation(
6856  uint32_t currentFrameIndex,
6857  uint32_t frameInUseCount,
6858  VkDeviceSize bufferImageGranularity,
6859  VkDeviceSize allocSize,
6860  VkDeviceSize allocAlignment,
6861  VmaSuballocationType allocType,
6862  VmaSuballocationList::const_iterator suballocItem,
6863  bool canMakeOtherLost,
6864  VkDeviceSize* pOffset,
6865  size_t* itemsToMakeLostCount,
6866  VkDeviceSize* pSumFreeSize,
6867  VkDeviceSize* pSumItemSize) const;
6868  // Given free suballocation, it merges it with following one, which must also be free.
6869  void MergeFreeWithNext(VmaSuballocationList::iterator item);
6870  // Releases given suballocation, making it free.
6871  // Merges it with adjacent free suballocations if applicable.
6872  // Returns iterator to new free suballocation at this place.
6873  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6874  // Given free suballocation, it inserts it into sorted list of
6875  // m_FreeSuballocationsBySize if it's suitable.
6876  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6877  // Given free suballocation, it removes it from sorted list of
6878  // m_FreeSuballocationsBySize if it's suitable.
6879  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6880 };
6881 
6882 /*
6883 Allocations and their references in internal data structure look like this:
6884 
6885 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6886 
6887  0 +-------+
6888  | |
6889  | |
6890  | |
6891  +-------+
6892  | Alloc | 1st[m_1stNullItemsBeginCount]
6893  +-------+
6894  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6895  +-------+
6896  | ... |
6897  +-------+
6898  | Alloc | 1st[1st.size() - 1]
6899  +-------+
6900  | |
6901  | |
6902  | |
6903 GetSize() +-------+
6904 
6905 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6906 
6907  0 +-------+
6908  | Alloc | 2nd[0]
6909  +-------+
6910  | Alloc | 2nd[1]
6911  +-------+
6912  | ... |
6913  +-------+
6914  | Alloc | 2nd[2nd.size() - 1]
6915  +-------+
6916  | |
6917  | |
6918  | |
6919  +-------+
6920  | Alloc | 1st[m_1stNullItemsBeginCount]
6921  +-------+
6922  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6923  +-------+
6924  | ... |
6925  +-------+
6926  | Alloc | 1st[1st.size() - 1]
6927  +-------+
6928  | |
6929 GetSize() +-------+
6930 
6931 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6932 
6933  0 +-------+
6934  | |
6935  | |
6936  | |
6937  +-------+
6938  | Alloc | 1st[m_1stNullItemsBeginCount]
6939  +-------+
6940  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6941  +-------+
6942  | ... |
6943  +-------+
6944  | Alloc | 1st[1st.size() - 1]
6945  +-------+
6946  | |
6947  | |
6948  | |
6949  +-------+
6950  | Alloc | 2nd[2nd.size() - 1]
6951  +-------+
6952  | ... |
6953  +-------+
6954  | Alloc | 2nd[1]
6955  +-------+
6956  | Alloc | 2nd[0]
6957 GetSize() +-------+
6958 
6959 */
6960 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6961 {
6962  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6963 public:
6964  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6965  virtual ~VmaBlockMetadata_Linear();
6966  virtual void Init(VkDeviceSize size);
6967 
6968  virtual bool Validate() const;
6969  virtual size_t GetAllocationCount() const;
6970  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6971  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6972  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6973 
6974  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6975  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6976 
6977 #if VMA_STATS_STRING_ENABLED
6978  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6979 #endif
6980 
6981  virtual bool CreateAllocationRequest(
6982  uint32_t currentFrameIndex,
6983  uint32_t frameInUseCount,
6984  VkDeviceSize bufferImageGranularity,
6985  VkDeviceSize allocSize,
6986  VkDeviceSize allocAlignment,
6987  bool upperAddress,
6988  VmaSuballocationType allocType,
6989  bool canMakeOtherLost,
6990  uint32_t strategy,
6991  VmaAllocationRequest* pAllocationRequest);
6992 
6993  virtual bool MakeRequestedAllocationsLost(
6994  uint32_t currentFrameIndex,
6995  uint32_t frameInUseCount,
6996  VmaAllocationRequest* pAllocationRequest);
6997 
6998  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6999 
7000  virtual VkResult CheckCorruption(const void* pBlockData);
7001 
7002  virtual void Alloc(
7003  const VmaAllocationRequest& request,
7004  VmaSuballocationType type,
7005  VkDeviceSize allocSize,
7006  VmaAllocation hAllocation);
7007 
7008  virtual void Free(const VmaAllocation allocation);
7009  virtual void FreeAtOffset(VkDeviceSize offset);
7010 
7011 private:
7012  /*
7013  There are two suballocation vectors, used in ping-pong way.
7014  The one with index m_1stVectorIndex is called 1st.
7015  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
7016  2nd can be non-empty only when 1st is not empty.
7017  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
7018  */
7019  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
7020 
7021  enum SECOND_VECTOR_MODE
7022  {
7023  SECOND_VECTOR_EMPTY,
7024  /*
7025  Suballocations in 2nd vector are created later than the ones in 1st, but they
7026  all have smaller offset.
7027  */
7028  SECOND_VECTOR_RING_BUFFER,
7029  /*
7030  Suballocations in 2nd vector are upper side of double stack.
7031  They all have offsets higher than those in 1st vector.
7032  Top of this stack means smaller offsets, but higher indices in this vector.
7033  */
7034  SECOND_VECTOR_DOUBLE_STACK,
7035  };
7036 
7037  VkDeviceSize m_SumFreeSize;
7038  SuballocationVectorType m_Suballocations0, m_Suballocations1;
7039  uint32_t m_1stVectorIndex;
7040  SECOND_VECTOR_MODE m_2ndVectorMode;
7041 
7042  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7043  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7044  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7045  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7046 
7047  // Number of items in 1st vector with hAllocation = null at the beginning.
7048  size_t m_1stNullItemsBeginCount;
7049  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7050  size_t m_1stNullItemsMiddleCount;
7051  // Number of items in 2nd vector with hAllocation = null.
7052  size_t m_2ndNullItemsCount;
7053 
7054  bool ShouldCompact1st() const;
7055  void CleanupAfterFree();
7056 
7057  bool CreateAllocationRequest_LowerAddress(
7058  uint32_t currentFrameIndex,
7059  uint32_t frameInUseCount,
7060  VkDeviceSize bufferImageGranularity,
7061  VkDeviceSize allocSize,
7062  VkDeviceSize allocAlignment,
7063  VmaSuballocationType allocType,
7064  bool canMakeOtherLost,
7065  uint32_t strategy,
7066  VmaAllocationRequest* pAllocationRequest);
7067  bool CreateAllocationRequest_UpperAddress(
7068  uint32_t currentFrameIndex,
7069  uint32_t frameInUseCount,
7070  VkDeviceSize bufferImageGranularity,
7071  VkDeviceSize allocSize,
7072  VkDeviceSize allocAlignment,
7073  VmaSuballocationType allocType,
7074  bool canMakeOtherLost,
7075  uint32_t strategy,
7076  VmaAllocationRequest* pAllocationRequest);
7077 };
7078 
7079 /*
7080 - GetSize() is the original size of allocated memory block.
7081 - m_UsableSize is this size aligned down to a power of two.
7082  All allocations and calculations happen relative to m_UsableSize.
7083 - GetUnusableSize() is the difference between them.
7084  It is reported as separate, unused range, not available for allocations.
7085 
7086 Node at level 0 has size = m_UsableSize.
7087 Each next level contains nodes with size 2 times smaller than current level.
7088 m_LevelCount is the maximum number of levels to use in the current object.
7089 */
7090 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
7091 {
7092  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
7093 public:
7094  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
7095  virtual ~VmaBlockMetadata_Buddy();
7096  virtual void Init(VkDeviceSize size);
7097 
7098  virtual bool Validate() const;
7099  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
7100  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
7101  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
7102  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
7103 
7104  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
7105  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
7106 
7107 #if VMA_STATS_STRING_ENABLED
7108  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
7109 #endif
7110 
7111  virtual bool CreateAllocationRequest(
7112  uint32_t currentFrameIndex,
7113  uint32_t frameInUseCount,
7114  VkDeviceSize bufferImageGranularity,
7115  VkDeviceSize allocSize,
7116  VkDeviceSize allocAlignment,
7117  bool upperAddress,
7118  VmaSuballocationType allocType,
7119  bool canMakeOtherLost,
7120  uint32_t strategy,
7121  VmaAllocationRequest* pAllocationRequest);
7122 
7123  virtual bool MakeRequestedAllocationsLost(
7124  uint32_t currentFrameIndex,
7125  uint32_t frameInUseCount,
7126  VmaAllocationRequest* pAllocationRequest);
7127 
7128  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
7129 
7130  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
7131 
7132  virtual void Alloc(
7133  const VmaAllocationRequest& request,
7134  VmaSuballocationType type,
7135  VkDeviceSize allocSize,
7136  VmaAllocation hAllocation);
7137 
7138  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
7139  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
7140 
7141 private:
7142  static const VkDeviceSize MIN_NODE_SIZE = 32;
7143  static const size_t MAX_LEVELS = 30;
7144 
7145  struct ValidationContext
7146  {
7147  size_t calculatedAllocationCount;
7148  size_t calculatedFreeCount;
7149  VkDeviceSize calculatedSumFreeSize;
7150 
7151  ValidationContext() :
7152  calculatedAllocationCount(0),
7153  calculatedFreeCount(0),
7154  calculatedSumFreeSize(0) { }
7155  };
7156 
7157  struct Node
7158  {
7159  VkDeviceSize offset;
7160  enum TYPE
7161  {
7162  TYPE_FREE,
7163  TYPE_ALLOCATION,
7164  TYPE_SPLIT,
7165  TYPE_COUNT
7166  } type;
7167  Node* parent;
7168  Node* buddy;
7169 
7170  union
7171  {
7172  struct
7173  {
7174  Node* prev;
7175  Node* next;
7176  } free;
7177  struct
7178  {
7179  VmaAllocation alloc;
7180  } allocation;
7181  struct
7182  {
7183  Node* leftChild;
7184  } split;
7185  };
7186  };
7187 
7188  // Size of the memory block aligned down to a power of two.
7189  VkDeviceSize m_UsableSize;
7190  uint32_t m_LevelCount;
7191 
7192  Node* m_Root;
7193  struct {
7194  Node* front;
7195  Node* back;
7196  } m_FreeList[MAX_LEVELS];
7197  // Number of nodes in the tree with type == TYPE_ALLOCATION.
7198  size_t m_AllocationCount;
7199  // Number of nodes in the tree with type == TYPE_FREE.
7200  size_t m_FreeCount;
7201  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
7202  VkDeviceSize m_SumFreeSize;
7203 
7204  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
7205  void DeleteNode(Node* node);
7206  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
7207  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
7208  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
7209  // Alloc passed just for validation. Can be null.
7210  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
7211  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
7212  // Adds node to the front of FreeList at given level.
7213  // node->type must be FREE.
7214  // node->free.prev, next can be undefined.
7215  void AddToFreeListFront(uint32_t level, Node* node);
7216  // Removes node from FreeList at given level.
7217  // node->type must be FREE.
7218  // node->free.prev, next stay untouched.
7219  void RemoveFromFreeList(uint32_t level, Node* node);
7220 
7221 #if VMA_STATS_STRING_ENABLED
7222  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
7223 #endif
7224 };
7225 
7226 /*
7227 Represents a single block of device memory (`VkDeviceMemory`) with all the
7228 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
7229 
7230 Thread-safety: This class must be externally synchronized.
7231 */
7232 class VmaDeviceMemoryBlock
7233 {
7234  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
7235 public:
7236  VmaBlockMetadata* m_pMetadata;
7237 
7238  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
7239 
7240  ~VmaDeviceMemoryBlock()
7241  {
7242  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
7243  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
7244  }
7245 
7246  // Always call after construction.
7247  void Init(
7248  VmaAllocator hAllocator,
7249  VmaPool hParentPool,
7250  uint32_t newMemoryTypeIndex,
7251  VkDeviceMemory newMemory,
7252  VkDeviceSize newSize,
7253  uint32_t id,
7254  uint32_t algorithm);
7255  // Always call before destruction.
7256  void Destroy(VmaAllocator allocator);
7257 
7258  VmaPool GetParentPool() const { return m_hParentPool; }
7259  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
7260  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7261  uint32_t GetId() const { return m_Id; }
7262  void* GetMappedData() const { return m_pMappedData; }
7263 
7264  // Validates all data structures inside this object. If not valid, returns false.
7265  bool Validate() const;
7266 
7267  VkResult CheckCorruption(VmaAllocator hAllocator);
7268 
7269  // ppData can be null.
7270  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
7271  void Unmap(VmaAllocator hAllocator, uint32_t count);
7272 
7273  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7274  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7275 
7276  VkResult BindBufferMemory(
7277  const VmaAllocator hAllocator,
7278  const VmaAllocation hAllocation,
7279  VkDeviceSize allocationLocalOffset,
7280  VkBuffer hBuffer,
7281  const void* pNext);
7282  VkResult BindImageMemory(
7283  const VmaAllocator hAllocator,
7284  const VmaAllocation hAllocation,
7285  VkDeviceSize allocationLocalOffset,
7286  VkImage hImage,
7287  const void* pNext);
7288 
7289 private:
7290  VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
7291  uint32_t m_MemoryTypeIndex;
7292  uint32_t m_Id;
7293  VkDeviceMemory m_hMemory;
7294 
7295  /*
7296  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
7297  Also protects m_MapCount, m_pMappedData.
7298  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
7299  */
7300  VMA_MUTEX m_Mutex;
7301  uint32_t m_MapCount;
7302  void* m_pMappedData;
7303 };
7304 
7305 struct VmaDefragmentationMove
7306 {
7307  size_t srcBlockIndex;
7308  size_t dstBlockIndex;
7309  VkDeviceSize srcOffset;
7310  VkDeviceSize dstOffset;
7311  VkDeviceSize size;
7312  VmaAllocation hAllocation;
7313  VmaDeviceMemoryBlock* pSrcBlock;
7314  VmaDeviceMemoryBlock* pDstBlock;
7315 };
7316 
7317 class VmaDefragmentationAlgorithm;
7318 
7319 /*
7320 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
7321 Vulkan memory type.
7322 
7323 Synchronized internally with a mutex.
7324 */
7325 struct VmaBlockVector
7326 {
7327  VMA_CLASS_NO_COPY(VmaBlockVector)
7328 public:
7329  VmaBlockVector(
7330  VmaAllocator hAllocator,
7331  VmaPool hParentPool,
7332  uint32_t memoryTypeIndex,
7333  VkDeviceSize preferredBlockSize,
7334  size_t minBlockCount,
7335  size_t maxBlockCount,
7336  VkDeviceSize bufferImageGranularity,
7337  uint32_t frameInUseCount,
7338  bool explicitBlockSize,
7339  uint32_t algorithm,
7340  float priority,
7341  VkDeviceSize minAllocationAlignment);
7342  ~VmaBlockVector();
7343 
7344  VkResult CreateMinBlocks();
7345 
7346  VmaAllocator GetAllocator() const { return m_hAllocator; }
7347  VmaPool GetParentPool() const { return m_hParentPool; }
7348  bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
7349  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7350  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
7351  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
7352  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
7353  uint32_t GetAlgorithm() const { return m_Algorithm; }
7354 
7355  void GetPoolStats(VmaPoolStats* pStats);
7356 
7357  bool IsEmpty();
7358  bool IsCorruptionDetectionEnabled() const;
7359 
7360  VkResult Allocate(
7361  uint32_t currentFrameIndex,
7362  VkDeviceSize size,
7363  VkDeviceSize alignment,
7364  const VmaAllocationCreateInfo& createInfo,
7365  VmaSuballocationType suballocType,
7366  size_t allocationCount,
7367  VmaAllocation* pAllocations);
7368 
7369  void Free(const VmaAllocation hAllocation);
7370 
7371  // Adds statistics of this BlockVector to pStats.
7372  void AddStats(VmaStats* pStats);
7373 
7374 #if VMA_STATS_STRING_ENABLED
7375  void PrintDetailedMap(class VmaJsonWriter& json);
7376 #endif
7377 
7378  void MakePoolAllocationsLost(
7379  uint32_t currentFrameIndex,
7380  size_t* pLostAllocationCount);
7381  VkResult CheckCorruption();
7382 
7383  // Saves results in pCtx->res.
7384  void Defragment(
7385  class VmaBlockVectorDefragmentationContext* pCtx,
7387  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
7388  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
7389  VkCommandBuffer commandBuffer);
7390  void DefragmentationEnd(
7391  class VmaBlockVectorDefragmentationContext* pCtx,
7392  uint32_t flags,
7393  VmaDefragmentationStats* pStats);
7394 
7395  uint32_t ProcessDefragmentations(
7396  class VmaBlockVectorDefragmentationContext *pCtx,
7397  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
7398 
7399  void CommitDefragmentations(
7400  class VmaBlockVectorDefragmentationContext *pCtx,
7401  VmaDefragmentationStats* pStats);
7402 
7404  // To be used only while the m_Mutex is locked. Used during defragmentation.
7405 
7406  size_t GetBlockCount() const { return m_Blocks.size(); }
7407  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
7408  size_t CalcAllocationCount() const;
7409  bool IsBufferImageGranularityConflictPossible() const;
7410 
7411 private:
7412  friend class VmaDefragmentationAlgorithm_Generic;
7413 
7414  const VmaAllocator m_hAllocator;
7415  const VmaPool m_hParentPool;
7416  const uint32_t m_MemoryTypeIndex;
7417  const VkDeviceSize m_PreferredBlockSize;
7418  const size_t m_MinBlockCount;
7419  const size_t m_MaxBlockCount;
7420  const VkDeviceSize m_BufferImageGranularity;
7421  const uint32_t m_FrameInUseCount;
7422  const bool m_ExplicitBlockSize;
7423  const uint32_t m_Algorithm;
7424  const float m_Priority;
7425  const VkDeviceSize m_MinAllocationAlignment;
7426  VMA_RW_MUTEX m_Mutex;
7427 
7428  /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7429  a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7430  bool m_HasEmptyBlock;
7431  // Incrementally sorted by sumFreeSize, ascending.
7432  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7433  uint32_t m_NextBlockId;
7434 
7435  VkDeviceSize CalcMaxBlockSize() const;
7436 
7437  // Finds and removes given block from vector.
7438  void Remove(VmaDeviceMemoryBlock* pBlock);
7439 
7440  // Performs single step in sorting m_Blocks. They may not be fully sorted
7441  // after this call.
7442  void IncrementallySortBlocks();
7443 
7444  VkResult AllocatePage(
7445  uint32_t currentFrameIndex,
7446  VkDeviceSize size,
7447  VkDeviceSize alignment,
7448  const VmaAllocationCreateInfo& createInfo,
7449  VmaSuballocationType suballocType,
7450  VmaAllocation* pAllocation);
7451 
7452  // To be used only without CAN_MAKE_OTHER_LOST flag.
7453  VkResult AllocateFromBlock(
7454  VmaDeviceMemoryBlock* pBlock,
7455  uint32_t currentFrameIndex,
7456  VkDeviceSize size,
7457  VkDeviceSize alignment,
7458  VmaAllocationCreateFlags allocFlags,
7459  void* pUserData,
7460  VmaSuballocationType suballocType,
7461  uint32_t strategy,
7462  VmaAllocation* pAllocation);
7463 
7464  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7465 
7466  // Saves result to pCtx->res.
7467  void ApplyDefragmentationMovesCpu(
7468  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7469  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7470  // Saves result to pCtx->res.
7471  void ApplyDefragmentationMovesGpu(
7472  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7473  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7474  VkCommandBuffer commandBuffer);
7475 
7476  /*
7477  Used during defragmentation. pDefragmentationStats is optional. It's in/out
7478  - updated with new data.
7479  */
7480  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7481 
7482  void UpdateHasEmptyBlock();
7483 };
7484 
7485 struct VmaPool_T
7486 {
7487  VMA_CLASS_NO_COPY(VmaPool_T)
7488 public:
7489  VmaBlockVector m_BlockVector;
7490 
7491  VmaPool_T(
7492  VmaAllocator hAllocator,
7493  const VmaPoolCreateInfo& createInfo,
7494  VkDeviceSize preferredBlockSize);
7495  ~VmaPool_T();
7496 
7497  uint32_t GetId() const { return m_Id; }
7498  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7499 
7500  const char* GetName() const { return m_Name; }
7501  void SetName(const char* pName);
7502 
7503 #if VMA_STATS_STRING_ENABLED
7504  //void PrintDetailedMap(class VmaStringBuilder& sb);
7505 #endif
7506 
7507 private:
7508  uint32_t m_Id;
7509  char* m_Name;
7510  VmaPool_T* m_PrevPool = VMA_NULL;
7511  VmaPool_T* m_NextPool = VMA_NULL;
7512  friend struct VmaPoolListItemTraits;
7513 };
7514 
7515 struct VmaPoolListItemTraits
7516 {
7517  typedef VmaPool_T ItemType;
7518  static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
7519  static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
7520  static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
7521  static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
7522 };
7523 
7524 /*
7525 Performs defragmentation:
7526 
7527 - Updates `pBlockVector->m_pMetadata`.
7528 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7529 - Does not move actual data, only returns requested moves as `moves`.
7530 */
7531 class VmaDefragmentationAlgorithm
7532 {
7533  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7534 public:
7535  VmaDefragmentationAlgorithm(
7536  VmaAllocator hAllocator,
7537  VmaBlockVector* pBlockVector,
7538  uint32_t currentFrameIndex) :
7539  m_hAllocator(hAllocator),
7540  m_pBlockVector(pBlockVector),
7541  m_CurrentFrameIndex(currentFrameIndex)
7542  {
7543  }
7544  virtual ~VmaDefragmentationAlgorithm()
7545  {
7546  }
7547 
7548  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7549  virtual void AddAll() = 0;
7550 
7551  virtual VkResult Defragment(
7552  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7553  VkDeviceSize maxBytesToMove,
7554  uint32_t maxAllocationsToMove,
7555  VmaDefragmentationFlags flags) = 0;
7556 
7557  virtual VkDeviceSize GetBytesMoved() const = 0;
7558  virtual uint32_t GetAllocationsMoved() const = 0;
7559 
7560 protected:
7561  VmaAllocator const m_hAllocator;
7562  VmaBlockVector* const m_pBlockVector;
7563  const uint32_t m_CurrentFrameIndex;
7564 
7565  struct AllocationInfo
7566  {
7567  VmaAllocation m_hAllocation;
7568  VkBool32* m_pChanged;
7569 
7570  AllocationInfo() :
7571  m_hAllocation(VK_NULL_HANDLE),
7572  m_pChanged(VMA_NULL)
7573  {
7574  }
7575  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7576  m_hAllocation(hAlloc),
7577  m_pChanged(pChanged)
7578  {
7579  }
7580  };
7581 };
7582 
7583 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7584 {
7585  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7586 public:
7587  VmaDefragmentationAlgorithm_Generic(
7588  VmaAllocator hAllocator,
7589  VmaBlockVector* pBlockVector,
7590  uint32_t currentFrameIndex,
7591  bool overlappingMoveSupported);
7592  virtual ~VmaDefragmentationAlgorithm_Generic();
7593 
7594  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7595  virtual void AddAll() { m_AllAllocations = true; }
7596 
7597  virtual VkResult Defragment(
7598  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7599  VkDeviceSize maxBytesToMove,
7600  uint32_t maxAllocationsToMove,
7601  VmaDefragmentationFlags flags);
7602 
7603  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7604  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7605 
7606 private:
7607  uint32_t m_AllocationCount;
7608  bool m_AllAllocations;
7609 
7610  VkDeviceSize m_BytesMoved;
7611  uint32_t m_AllocationsMoved;
7612 
7613  struct AllocationInfoSizeGreater
7614  {
7615  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7616  {
7617  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7618  }
7619  };
7620 
7621  struct AllocationInfoOffsetGreater
7622  {
7623  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7624  {
7625  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7626  }
7627  };
7628 
7629  struct BlockInfo
7630  {
7631  size_t m_OriginalBlockIndex;
7632  VmaDeviceMemoryBlock* m_pBlock;
7633  bool m_HasNonMovableAllocations;
7634  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7635 
7636  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7637  m_OriginalBlockIndex(SIZE_MAX),
7638  m_pBlock(VMA_NULL),
7639  m_HasNonMovableAllocations(true),
7640  m_Allocations(pAllocationCallbacks)
7641  {
7642  }
7643 
7644  void CalcHasNonMovableAllocations()
7645  {
7646  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7647  const size_t defragmentAllocCount = m_Allocations.size();
7648  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7649  }
7650 
7651  void SortAllocationsBySizeDescending()
7652  {
7653  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7654  }
7655 
7656  void SortAllocationsByOffsetDescending()
7657  {
7658  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7659  }
7660  };
7661 
7662  struct BlockPointerLess
7663  {
7664  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7665  {
7666  return pLhsBlockInfo->m_pBlock < pRhsBlock;
7667  }
7668  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7669  {
7670  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7671  }
7672  };
7673 
7674  // 1. Blocks with some non-movable allocations go first.
7675  // 2. Blocks with smaller sumFreeSize go first.
7676  struct BlockInfoCompareMoveDestination
7677  {
7678  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7679  {
7680  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7681  {
7682  return true;
7683  }
7684  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7685  {
7686  return false;
7687  }
7688  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7689  {
7690  return true;
7691  }
7692  return false;
7693  }
7694  };
7695 
7696  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7697  BlockInfoVector m_Blocks;
7698 
7699  VkResult DefragmentRound(
7700  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7701  VkDeviceSize maxBytesToMove,
7702  uint32_t maxAllocationsToMove,
7703  bool freeOldAllocations);
7704 
7705  size_t CalcBlocksWithNonMovableCount() const;
7706 
7707  static bool MoveMakesSense(
7708  size_t dstBlockIndex, VkDeviceSize dstOffset,
7709  size_t srcBlockIndex, VkDeviceSize srcOffset);
7710 };
7711 
7712 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7713 {
7714  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7715 public:
7716  VmaDefragmentationAlgorithm_Fast(
7717  VmaAllocator hAllocator,
7718  VmaBlockVector* pBlockVector,
7719  uint32_t currentFrameIndex,
7720  bool overlappingMoveSupported);
7721  virtual ~VmaDefragmentationAlgorithm_Fast();
7722 
7723  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7724  virtual void AddAll() { m_AllAllocations = true; }
7725 
7726  virtual VkResult Defragment(
7727  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7728  VkDeviceSize maxBytesToMove,
7729  uint32_t maxAllocationsToMove,
7730  VmaDefragmentationFlags flags);
7731 
7732  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7733  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7734 
7735 private:
7736  struct BlockInfo
7737  {
7738  size_t origBlockIndex;
7739  };
7740 
7741  class FreeSpaceDatabase
7742  {
7743  public:
7744  FreeSpaceDatabase()
7745  {
7746  FreeSpace s = {};
7747  s.blockInfoIndex = SIZE_MAX;
7748  for(size_t i = 0; i < MAX_COUNT; ++i)
7749  {
7750  m_FreeSpaces[i] = s;
7751  }
7752  }
7753 
7754  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7755  {
7756  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7757  {
7758  return;
7759  }
7760 
7761  // Find first invalid or the smallest structure.
7762  size_t bestIndex = SIZE_MAX;
7763  for(size_t i = 0; i < MAX_COUNT; ++i)
7764  {
7765  // Empty structure.
7766  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7767  {
7768  bestIndex = i;
7769  break;
7770  }
7771  if(m_FreeSpaces[i].size < size &&
7772  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7773  {
7774  bestIndex = i;
7775  }
7776  }
7777 
7778  if(bestIndex != SIZE_MAX)
7779  {
7780  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7781  m_FreeSpaces[bestIndex].offset = offset;
7782  m_FreeSpaces[bestIndex].size = size;
7783  }
7784  }
7785 
7786  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7787  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7788  {
7789  size_t bestIndex = SIZE_MAX;
7790  VkDeviceSize bestFreeSpaceAfter = 0;
7791  for(size_t i = 0; i < MAX_COUNT; ++i)
7792  {
7793  // Structure is valid.
7794  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7795  {
7796  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7797  // Allocation fits into this structure.
7798  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7799  {
7800  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7801  (dstOffset + size);
7802  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7803  {
7804  bestIndex = i;
7805  bestFreeSpaceAfter = freeSpaceAfter;
7806  }
7807  }
7808  }
7809  }
7810 
7811  if(bestIndex != SIZE_MAX)
7812  {
7813  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7814  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7815 
7816  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7817  {
7818  // Leave this structure for remaining empty space.
7819  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7820  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7821  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7822  }
7823  else
7824  {
7825  // This structure becomes invalid.
7826  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7827  }
7828 
7829  return true;
7830  }
7831 
7832  return false;
7833  }
7834 
7835  private:
7836  static const size_t MAX_COUNT = 4;
7837 
7838  struct FreeSpace
7839  {
7840  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7841  VkDeviceSize offset;
7842  VkDeviceSize size;
7843  } m_FreeSpaces[MAX_COUNT];
7844  };
7845 
7846  const bool m_OverlappingMoveSupported;
7847 
7848  uint32_t m_AllocationCount;
7849  bool m_AllAllocations;
7850 
7851  VkDeviceSize m_BytesMoved;
7852  uint32_t m_AllocationsMoved;
7853 
7854  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7855 
7856  void PreprocessMetadata();
7857  void PostprocessMetadata();
7858  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7859 };
7860 
7861 struct VmaBlockDefragmentationContext
7862 {
7863  enum BLOCK_FLAG
7864  {
7865  BLOCK_FLAG_USED = 0x00000001,
7866  };
7867  uint32_t flags;
7868  VkBuffer hBuffer;
7869 };
7870 
7871 class VmaBlockVectorDefragmentationContext
7872 {
7873  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7874 public:
7875  VkResult res;
7876  bool mutexLocked;
7877  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7878  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7879  uint32_t defragmentationMovesProcessed;
7880  uint32_t defragmentationMovesCommitted;
7881  bool hasDefragmentationPlan;
7882 
7883  VmaBlockVectorDefragmentationContext(
7884  VmaAllocator hAllocator,
7885  VmaPool hCustomPool, // Optional.
7886  VmaBlockVector* pBlockVector,
7887  uint32_t currFrameIndex);
7888  ~VmaBlockVectorDefragmentationContext();
7889 
7890  VmaPool GetCustomPool() const { return m_hCustomPool; }
7891  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7892  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7893 
7894  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7895  void AddAll() { m_AllAllocations = true; }
7896 
7897  void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7898 
7899 private:
7900  const VmaAllocator m_hAllocator;
7901  // Null if not from custom pool.
7902  const VmaPool m_hCustomPool;
7903  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7904  VmaBlockVector* const m_pBlockVector;
7905  const uint32_t m_CurrFrameIndex;
7906  // Owner of this object.
7907  VmaDefragmentationAlgorithm* m_pAlgorithm;
7908 
7909  struct AllocInfo
7910  {
7911  VmaAllocation hAlloc;
7912  VkBool32* pChanged;
7913  };
7914  // Used between constructor and Begin.
7915  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7916  bool m_AllAllocations;
7917 };
7918 
7919 struct VmaDefragmentationContext_T
7920 {
7921 private:
7922  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7923 public:
7924  VmaDefragmentationContext_T(
7925  VmaAllocator hAllocator,
7926  uint32_t currFrameIndex,
7927  uint32_t flags,
7928  VmaDefragmentationStats* pStats);
7929  ~VmaDefragmentationContext_T();
7930 
7931  void AddPools(uint32_t poolCount, const VmaPool* pPools);
7932  void AddAllocations(
7933  uint32_t allocationCount,
7934  const VmaAllocation* pAllocations,
7935  VkBool32* pAllocationsChanged);
7936 
7937  /*
7938  Returns:
7939  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7940  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7941  - Negative value if error occurred and object can be destroyed immediately.
7942  */
7943  VkResult Defragment(
7944  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7945  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7946  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7947 
7948  VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7949  VkResult DefragmentPassEnd();
7950 
7951 private:
7952  const VmaAllocator m_hAllocator;
7953  const uint32_t m_CurrFrameIndex;
7954  const uint32_t m_Flags;
7955  VmaDefragmentationStats* const m_pStats;
7956 
7957  VkDeviceSize m_MaxCpuBytesToMove;
7958  uint32_t m_MaxCpuAllocationsToMove;
7959  VkDeviceSize m_MaxGpuBytesToMove;
7960  uint32_t m_MaxGpuAllocationsToMove;
7961 
7962  // Owner of these objects.
7963  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7964  // Owner of these objects.
7965  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7966 };
7967 
7968 #if VMA_RECORDING_ENABLED
7969 
7970 class VmaRecorder
7971 {
7972 public:
7973  VmaRecorder();
7974  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7975  void WriteConfiguration(
7976  const VkPhysicalDeviceProperties& devProps,
7977  const VkPhysicalDeviceMemoryProperties& memProps,
7978  uint32_t vulkanApiVersion,
7979  bool dedicatedAllocationExtensionEnabled,
7980  bool bindMemory2ExtensionEnabled,
7981  bool memoryBudgetExtensionEnabled,
7982  bool deviceCoherentMemoryExtensionEnabled);
7983  ~VmaRecorder();
7984 
7985  void RecordCreateAllocator(uint32_t frameIndex);
7986  void RecordDestroyAllocator(uint32_t frameIndex);
7987  void RecordCreatePool(uint32_t frameIndex,
7988  const VmaPoolCreateInfo& createInfo,
7989  VmaPool pool);
7990  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7991  void RecordAllocateMemory(uint32_t frameIndex,
7992  const VkMemoryRequirements& vkMemReq,
7993  const VmaAllocationCreateInfo& createInfo,
7994  VmaAllocation allocation);
7995  void RecordAllocateMemoryPages(uint32_t frameIndex,
7996  const VkMemoryRequirements& vkMemReq,
7997  const VmaAllocationCreateInfo& createInfo,
7998  uint64_t allocationCount,
7999  const VmaAllocation* pAllocations);
8000  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
8001  const VkMemoryRequirements& vkMemReq,
8002  bool requiresDedicatedAllocation,
8003  bool prefersDedicatedAllocation,
8004  const VmaAllocationCreateInfo& createInfo,
8005  VmaAllocation allocation);
8006  void RecordAllocateMemoryForImage(uint32_t frameIndex,
8007  const VkMemoryRequirements& vkMemReq,
8008  bool requiresDedicatedAllocation,
8009  bool prefersDedicatedAllocation,
8010  const VmaAllocationCreateInfo& createInfo,
8011  VmaAllocation allocation);
8012  void RecordFreeMemory(uint32_t frameIndex,
8013  VmaAllocation allocation);
8014  void RecordFreeMemoryPages(uint32_t frameIndex,
8015  uint64_t allocationCount,
8016  const VmaAllocation* pAllocations);
8017  void RecordSetAllocationUserData(uint32_t frameIndex,
8018  VmaAllocation allocation,
8019  const void* pUserData);
8020  void RecordCreateLostAllocation(uint32_t frameIndex,
8021  VmaAllocation allocation);
8022  void RecordMapMemory(uint32_t frameIndex,
8023  VmaAllocation allocation);
8024  void RecordUnmapMemory(uint32_t frameIndex,
8025  VmaAllocation allocation);
8026  void RecordFlushAllocation(uint32_t frameIndex,
8027  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
8028  void RecordInvalidateAllocation(uint32_t frameIndex,
8029  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
8030  void RecordCreateBuffer(uint32_t frameIndex,
8031  const VkBufferCreateInfo& bufCreateInfo,
8032  const VmaAllocationCreateInfo& allocCreateInfo,
8033  VmaAllocation allocation);
8034  void RecordCreateImage(uint32_t frameIndex,
8035  const VkImageCreateInfo& imageCreateInfo,
8036  const VmaAllocationCreateInfo& allocCreateInfo,
8037  VmaAllocation allocation);
8038  void RecordDestroyBuffer(uint32_t frameIndex,
8039  VmaAllocation allocation);
8040  void RecordDestroyImage(uint32_t frameIndex,
8041  VmaAllocation allocation);
8042  void RecordTouchAllocation(uint32_t frameIndex,
8043  VmaAllocation allocation);
8044  void RecordGetAllocationInfo(uint32_t frameIndex,
8045  VmaAllocation allocation);
8046  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
8047  VmaPool pool);
8048  void RecordDefragmentationBegin(uint32_t frameIndex,
8049  const VmaDefragmentationInfo2& info,
8051  void RecordDefragmentationEnd(uint32_t frameIndex,
8053  void RecordSetPoolName(uint32_t frameIndex,
8054  VmaPool pool,
8055  const char* name);
8056 
8057 private:
8058  struct CallParams
8059  {
8060  uint32_t threadId;
8061  double time;
8062  };
8063 
8064  class UserDataString
8065  {
8066  public:
8067  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
8068  const char* GetString() const { return m_Str; }
8069 
8070  private:
8071  char m_PtrStr[17];
8072  const char* m_Str;
8073  };
8074 
8075  bool m_UseMutex;
8076  VmaRecordFlags m_Flags;
8077  FILE* m_File;
8078  VMA_MUTEX m_FileMutex;
8079  std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
8080 
8081  void GetBasicParams(CallParams& outParams);
8082 
8083  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
8084  template<typename T>
8085  void PrintPointerList(uint64_t count, const T* pItems)
8086  {
8087  if(count)
8088  {
8089  fprintf(m_File, "%p", pItems[0]);
8090  for(uint64_t i = 1; i < count; ++i)
8091  {
8092  fprintf(m_File, " %p", pItems[i]);
8093  }
8094  }
8095  }
8096 
8097  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
8098  void Flush();
8099 };
8100 
8101 #endif // #if VMA_RECORDING_ENABLED
8102 
8103 /*
8104 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
8105 */
8106 class VmaAllocationObjectAllocator
8107 {
8108  VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
8109 public:
8110  VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
8111 
8112  template<typename... Types> VmaAllocation Allocate(Types... args);
8113  void Free(VmaAllocation hAlloc);
8114 
8115 private:
8116  VMA_MUTEX m_Mutex;
8117  VmaPoolAllocator<VmaAllocation_T> m_Allocator;
8118 };
8119 
8120 struct VmaCurrentBudgetData
8121 {
8122  VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
8123  VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
8124 
8125 #if VMA_MEMORY_BUDGET
8126  VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
8127  VMA_RW_MUTEX m_BudgetMutex;
8128  uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
8129  uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
8130  uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
8131 #endif // #if VMA_MEMORY_BUDGET
8132 
8133  VmaCurrentBudgetData()
8134  {
8135  for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
8136  {
8137  m_BlockBytes[heapIndex] = 0;
8138  m_AllocationBytes[heapIndex] = 0;
8139 #if VMA_MEMORY_BUDGET
8140  m_VulkanUsage[heapIndex] = 0;
8141  m_VulkanBudget[heapIndex] = 0;
8142  m_BlockBytesAtBudgetFetch[heapIndex] = 0;
8143 #endif
8144  }
8145 
8146 #if VMA_MEMORY_BUDGET
8147  m_OperationsSinceBudgetFetch = 0;
8148 #endif
8149  }
8150 
8151  void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8152  {
8153  m_AllocationBytes[heapIndex] += allocationSize;
8154 #if VMA_MEMORY_BUDGET
8155  ++m_OperationsSinceBudgetFetch;
8156 #endif
8157  }
8158 
8159  void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8160  {
8161  VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
8162  m_AllocationBytes[heapIndex] -= allocationSize;
8163 #if VMA_MEMORY_BUDGET
8164  ++m_OperationsSinceBudgetFetch;
8165 #endif
8166  }
8167 };
8168 
8169 // Main allocator object.
8170 struct VmaAllocator_T
8171 {
8172  VMA_CLASS_NO_COPY(VmaAllocator_T)
8173 public:
8174  bool m_UseMutex;
8175  uint32_t m_VulkanApiVersion;
8176  bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8177  bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8178  bool m_UseExtMemoryBudget;
8179  bool m_UseAmdDeviceCoherentMemory;
8180  bool m_UseKhrBufferDeviceAddress;
8181  bool m_UseExtMemoryPriority;
8182  VkDevice m_hDevice;
8183  VkInstance m_hInstance;
8184  bool m_AllocationCallbacksSpecified;
8185  VkAllocationCallbacks m_AllocationCallbacks;
8186  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
8187  VmaAllocationObjectAllocator m_AllocationObjectAllocator;
8188 
8189  // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
8190  uint32_t m_HeapSizeLimitMask;
8191 
8192  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
8193  VkPhysicalDeviceMemoryProperties m_MemProps;
8194 
8195  // Default pools.
8196  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
8197 
8198  typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
8199  DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
8200  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
8201 
8202  VmaCurrentBudgetData m_Budget;
8203  VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
8204 
8205  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
8206  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
8207  ~VmaAllocator_T();
8208 
8209  const VkAllocationCallbacks* GetAllocationCallbacks() const
8210  {
8211  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
8212  }
8213  const VmaVulkanFunctions& GetVulkanFunctions() const
8214  {
8215  return m_VulkanFunctions;
8216  }
8217 
8218  VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
8219 
8220  VkDeviceSize GetBufferImageGranularity() const
8221  {
8222  return VMA_MAX(
8223  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
8224  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
8225  }
8226 
8227  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
8228  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
8229 
8230  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
8231  {
8232  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
8233  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
8234  }
8235  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
8236  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
8237  {
8238  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
8239  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8240  }
8241  // Minimum alignment for all allocations in specific memory type.
8242  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
8243  {
8244  return IsMemoryTypeNonCoherent(memTypeIndex) ?
8245  VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
8246  (VkDeviceSize)VMA_MIN_ALIGNMENT;
8247  }
8248 
8249  bool IsIntegratedGpu() const
8250  {
8251  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
8252  }
8253 
8254  uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
8255 
8256 #if VMA_RECORDING_ENABLED
8257  VmaRecorder* GetRecorder() const { return m_pRecorder; }
8258 #endif
8259 
8260  void GetBufferMemoryRequirements(
8261  VkBuffer hBuffer,
8262  VkMemoryRequirements& memReq,
8263  bool& requiresDedicatedAllocation,
8264  bool& prefersDedicatedAllocation) const;
8265  void GetImageMemoryRequirements(
8266  VkImage hImage,
8267  VkMemoryRequirements& memReq,
8268  bool& requiresDedicatedAllocation,
8269  bool& prefersDedicatedAllocation) const;
8270 
8271  // Main allocation function.
8272  VkResult AllocateMemory(
8273  const VkMemoryRequirements& vkMemReq,
8274  bool requiresDedicatedAllocation,
8275  bool prefersDedicatedAllocation,
8276  VkBuffer dedicatedBuffer,
8277  VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
8278  VkImage dedicatedImage,
8279  const VmaAllocationCreateInfo& createInfo,
8280  VmaSuballocationType suballocType,
8281  size_t allocationCount,
8282  VmaAllocation* pAllocations);
8283 
8284  // Main deallocation function.
8285  void FreeMemory(
8286  size_t allocationCount,
8287  const VmaAllocation* pAllocations);
8288 
8289  void CalculateStats(VmaStats* pStats);
8290 
8291  void GetBudget(
8292  VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
8293 
8294 #if VMA_STATS_STRING_ENABLED
8295  void PrintDetailedMap(class VmaJsonWriter& json);
8296 #endif
8297 
8298  VkResult DefragmentationBegin(
8299  const VmaDefragmentationInfo2& info,
8300  VmaDefragmentationStats* pStats,
8301  VmaDefragmentationContext* pContext);
8302  VkResult DefragmentationEnd(
8303  VmaDefragmentationContext context);
8304 
8305  VkResult DefragmentationPassBegin(
8307  VmaDefragmentationContext context);
8308  VkResult DefragmentationPassEnd(
8309  VmaDefragmentationContext context);
8310 
8311  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
8312  bool TouchAllocation(VmaAllocation hAllocation);
8313 
8314  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
8315  void DestroyPool(VmaPool pool);
8316  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
8317 
8318  void SetCurrentFrameIndex(uint32_t frameIndex);
8319  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
8320 
8321  void MakePoolAllocationsLost(
8322  VmaPool hPool,
8323  size_t* pLostAllocationCount);
8324  VkResult CheckPoolCorruption(VmaPool hPool);
8325  VkResult CheckCorruption(uint32_t memoryTypeBits);
8326 
8327  void CreateLostAllocation(VmaAllocation* pAllocation);
8328 
8329  // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
8330  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
8331  // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
8332  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
8333  // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
8334  VkResult BindVulkanBuffer(
8335  VkDeviceMemory memory,
8336  VkDeviceSize memoryOffset,
8337  VkBuffer buffer,
8338  const void* pNext);
8339  // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
8340  VkResult BindVulkanImage(
8341  VkDeviceMemory memory,
8342  VkDeviceSize memoryOffset,
8343  VkImage image,
8344  const void* pNext);
8345 
8346  VkResult Map(VmaAllocation hAllocation, void** ppData);
8347  void Unmap(VmaAllocation hAllocation);
8348 
8349  VkResult BindBufferMemory(
8350  VmaAllocation hAllocation,
8351  VkDeviceSize allocationLocalOffset,
8352  VkBuffer hBuffer,
8353  const void* pNext);
8354  VkResult BindImageMemory(
8355  VmaAllocation hAllocation,
8356  VkDeviceSize allocationLocalOffset,
8357  VkImage hImage,
8358  const void* pNext);
8359 
8360  VkResult FlushOrInvalidateAllocation(
8361  VmaAllocation hAllocation,
8362  VkDeviceSize offset, VkDeviceSize size,
8363  VMA_CACHE_OPERATION op);
8364  VkResult FlushOrInvalidateAllocations(
8365  uint32_t allocationCount,
8366  const VmaAllocation* allocations,
8367  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
8368  VMA_CACHE_OPERATION op);
8369 
8370  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
8371 
8372  /*
8373  Returns bit mask of memory types that can support defragmentation on GPU as
8374  they support creation of required buffer for copy operations.
8375  */
8376  uint32_t GetGpuDefragmentationMemoryTypeBits();
8377 
8378 private:
8379  VkDeviceSize m_PreferredLargeHeapBlockSize;
8380 
8381  VkPhysicalDevice m_PhysicalDevice;
8382  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
8383  VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
8384 
8385  VMA_RW_MUTEX m_PoolsMutex;
8386  typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
8387  // Protected by m_PoolsMutex.
8388  PoolList m_Pools;
8389  uint32_t m_NextPoolId;
8390 
8391  VmaVulkanFunctions m_VulkanFunctions;
8392 
8393  // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
8394  uint32_t m_GlobalMemoryTypeBits;
8395 
8396 #if VMA_RECORDING_ENABLED
8397  VmaRecorder* m_pRecorder;
8398 #endif
8399 
8400  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
8401 
8402 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8403  void ImportVulkanFunctions_Static();
8404 #endif
8405 
8406  void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8407 
8408 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8409  void ImportVulkanFunctions_Dynamic();
8410 #endif
8411 
8412  void ValidateVulkanFunctions();
8413 
8414  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8415 
8416  VkResult AllocateMemoryOfType(
8417  VkDeviceSize size,
8418  VkDeviceSize alignment,
8419  bool dedicatedAllocation,
8420  VkBuffer dedicatedBuffer,
8421  VkBufferUsageFlags dedicatedBufferUsage,
8422  VkImage dedicatedImage,
8423  const VmaAllocationCreateInfo& createInfo,
8424  uint32_t memTypeIndex,
8425  VmaSuballocationType suballocType,
8426  size_t allocationCount,
8427  VmaAllocation* pAllocations);
8428 
8429  // Helper function only to be used inside AllocateDedicatedMemory.
8430  VkResult AllocateDedicatedMemoryPage(
8431  VkDeviceSize size,
8432  VmaSuballocationType suballocType,
8433  uint32_t memTypeIndex,
8434  const VkMemoryAllocateInfo& allocInfo,
8435  bool map,
8436  bool isUserDataString,
8437  void* pUserData,
8438  VmaAllocation* pAllocation);
8439 
8440  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8441  VkResult AllocateDedicatedMemory(
8442  VkDeviceSize size,
8443  VmaSuballocationType suballocType,
8444  uint32_t memTypeIndex,
8445  bool withinBudget,
8446  bool map,
8447  bool isUserDataString,
8448  void* pUserData,
8449  float priority,
8450  VkBuffer dedicatedBuffer,
8451  VkBufferUsageFlags dedicatedBufferUsage,
8452  VkImage dedicatedImage,
8453  size_t allocationCount,
8454  VmaAllocation* pAllocations);
8455 
8456  void FreeDedicatedMemory(const VmaAllocation allocation);
8457 
8458  /*
8459  Calculates and returns bit mask of memory types that can support defragmentation
8460  on GPU as they support creation of required buffer for copy operations.
8461  */
8462  uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8463 
8464  uint32_t CalculateGlobalMemoryTypeBits() const;
8465 
8466  bool GetFlushOrInvalidateRange(
8467  VmaAllocation allocation,
8468  VkDeviceSize offset, VkDeviceSize size,
8469  VkMappedMemoryRange& outRange) const;
8470 
8471 #if VMA_MEMORY_BUDGET
8472  void UpdateVulkanBudget();
8473 #endif // #if VMA_MEMORY_BUDGET
8474 };
8475 
8477 // Memory allocation #2 after VmaAllocator_T definition
8478 
8479 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8480 {
8481  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8482 }
8483 
8484 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8485 {
8486  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8487 }
8488 
8489 template<typename T>
8490 static T* VmaAllocate(VmaAllocator hAllocator)
8491 {
8492  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8493 }
8494 
8495 template<typename T>
8496 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8497 {
8498  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8499 }
8500 
8501 template<typename T>
8502 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8503 {
8504  if(ptr != VMA_NULL)
8505  {
8506  ptr->~T();
8507  VmaFree(hAllocator, ptr);
8508  }
8509 }
8510 
8511 template<typename T>
8512 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8513 {
8514  if(ptr != VMA_NULL)
8515  {
8516  for(size_t i = count; i--; )
8517  ptr[i].~T();
8518  VmaFree(hAllocator, ptr);
8519  }
8520 }
8521 
8523 // VmaStringBuilder
8524 
8525 #if VMA_STATS_STRING_ENABLED
8526 
8527 class VmaStringBuilder
8528 {
8529 public:
8530  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8531  size_t GetLength() const { return m_Data.size(); }
8532  const char* GetData() const { return m_Data.data(); }
8533 
8534  void Add(char ch) { m_Data.push_back(ch); }
8535  void Add(const char* pStr);
8536  void AddNewLine() { Add('\n'); }
8537  void AddNumber(uint32_t num);
8538  void AddNumber(uint64_t num);
8539  void AddPointer(const void* ptr);
8540 
8541 private:
8542  VmaVector< char, VmaStlAllocator<char> > m_Data;
8543 };
8544 
8545 void VmaStringBuilder::Add(const char* pStr)
8546 {
8547  const size_t strLen = strlen(pStr);
8548  if(strLen > 0)
8549  {
8550  const size_t oldCount = m_Data.size();
8551  m_Data.resize(oldCount + strLen);
8552  memcpy(m_Data.data() + oldCount, pStr, strLen);
8553  }
8554 }
8555 
8556 void VmaStringBuilder::AddNumber(uint32_t num)
8557 {
8558  char buf[11];
8559  buf[10] = '\0';
8560  char *p = &buf[10];
8561  do
8562  {
8563  *--p = '0' + (num % 10);
8564  num /= 10;
8565  }
8566  while(num);
8567  Add(p);
8568 }
8569 
8570 void VmaStringBuilder::AddNumber(uint64_t num)
8571 {
8572  char buf[21];
8573  buf[20] = '\0';
8574  char *p = &buf[20];
8575  do
8576  {
8577  *--p = '0' + (num % 10);
8578  num /= 10;
8579  }
8580  while(num);
8581  Add(p);
8582 }
8583 
8584 void VmaStringBuilder::AddPointer(const void* ptr)
8585 {
8586  char buf[21];
8587  VmaPtrToStr(buf, sizeof(buf), ptr);
8588  Add(buf);
8589 }
8590 
8591 #endif // #if VMA_STATS_STRING_ENABLED
8592 
8594 // VmaJsonWriter
8595 
8596 #if VMA_STATS_STRING_ENABLED
8597 
8598 class VmaJsonWriter
8599 {
8600  VMA_CLASS_NO_COPY(VmaJsonWriter)
8601 public:
8602  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8603  ~VmaJsonWriter();
8604 
8605  void BeginObject(bool singleLine = false);
8606  void EndObject();
8607 
8608  void BeginArray(bool singleLine = false);
8609  void EndArray();
8610 
8611  void WriteString(const char* pStr);
8612  void BeginString(const char* pStr = VMA_NULL);
8613  void ContinueString(const char* pStr);
8614  void ContinueString(uint32_t n);
8615  void ContinueString(uint64_t n);
8616  void ContinueString_Pointer(const void* ptr);
8617  void EndString(const char* pStr = VMA_NULL);
8618 
8619  void WriteNumber(uint32_t n);
8620  void WriteNumber(uint64_t n);
8621  void WriteBool(bool b);
8622  void WriteNull();
8623 
8624 private:
8625  static const char* const INDENT;
8626 
8627  enum COLLECTION_TYPE
8628  {
8629  COLLECTION_TYPE_OBJECT,
8630  COLLECTION_TYPE_ARRAY,
8631  };
8632  struct StackItem
8633  {
8634  COLLECTION_TYPE type;
8635  uint32_t valueCount;
8636  bool singleLineMode;
8637  };
8638 
8639  VmaStringBuilder& m_SB;
8640  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8641  bool m_InsideString;
8642 
8643  void BeginValue(bool isString);
8644  void WriteIndent(bool oneLess = false);
8645 };
8646 
8647 const char* const VmaJsonWriter::INDENT = " ";
8648 
8649 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8650  m_SB(sb),
8651  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8652  m_InsideString(false)
8653 {
8654 }
8655 
8656 VmaJsonWriter::~VmaJsonWriter()
8657 {
8658  VMA_ASSERT(!m_InsideString);
8659  VMA_ASSERT(m_Stack.empty());
8660 }
8661 
8662 void VmaJsonWriter::BeginObject(bool singleLine)
8663 {
8664  VMA_ASSERT(!m_InsideString);
8665 
8666  BeginValue(false);
8667  m_SB.Add('{');
8668 
8669  StackItem item;
8670  item.type = COLLECTION_TYPE_OBJECT;
8671  item.valueCount = 0;
8672  item.singleLineMode = singleLine;
8673  m_Stack.push_back(item);
8674 }
8675 
8676 void VmaJsonWriter::EndObject()
8677 {
8678  VMA_ASSERT(!m_InsideString);
8679 
8680  WriteIndent(true);
8681  m_SB.Add('}');
8682 
8683  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8684  m_Stack.pop_back();
8685 }
8686 
8687 void VmaJsonWriter::BeginArray(bool singleLine)
8688 {
8689  VMA_ASSERT(!m_InsideString);
8690 
8691  BeginValue(false);
8692  m_SB.Add('[');
8693 
8694  StackItem item;
8695  item.type = COLLECTION_TYPE_ARRAY;
8696  item.valueCount = 0;
8697  item.singleLineMode = singleLine;
8698  m_Stack.push_back(item);
8699 }
8700 
8701 void VmaJsonWriter::EndArray()
8702 {
8703  VMA_ASSERT(!m_InsideString);
8704 
8705  WriteIndent(true);
8706  m_SB.Add(']');
8707 
8708  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8709  m_Stack.pop_back();
8710 }
8711 
8712 void VmaJsonWriter::WriteString(const char* pStr)
8713 {
8714  BeginString(pStr);
8715  EndString();
8716 }
8717 
8718 void VmaJsonWriter::BeginString(const char* pStr)
8719 {
8720  VMA_ASSERT(!m_InsideString);
8721 
8722  BeginValue(true);
8723  m_SB.Add('"');
8724  m_InsideString = true;
8725  if(pStr != VMA_NULL && pStr[0] != '\0')
8726  {
8727  ContinueString(pStr);
8728  }
8729 }
8730 
8731 void VmaJsonWriter::ContinueString(const char* pStr)
8732 {
8733  VMA_ASSERT(m_InsideString);
8734 
8735  const size_t strLen = strlen(pStr);
8736  for(size_t i = 0; i < strLen; ++i)
8737  {
8738  char ch = pStr[i];
8739  if(ch == '\\')
8740  {
8741  m_SB.Add("\\\\");
8742  }
8743  else if(ch == '"')
8744  {
8745  m_SB.Add("\\\"");
8746  }
8747  else if(ch >= 32)
8748  {
8749  m_SB.Add(ch);
8750  }
8751  else switch(ch)
8752  {
8753  case '\b':
8754  m_SB.Add("\\b");
8755  break;
8756  case '\f':
8757  m_SB.Add("\\f");
8758  break;
8759  case '\n':
8760  m_SB.Add("\\n");
8761  break;
8762  case '\r':
8763  m_SB.Add("\\r");
8764  break;
8765  case '\t':
8766  m_SB.Add("\\t");
8767  break;
8768  default:
8769  VMA_ASSERT(0 && "Character not currently supported.");
8770  break;
8771  }
8772  }
8773 }
8774 
8775 void VmaJsonWriter::ContinueString(uint32_t n)
8776 {
8777  VMA_ASSERT(m_InsideString);
8778  m_SB.AddNumber(n);
8779 }
8780 
8781 void VmaJsonWriter::ContinueString(uint64_t n)
8782 {
8783  VMA_ASSERT(m_InsideString);
8784  m_SB.AddNumber(n);
8785 }
8786 
8787 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8788 {
8789  VMA_ASSERT(m_InsideString);
8790  m_SB.AddPointer(ptr);
8791 }
8792 
8793 void VmaJsonWriter::EndString(const char* pStr)
8794 {
8795  VMA_ASSERT(m_InsideString);
8796  if(pStr != VMA_NULL && pStr[0] != '\0')
8797  {
8798  ContinueString(pStr);
8799  }
8800  m_SB.Add('"');
8801  m_InsideString = false;
8802 }
8803 
8804 void VmaJsonWriter::WriteNumber(uint32_t n)
8805 {
8806  VMA_ASSERT(!m_InsideString);
8807  BeginValue(false);
8808  m_SB.AddNumber(n);
8809 }
8810 
8811 void VmaJsonWriter::WriteNumber(uint64_t n)
8812 {
8813  VMA_ASSERT(!m_InsideString);
8814  BeginValue(false);
8815  m_SB.AddNumber(n);
8816 }
8817 
8818 void VmaJsonWriter::WriteBool(bool b)
8819 {
8820  VMA_ASSERT(!m_InsideString);
8821  BeginValue(false);
8822  m_SB.Add(b ? "true" : "false");
8823 }
8824 
8825 void VmaJsonWriter::WriteNull()
8826 {
8827  VMA_ASSERT(!m_InsideString);
8828  BeginValue(false);
8829  m_SB.Add("null");
8830 }
8831 
8832 void VmaJsonWriter::BeginValue(bool isString)
8833 {
8834  if(!m_Stack.empty())
8835  {
8836  StackItem& currItem = m_Stack.back();
8837  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8838  currItem.valueCount % 2 == 0)
8839  {
8840  VMA_ASSERT(isString);
8841  }
8842 
8843  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8844  currItem.valueCount % 2 != 0)
8845  {
8846  m_SB.Add(": ");
8847  }
8848  else if(currItem.valueCount > 0)
8849  {
8850  m_SB.Add(", ");
8851  WriteIndent();
8852  }
8853  else
8854  {
8855  WriteIndent();
8856  }
8857  ++currItem.valueCount;
8858  }
8859 }
8860 
8861 void VmaJsonWriter::WriteIndent(bool oneLess)
8862 {
8863  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8864  {
8865  m_SB.AddNewLine();
8866 
8867  size_t count = m_Stack.size();
8868  if(count > 0 && oneLess)
8869  {
8870  --count;
8871  }
8872  for(size_t i = 0; i < count; ++i)
8873  {
8874  m_SB.Add(INDENT);
8875  }
8876  }
8877 }
8878 
8879 #endif // #if VMA_STATS_STRING_ENABLED
8880 
8882 
8883 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8884 {
8885  if(IsUserDataString())
8886  {
8887  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8888 
8889  FreeUserDataString(hAllocator);
8890 
8891  if(pUserData != VMA_NULL)
8892  {
8893  m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8894  }
8895  }
8896  else
8897  {
8898  m_pUserData = pUserData;
8899  }
8900 }
8901 
8902 void VmaAllocation_T::ChangeBlockAllocation(
8903  VmaAllocator hAllocator,
8904  VmaDeviceMemoryBlock* block,
8905  VkDeviceSize offset)
8906 {
8907  VMA_ASSERT(block != VMA_NULL);
8908  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8909 
8910  // Move mapping reference counter from old block to new block.
8911  if(block != m_BlockAllocation.m_Block)
8912  {
8913  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8914  if(IsPersistentMap())
8915  ++mapRefCount;
8916  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8917  block->Map(hAllocator, mapRefCount, VMA_NULL);
8918  }
8919 
8920  m_BlockAllocation.m_Block = block;
8921  m_BlockAllocation.m_Offset = offset;
8922 }
8923 
8924 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8925 {
8926  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8927  m_BlockAllocation.m_Offset = newOffset;
8928 }
8929 
8930 VkDeviceSize VmaAllocation_T::GetOffset() const
8931 {
8932  switch(m_Type)
8933  {
8934  case ALLOCATION_TYPE_BLOCK:
8935  return m_BlockAllocation.m_Offset;
8936  case ALLOCATION_TYPE_DEDICATED:
8937  return 0;
8938  default:
8939  VMA_ASSERT(0);
8940  return 0;
8941  }
8942 }
8943 
8944 VkDeviceMemory VmaAllocation_T::GetMemory() const
8945 {
8946  switch(m_Type)
8947  {
8948  case ALLOCATION_TYPE_BLOCK:
8949  return m_BlockAllocation.m_Block->GetDeviceMemory();
8950  case ALLOCATION_TYPE_DEDICATED:
8951  return m_DedicatedAllocation.m_hMemory;
8952  default:
8953  VMA_ASSERT(0);
8954  return VK_NULL_HANDLE;
8955  }
8956 }
8957 
8958 void* VmaAllocation_T::GetMappedData() const
8959 {
8960  switch(m_Type)
8961  {
8962  case ALLOCATION_TYPE_BLOCK:
8963  if(m_MapCount != 0)
8964  {
8965  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8966  VMA_ASSERT(pBlockData != VMA_NULL);
8967  return (char*)pBlockData + m_BlockAllocation.m_Offset;
8968  }
8969  else
8970  {
8971  return VMA_NULL;
8972  }
8973  break;
8974  case ALLOCATION_TYPE_DEDICATED:
8975  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8976  return m_DedicatedAllocation.m_pMappedData;
8977  default:
8978  VMA_ASSERT(0);
8979  return VMA_NULL;
8980  }
8981 }
8982 
8983 bool VmaAllocation_T::CanBecomeLost() const
8984 {
8985  switch(m_Type)
8986  {
8987  case ALLOCATION_TYPE_BLOCK:
8988  return m_BlockAllocation.m_CanBecomeLost;
8989  case ALLOCATION_TYPE_DEDICATED:
8990  return false;
8991  default:
8992  VMA_ASSERT(0);
8993  return false;
8994  }
8995 }
8996 
8997 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8998 {
8999  VMA_ASSERT(CanBecomeLost());
9000 
9001  /*
9002  Warning: This is a carefully designed algorithm.
9003  Do not modify unless you really know what you're doing :)
9004  */
9005  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
9006  for(;;)
9007  {
9008  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
9009  {
9010  VMA_ASSERT(0);
9011  return false;
9012  }
9013  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
9014  {
9015  return false;
9016  }
9017  else // Last use time earlier than current time.
9018  {
9019  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
9020  {
9021  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
9022  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
9023  return true;
9024  }
9025  }
9026  }
9027 }
9028 
9029 #if VMA_STATS_STRING_ENABLED
9030 
9031 // Correspond to values of enum VmaSuballocationType.
9032 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
9033  "FREE",
9034  "UNKNOWN",
9035  "BUFFER",
9036  "IMAGE_UNKNOWN",
9037  "IMAGE_LINEAR",
9038  "IMAGE_OPTIMAL",
9039 };
9040 
9041 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
9042 {
9043  json.WriteString("Type");
9044  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
9045 
9046  json.WriteString("Size");
9047  json.WriteNumber(m_Size);
9048 
9049  if(m_pUserData != VMA_NULL)
9050  {
9051  json.WriteString("UserData");
9052  if(IsUserDataString())
9053  {
9054  json.WriteString((const char*)m_pUserData);
9055  }
9056  else
9057  {
9058  json.BeginString();
9059  json.ContinueString_Pointer(m_pUserData);
9060  json.EndString();
9061  }
9062  }
9063 
9064  json.WriteString("CreationFrameIndex");
9065  json.WriteNumber(m_CreationFrameIndex);
9066 
9067  json.WriteString("LastUseFrameIndex");
9068  json.WriteNumber(GetLastUseFrameIndex());
9069 
9070  if(m_BufferImageUsage != 0)
9071  {
9072  json.WriteString("Usage");
9073  json.WriteNumber(m_BufferImageUsage);
9074  }
9075 }
9076 
9077 #endif
9078 
9079 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
9080 {
9081  VMA_ASSERT(IsUserDataString());
9082  VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
9083  m_pUserData = VMA_NULL;
9084 }
9085 
9086 void VmaAllocation_T::BlockAllocMap()
9087 {
9088  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9089 
9090  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9091  {
9092  ++m_MapCount;
9093  }
9094  else
9095  {
9096  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
9097  }
9098 }
9099 
9100 void VmaAllocation_T::BlockAllocUnmap()
9101 {
9102  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9103 
9104  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9105  {
9106  --m_MapCount;
9107  }
9108  else
9109  {
9110  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
9111  }
9112 }
9113 
9114 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
9115 {
9116  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9117 
9118  if(m_MapCount != 0)
9119  {
9120  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9121  {
9122  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
9123  *ppData = m_DedicatedAllocation.m_pMappedData;
9124  ++m_MapCount;
9125  return VK_SUCCESS;
9126  }
9127  else
9128  {
9129  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
9130  return VK_ERROR_MEMORY_MAP_FAILED;
9131  }
9132  }
9133  else
9134  {
9135  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
9136  hAllocator->m_hDevice,
9137  m_DedicatedAllocation.m_hMemory,
9138  0, // offset
9139  VK_WHOLE_SIZE,
9140  0, // flags
9141  ppData);
9142  if(result == VK_SUCCESS)
9143  {
9144  m_DedicatedAllocation.m_pMappedData = *ppData;
9145  m_MapCount = 1;
9146  }
9147  return result;
9148  }
9149 }
9150 
9151 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
9152 {
9153  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9154 
9155  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9156  {
9157  --m_MapCount;
9158  if(m_MapCount == 0)
9159  {
9160  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
9161  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
9162  hAllocator->m_hDevice,
9163  m_DedicatedAllocation.m_hMemory);
9164  }
9165  }
9166  else
9167  {
9168  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
9169  }
9170 }
9171 
9172 #if VMA_STATS_STRING_ENABLED
9173 
9174 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
9175 {
9176  json.BeginObject();
9177 
9178  json.WriteString("Blocks");
9179  json.WriteNumber(stat.blockCount);
9180 
9181  json.WriteString("Allocations");
9182  json.WriteNumber(stat.allocationCount);
9183 
9184  json.WriteString("UnusedRanges");
9185  json.WriteNumber(stat.unusedRangeCount);
9186 
9187  json.WriteString("UsedBytes");
9188  json.WriteNumber(stat.usedBytes);
9189 
9190  json.WriteString("UnusedBytes");
9191  json.WriteNumber(stat.unusedBytes);
9192 
9193  if(stat.allocationCount > 1)
9194  {
9195  json.WriteString("AllocationSize");
9196  json.BeginObject(true);
9197  json.WriteString("Min");
9198  json.WriteNumber(stat.allocationSizeMin);
9199  json.WriteString("Avg");
9200  json.WriteNumber(stat.allocationSizeAvg);
9201  json.WriteString("Max");
9202  json.WriteNumber(stat.allocationSizeMax);
9203  json.EndObject();
9204  }
9205 
9206  if(stat.unusedRangeCount > 1)
9207  {
9208  json.WriteString("UnusedRangeSize");
9209  json.BeginObject(true);
9210  json.WriteString("Min");
9211  json.WriteNumber(stat.unusedRangeSizeMin);
9212  json.WriteString("Avg");
9213  json.WriteNumber(stat.unusedRangeSizeAvg);
9214  json.WriteString("Max");
9215  json.WriteNumber(stat.unusedRangeSizeMax);
9216  json.EndObject();
9217  }
9218 
9219  json.EndObject();
9220 }
9221 
9222 #endif // #if VMA_STATS_STRING_ENABLED
9223 
9224 struct VmaSuballocationItemSizeLess
9225 {
9226  bool operator()(
9227  const VmaSuballocationList::iterator lhs,
9228  const VmaSuballocationList::iterator rhs) const
9229  {
9230  return lhs->size < rhs->size;
9231  }
9232  bool operator()(
9233  const VmaSuballocationList::iterator lhs,
9234  VkDeviceSize rhsSize) const
9235  {
9236  return lhs->size < rhsSize;
9237  }
9238 };
9239 
9240 
9242 // class VmaBlockMetadata
9243 
9244 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
9245  m_Size(0),
9246  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
9247 {
9248 }
9249 
9250 #if VMA_STATS_STRING_ENABLED
9251 
9252 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
9253  VkDeviceSize unusedBytes,
9254  size_t allocationCount,
9255  size_t unusedRangeCount) const
9256 {
9257  json.BeginObject();
9258 
9259  json.WriteString("TotalBytes");
9260  json.WriteNumber(GetSize());
9261 
9262  json.WriteString("UnusedBytes");
9263  json.WriteNumber(unusedBytes);
9264 
9265  json.WriteString("Allocations");
9266  json.WriteNumber((uint64_t)allocationCount);
9267 
9268  json.WriteString("UnusedRanges");
9269  json.WriteNumber((uint64_t)unusedRangeCount);
9270 
9271  json.WriteString("Suballocations");
9272  json.BeginArray();
9273 }
9274 
9275 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
9276  VkDeviceSize offset,
9277  VmaAllocation hAllocation) const
9278 {
9279  json.BeginObject(true);
9280 
9281  json.WriteString("Offset");
9282  json.WriteNumber(offset);
9283 
9284  hAllocation->PrintParameters(json);
9285 
9286  json.EndObject();
9287 }
9288 
9289 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
9290  VkDeviceSize offset,
9291  VkDeviceSize size) const
9292 {
9293  json.BeginObject(true);
9294 
9295  json.WriteString("Offset");
9296  json.WriteNumber(offset);
9297 
9298  json.WriteString("Type");
9299  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
9300 
9301  json.WriteString("Size");
9302  json.WriteNumber(size);
9303 
9304  json.EndObject();
9305 }
9306 
9307 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
9308 {
9309  json.EndArray();
9310  json.EndObject();
9311 }
9312 
9313 #endif // #if VMA_STATS_STRING_ENABLED
9314 
9316 // class VmaBlockMetadata_Generic
9317 
9318 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
9319  VmaBlockMetadata(hAllocator),
9320  m_FreeCount(0),
9321  m_SumFreeSize(0),
9322  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9323  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
9324 {
9325 }
9326 
9327 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
9328 {
9329 }
9330 
9331 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
9332 {
9333  VmaBlockMetadata::Init(size);
9334 
9335  m_FreeCount = 1;
9336  m_SumFreeSize = size;
9337 
9338  VmaSuballocation suballoc = {};
9339  suballoc.offset = 0;
9340  suballoc.size = size;
9341  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9342  suballoc.hAllocation = VK_NULL_HANDLE;
9343 
9344  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9345  m_Suballocations.push_back(suballoc);
9346  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
9347  --suballocItem;
9348  m_FreeSuballocationsBySize.push_back(suballocItem);
9349 }
9350 
9351 bool VmaBlockMetadata_Generic::Validate() const
9352 {
9353  VMA_VALIDATE(!m_Suballocations.empty());
9354 
9355  // Expected offset of new suballocation as calculated from previous ones.
9356  VkDeviceSize calculatedOffset = 0;
9357  // Expected number of free suballocations as calculated from traversing their list.
9358  uint32_t calculatedFreeCount = 0;
9359  // Expected sum size of free suballocations as calculated from traversing their list.
9360  VkDeviceSize calculatedSumFreeSize = 0;
9361  // Expected number of free suballocations that should be registered in
9362  // m_FreeSuballocationsBySize calculated from traversing their list.
9363  size_t freeSuballocationsToRegister = 0;
9364  // True if previous visited suballocation was free.
9365  bool prevFree = false;
9366 
9367  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9368  suballocItem != m_Suballocations.cend();
9369  ++suballocItem)
9370  {
9371  const VmaSuballocation& subAlloc = *suballocItem;
9372 
9373  // Actual offset of this suballocation doesn't match expected one.
9374  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
9375 
9376  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
9377  // Two adjacent free suballocations are invalid. They should be merged.
9378  VMA_VALIDATE(!prevFree || !currFree);
9379 
9380  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
9381 
9382  if(currFree)
9383  {
9384  calculatedSumFreeSize += subAlloc.size;
9385  ++calculatedFreeCount;
9386  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9387  {
9388  ++freeSuballocationsToRegister;
9389  }
9390 
9391  // Margin required between allocations - every free space must be at least that large.
9392  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
9393  }
9394  else
9395  {
9396  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
9397  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
9398 
9399  // Margin required between allocations - previous allocation must be free.
9400  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
9401  }
9402 
9403  calculatedOffset += subAlloc.size;
9404  prevFree = currFree;
9405  }
9406 
9407  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9408  // match expected one.
9409  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9410 
9411  VkDeviceSize lastSize = 0;
9412  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9413  {
9414  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9415 
9416  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9417  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9418  // They must be sorted by size ascending.
9419  VMA_VALIDATE(suballocItem->size >= lastSize);
9420 
9421  lastSize = suballocItem->size;
9422  }
9423 
9424  // Check if totals match calculated values.
9425  VMA_VALIDATE(ValidateFreeSuballocationList());
9426  VMA_VALIDATE(calculatedOffset == GetSize());
9427  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9428  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9429 
9430  return true;
9431 }
9432 
9433 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9434 {
9435  if(!m_FreeSuballocationsBySize.empty())
9436  {
9437  return m_FreeSuballocationsBySize.back()->size;
9438  }
9439  else
9440  {
9441  return 0;
9442  }
9443 }
9444 
9445 bool VmaBlockMetadata_Generic::IsEmpty() const
9446 {
9447  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9448 }
9449 
9450 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9451 {
9452  outInfo.blockCount = 1;
9453 
9454  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9455  outInfo.allocationCount = rangeCount - m_FreeCount;
9456  outInfo.unusedRangeCount = m_FreeCount;
9457 
9458  outInfo.unusedBytes = m_SumFreeSize;
9459  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9460 
9461  outInfo.allocationSizeMin = UINT64_MAX;
9462  outInfo.allocationSizeMax = 0;
9463  outInfo.unusedRangeSizeMin = UINT64_MAX;
9464  outInfo.unusedRangeSizeMax = 0;
9465 
9466  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9467  suballocItem != m_Suballocations.cend();
9468  ++suballocItem)
9469  {
9470  const VmaSuballocation& suballoc = *suballocItem;
9471  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9472  {
9473  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9474  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9475  }
9476  else
9477  {
9478  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9479  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9480  }
9481  }
9482 }
9483 
9484 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9485 {
9486  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9487 
9488  inoutStats.size += GetSize();
9489  inoutStats.unusedSize += m_SumFreeSize;
9490  inoutStats.allocationCount += rangeCount - m_FreeCount;
9491  inoutStats.unusedRangeCount += m_FreeCount;
9492  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9493 }
9494 
9495 #if VMA_STATS_STRING_ENABLED
9496 
9497 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9498 {
9499  PrintDetailedMap_Begin(json,
9500  m_SumFreeSize, // unusedBytes
9501  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9502  m_FreeCount); // unusedRangeCount
9503 
9504  size_t i = 0;
9505  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9506  suballocItem != m_Suballocations.cend();
9507  ++suballocItem, ++i)
9508  {
9509  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9510  {
9511  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9512  }
9513  else
9514  {
9515  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9516  }
9517  }
9518 
9519  PrintDetailedMap_End(json);
9520 }
9521 
9522 #endif // #if VMA_STATS_STRING_ENABLED
9523 
9524 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9525  uint32_t currentFrameIndex,
9526  uint32_t frameInUseCount,
9527  VkDeviceSize bufferImageGranularity,
9528  VkDeviceSize allocSize,
9529  VkDeviceSize allocAlignment,
9530  bool upperAddress,
9531  VmaSuballocationType allocType,
9532  bool canMakeOtherLost,
9533  uint32_t strategy,
9534  VmaAllocationRequest* pAllocationRequest)
9535 {
9536  VMA_ASSERT(allocSize > 0);
9537  VMA_ASSERT(!upperAddress);
9538  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9539  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9540  VMA_HEAVY_ASSERT(Validate());
9541 
9542  pAllocationRequest->type = VmaAllocationRequestType::Normal;
9543 
9544  // There is not enough total free space in this block to fullfill the request: Early return.
9545  if(canMakeOtherLost == false &&
9546  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9547  {
9548  return false;
9549  }
9550 
9551  // New algorithm, efficiently searching freeSuballocationsBySize.
9552  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9553  if(freeSuballocCount > 0)
9554  {
9556  {
9557  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9558  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9559  m_FreeSuballocationsBySize.data(),
9560  m_FreeSuballocationsBySize.data() + freeSuballocCount,
9561  allocSize + 2 * VMA_DEBUG_MARGIN,
9562  VmaSuballocationItemSizeLess());
9563  size_t index = it - m_FreeSuballocationsBySize.data();
9564  for(; index < freeSuballocCount; ++index)
9565  {
9566  if(CheckAllocation(
9567  currentFrameIndex,
9568  frameInUseCount,
9569  bufferImageGranularity,
9570  allocSize,
9571  allocAlignment,
9572  allocType,
9573  m_FreeSuballocationsBySize[index],
9574  false, // canMakeOtherLost
9575  &pAllocationRequest->offset,
9576  &pAllocationRequest->itemsToMakeLostCount,
9577  &pAllocationRequest->sumFreeSize,
9578  &pAllocationRequest->sumItemSize))
9579  {
9580  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9581  return true;
9582  }
9583  }
9584  }
9585  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9586  {
9587  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9588  it != m_Suballocations.end();
9589  ++it)
9590  {
9591  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9592  currentFrameIndex,
9593  frameInUseCount,
9594  bufferImageGranularity,
9595  allocSize,
9596  allocAlignment,
9597  allocType,
9598  it,
9599  false, // canMakeOtherLost
9600  &pAllocationRequest->offset,
9601  &pAllocationRequest->itemsToMakeLostCount,
9602  &pAllocationRequest->sumFreeSize,
9603  &pAllocationRequest->sumItemSize))
9604  {
9605  pAllocationRequest->item = it;
9606  return true;
9607  }
9608  }
9609  }
9610  else // WORST_FIT, FIRST_FIT
9611  {
9612  // Search staring from biggest suballocations.
9613  for(size_t index = freeSuballocCount; index--; )
9614  {
9615  if(CheckAllocation(
9616  currentFrameIndex,
9617  frameInUseCount,
9618  bufferImageGranularity,
9619  allocSize,
9620  allocAlignment,
9621  allocType,
9622  m_FreeSuballocationsBySize[index],
9623  false, // canMakeOtherLost
9624  &pAllocationRequest->offset,
9625  &pAllocationRequest->itemsToMakeLostCount,
9626  &pAllocationRequest->sumFreeSize,
9627  &pAllocationRequest->sumItemSize))
9628  {
9629  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9630  return true;
9631  }
9632  }
9633  }
9634  }
9635 
9636  if(canMakeOtherLost)
9637  {
9638  // Brute-force algorithm. TODO: Come up with something better.
9639 
9640  bool found = false;
9641  VmaAllocationRequest tmpAllocRequest = {};
9642  tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9643  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9644  suballocIt != m_Suballocations.end();
9645  ++suballocIt)
9646  {
9647  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9648  suballocIt->hAllocation->CanBecomeLost())
9649  {
9650  if(CheckAllocation(
9651  currentFrameIndex,
9652  frameInUseCount,
9653  bufferImageGranularity,
9654  allocSize,
9655  allocAlignment,
9656  allocType,
9657  suballocIt,
9658  canMakeOtherLost,
9659  &tmpAllocRequest.offset,
9660  &tmpAllocRequest.itemsToMakeLostCount,
9661  &tmpAllocRequest.sumFreeSize,
9662  &tmpAllocRequest.sumItemSize))
9663  {
9665  {
9666  *pAllocationRequest = tmpAllocRequest;
9667  pAllocationRequest->item = suballocIt;
9668  break;
9669  }
9670  if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9671  {
9672  *pAllocationRequest = tmpAllocRequest;
9673  pAllocationRequest->item = suballocIt;
9674  found = true;
9675  }
9676  }
9677  }
9678  }
9679 
9680  return found;
9681  }
9682 
9683  return false;
9684 }
9685 
9686 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9687  uint32_t currentFrameIndex,
9688  uint32_t frameInUseCount,
9689  VmaAllocationRequest* pAllocationRequest)
9690 {
9691  VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9692 
9693  while(pAllocationRequest->itemsToMakeLostCount > 0)
9694  {
9695  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9696  {
9697  ++pAllocationRequest->item;
9698  }
9699  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9700  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9701  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9702  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9703  {
9704  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9705  --pAllocationRequest->itemsToMakeLostCount;
9706  }
9707  else
9708  {
9709  return false;
9710  }
9711  }
9712 
9713  VMA_HEAVY_ASSERT(Validate());
9714  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9715  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9716 
9717  return true;
9718 }
9719 
9720 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9721 {
9722  uint32_t lostAllocationCount = 0;
9723  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9724  it != m_Suballocations.end();
9725  ++it)
9726  {
9727  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9728  it->hAllocation->CanBecomeLost() &&
9729  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9730  {
9731  it = FreeSuballocation(it);
9732  ++lostAllocationCount;
9733  }
9734  }
9735  return lostAllocationCount;
9736 }
9737 
9738 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9739 {
9740  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9741  it != m_Suballocations.end();
9742  ++it)
9743  {
9744  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9745  {
9746  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9747  {
9748  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9749  return VK_ERROR_VALIDATION_FAILED_EXT;
9750  }
9751  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9752  {
9753  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9754  return VK_ERROR_VALIDATION_FAILED_EXT;
9755  }
9756  }
9757  }
9758 
9759  return VK_SUCCESS;
9760 }
9761 
9762 void VmaBlockMetadata_Generic::Alloc(
9763  const VmaAllocationRequest& request,
9764  VmaSuballocationType type,
9765  VkDeviceSize allocSize,
9766  VmaAllocation hAllocation)
9767 {
9768  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9769  VMA_ASSERT(request.item != m_Suballocations.end());
9770  VmaSuballocation& suballoc = *request.item;
9771  // Given suballocation is a free block.
9772  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9773  // Given offset is inside this suballocation.
9774  VMA_ASSERT(request.offset >= suballoc.offset);
9775  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9776  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9777  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9778 
9779  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9780  // it to become used.
9781  UnregisterFreeSuballocation(request.item);
9782 
9783  suballoc.offset = request.offset;
9784  suballoc.size = allocSize;
9785  suballoc.type = type;
9786  suballoc.hAllocation = hAllocation;
9787 
9788  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9789  if(paddingEnd)
9790  {
9791  VmaSuballocation paddingSuballoc = {};
9792  paddingSuballoc.offset = request.offset + allocSize;
9793  paddingSuballoc.size = paddingEnd;
9794  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9795  VmaSuballocationList::iterator next = request.item;
9796  ++next;
9797  const VmaSuballocationList::iterator paddingEndItem =
9798  m_Suballocations.insert(next, paddingSuballoc);
9799  RegisterFreeSuballocation(paddingEndItem);
9800  }
9801 
9802  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9803  if(paddingBegin)
9804  {
9805  VmaSuballocation paddingSuballoc = {};
9806  paddingSuballoc.offset = request.offset - paddingBegin;
9807  paddingSuballoc.size = paddingBegin;
9808  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9809  const VmaSuballocationList::iterator paddingBeginItem =
9810  m_Suballocations.insert(request.item, paddingSuballoc);
9811  RegisterFreeSuballocation(paddingBeginItem);
9812  }
9813 
9814  // Update totals.
9815  m_FreeCount = m_FreeCount - 1;
9816  if(paddingBegin > 0)
9817  {
9818  ++m_FreeCount;
9819  }
9820  if(paddingEnd > 0)
9821  {
9822  ++m_FreeCount;
9823  }
9824  m_SumFreeSize -= allocSize;
9825 }
9826 
9827 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9828 {
9829  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9830  suballocItem != m_Suballocations.end();
9831  ++suballocItem)
9832  {
9833  VmaSuballocation& suballoc = *suballocItem;
9834  if(suballoc.hAllocation == allocation)
9835  {
9836  FreeSuballocation(suballocItem);
9837  VMA_HEAVY_ASSERT(Validate());
9838  return;
9839  }
9840  }
9841  VMA_ASSERT(0 && "Not found!");
9842 }
9843 
9844 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9845 {
9846  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9847  suballocItem != m_Suballocations.end();
9848  ++suballocItem)
9849  {
9850  VmaSuballocation& suballoc = *suballocItem;
9851  if(suballoc.offset == offset)
9852  {
9853  FreeSuballocation(suballocItem);
9854  return;
9855  }
9856  }
9857  VMA_ASSERT(0 && "Not found!");
9858 }
9859 
9860 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9861 {
9862  VkDeviceSize lastSize = 0;
9863  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9864  {
9865  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9866 
9867  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9868  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9869  VMA_VALIDATE(it->size >= lastSize);
9870  lastSize = it->size;
9871  }
9872  return true;
9873 }
9874 
9875 bool VmaBlockMetadata_Generic::CheckAllocation(
9876  uint32_t currentFrameIndex,
9877  uint32_t frameInUseCount,
9878  VkDeviceSize bufferImageGranularity,
9879  VkDeviceSize allocSize,
9880  VkDeviceSize allocAlignment,
9881  VmaSuballocationType allocType,
9882  VmaSuballocationList::const_iterator suballocItem,
9883  bool canMakeOtherLost,
9884  VkDeviceSize* pOffset,
9885  size_t* itemsToMakeLostCount,
9886  VkDeviceSize* pSumFreeSize,
9887  VkDeviceSize* pSumItemSize) const
9888 {
9889  VMA_ASSERT(allocSize > 0);
9890  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9891  VMA_ASSERT(suballocItem != m_Suballocations.cend());
9892  VMA_ASSERT(pOffset != VMA_NULL);
9893 
9894  *itemsToMakeLostCount = 0;
9895  *pSumFreeSize = 0;
9896  *pSumItemSize = 0;
9897 
9898  if(canMakeOtherLost)
9899  {
9900  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9901  {
9902  *pSumFreeSize = suballocItem->size;
9903  }
9904  else
9905  {
9906  if(suballocItem->hAllocation->CanBecomeLost() &&
9907  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9908  {
9909  ++*itemsToMakeLostCount;
9910  *pSumItemSize = suballocItem->size;
9911  }
9912  else
9913  {
9914  return false;
9915  }
9916  }
9917 
9918  // Remaining size is too small for this request: Early return.
9919  if(GetSize() - suballocItem->offset < allocSize)
9920  {
9921  return false;
9922  }
9923 
9924  // Start from offset equal to beginning of this suballocation.
9925  *pOffset = suballocItem->offset;
9926 
9927  // Apply VMA_DEBUG_MARGIN at the beginning.
9928  if(VMA_DEBUG_MARGIN > 0)
9929  {
9930  *pOffset += VMA_DEBUG_MARGIN;
9931  }
9932 
9933  // Apply alignment.
9934  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9935 
9936  // Check previous suballocations for BufferImageGranularity conflicts.
9937  // Make bigger alignment if necessary.
9938  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
9939  {
9940  bool bufferImageGranularityConflict = false;
9941  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9942  while(prevSuballocItem != m_Suballocations.cbegin())
9943  {
9944  --prevSuballocItem;
9945  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9946  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9947  {
9948  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9949  {
9950  bufferImageGranularityConflict = true;
9951  break;
9952  }
9953  }
9954  else
9955  // Already on previous page.
9956  break;
9957  }
9958  if(bufferImageGranularityConflict)
9959  {
9960  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9961  }
9962  }
9963 
9964  // Now that we have final *pOffset, check if we are past suballocItem.
9965  // If yes, return false - this function should be called for another suballocItem as starting point.
9966  if(*pOffset >= suballocItem->offset + suballocItem->size)
9967  {
9968  return false;
9969  }
9970 
9971  // Calculate padding at the beginning based on current offset.
9972  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9973 
9974  // Calculate required margin at the end.
9975  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9976 
9977  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9978  // Another early return check.
9979  if(suballocItem->offset + totalSize > GetSize())
9980  {
9981  return false;
9982  }
9983 
9984  // Advance lastSuballocItem until desired size is reached.
9985  // Update itemsToMakeLostCount.
9986  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9987  if(totalSize > suballocItem->size)
9988  {
9989  VkDeviceSize remainingSize = totalSize - suballocItem->size;
9990  while(remainingSize > 0)
9991  {
9992  ++lastSuballocItem;
9993  if(lastSuballocItem == m_Suballocations.cend())
9994  {
9995  return false;
9996  }
9997  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9998  {
9999  *pSumFreeSize += lastSuballocItem->size;
10000  }
10001  else
10002  {
10003  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
10004  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
10005  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10006  {
10007  ++*itemsToMakeLostCount;
10008  *pSumItemSize += lastSuballocItem->size;
10009  }
10010  else
10011  {
10012  return false;
10013  }
10014  }
10015  remainingSize = (lastSuballocItem->size < remainingSize) ?
10016  remainingSize - lastSuballocItem->size : 0;
10017  }
10018  }
10019 
10020  // Check next suballocations for BufferImageGranularity conflicts.
10021  // If conflict exists, we must mark more allocations lost or fail.
10022  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
10023  {
10024  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
10025  ++nextSuballocItem;
10026  while(nextSuballocItem != m_Suballocations.cend())
10027  {
10028  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
10029  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10030  {
10031  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10032  {
10033  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
10034  if(nextSuballoc.hAllocation->CanBecomeLost() &&
10035  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10036  {
10037  ++*itemsToMakeLostCount;
10038  }
10039  else
10040  {
10041  return false;
10042  }
10043  }
10044  }
10045  else
10046  {
10047  // Already on next page.
10048  break;
10049  }
10050  ++nextSuballocItem;
10051  }
10052  }
10053  }
10054  else
10055  {
10056  const VmaSuballocation& suballoc = *suballocItem;
10057  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10058 
10059  *pSumFreeSize = suballoc.size;
10060 
10061  // Size of this suballocation is too small for this request: Early return.
10062  if(suballoc.size < allocSize)
10063  {
10064  return false;
10065  }
10066 
10067  // Start from offset equal to beginning of this suballocation.
10068  *pOffset = suballoc.offset;
10069 
10070  // Apply VMA_DEBUG_MARGIN at the beginning.
10071  if(VMA_DEBUG_MARGIN > 0)
10072  {
10073  *pOffset += VMA_DEBUG_MARGIN;
10074  }
10075 
10076  // Apply alignment.
10077  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
10078 
10079  // Check previous suballocations for BufferImageGranularity conflicts.
10080  // Make bigger alignment if necessary.
10081  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
10082  {
10083  bool bufferImageGranularityConflict = false;
10084  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
10085  while(prevSuballocItem != m_Suballocations.cbegin())
10086  {
10087  --prevSuballocItem;
10088  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
10089  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
10090  {
10091  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10092  {
10093  bufferImageGranularityConflict = true;
10094  break;
10095  }
10096  }
10097  else
10098  // Already on previous page.
10099  break;
10100  }
10101  if(bufferImageGranularityConflict)
10102  {
10103  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
10104  }
10105  }
10106 
10107  // Calculate padding at the beginning based on current offset.
10108  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
10109 
10110  // Calculate required margin at the end.
10111  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
10112 
10113  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
10114  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
10115  {
10116  return false;
10117  }
10118 
10119  // Check next suballocations for BufferImageGranularity conflicts.
10120  // If conflict exists, allocation cannot be made here.
10121  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
10122  {
10123  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
10124  ++nextSuballocItem;
10125  while(nextSuballocItem != m_Suballocations.cend())
10126  {
10127  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
10128  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10129  {
10130  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10131  {
10132  return false;
10133  }
10134  }
10135  else
10136  {
10137  // Already on next page.
10138  break;
10139  }
10140  ++nextSuballocItem;
10141  }
10142  }
10143  }
10144 
10145  // All tests passed: Success. pOffset is already filled.
10146  return true;
10147 }
10148 
10149 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
10150 {
10151  VMA_ASSERT(item != m_Suballocations.end());
10152  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10153 
10154  VmaSuballocationList::iterator nextItem = item;
10155  ++nextItem;
10156  VMA_ASSERT(nextItem != m_Suballocations.end());
10157  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
10158 
10159  item->size += nextItem->size;
10160  --m_FreeCount;
10161  m_Suballocations.erase(nextItem);
10162 }
10163 
10164 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
10165 {
10166  // Change this suballocation to be marked as free.
10167  VmaSuballocation& suballoc = *suballocItem;
10168  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10169  suballoc.hAllocation = VK_NULL_HANDLE;
10170 
10171  // Update totals.
10172  ++m_FreeCount;
10173  m_SumFreeSize += suballoc.size;
10174 
10175  // Merge with previous and/or next suballocation if it's also free.
10176  bool mergeWithNext = false;
10177  bool mergeWithPrev = false;
10178 
10179  VmaSuballocationList::iterator nextItem = suballocItem;
10180  ++nextItem;
10181  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
10182  {
10183  mergeWithNext = true;
10184  }
10185 
10186  VmaSuballocationList::iterator prevItem = suballocItem;
10187  if(suballocItem != m_Suballocations.begin())
10188  {
10189  --prevItem;
10190  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
10191  {
10192  mergeWithPrev = true;
10193  }
10194  }
10195 
10196  if(mergeWithNext)
10197  {
10198  UnregisterFreeSuballocation(nextItem);
10199  MergeFreeWithNext(suballocItem);
10200  }
10201 
10202  if(mergeWithPrev)
10203  {
10204  UnregisterFreeSuballocation(prevItem);
10205  MergeFreeWithNext(prevItem);
10206  RegisterFreeSuballocation(prevItem);
10207  return prevItem;
10208  }
10209  else
10210  {
10211  RegisterFreeSuballocation(suballocItem);
10212  return suballocItem;
10213  }
10214 }
10215 
10216 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
10217 {
10218  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10219  VMA_ASSERT(item->size > 0);
10220 
10221  // You may want to enable this validation at the beginning or at the end of
10222  // this function, depending on what do you want to check.
10223  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10224 
10225  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10226  {
10227  if(m_FreeSuballocationsBySize.empty())
10228  {
10229  m_FreeSuballocationsBySize.push_back(item);
10230  }
10231  else
10232  {
10233  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
10234  }
10235  }
10236 
10237  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10238 }
10239 
10240 
10241 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
10242 {
10243  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10244  VMA_ASSERT(item->size > 0);
10245 
10246  // You may want to enable this validation at the beginning or at the end of
10247  // this function, depending on what do you want to check.
10248  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10249 
10250  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10251  {
10252  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
10253  m_FreeSuballocationsBySize.data(),
10254  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
10255  item,
10256  VmaSuballocationItemSizeLess());
10257  for(size_t index = it - m_FreeSuballocationsBySize.data();
10258  index < m_FreeSuballocationsBySize.size();
10259  ++index)
10260  {
10261  if(m_FreeSuballocationsBySize[index] == item)
10262  {
10263  VmaVectorRemove(m_FreeSuballocationsBySize, index);
10264  return;
10265  }
10266  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
10267  }
10268  VMA_ASSERT(0 && "Not found.");
10269  }
10270 
10271  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10272 }
10273 
10274 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
10275  VkDeviceSize bufferImageGranularity,
10276  VmaSuballocationType& inOutPrevSuballocType) const
10277 {
10278  if(bufferImageGranularity == 1 || IsEmpty())
10279  {
10280  return false;
10281  }
10282 
10283  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
10284  bool typeConflictFound = false;
10285  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
10286  it != m_Suballocations.cend();
10287  ++it)
10288  {
10289  const VmaSuballocationType suballocType = it->type;
10290  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
10291  {
10292  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
10293  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
10294  {
10295  typeConflictFound = true;
10296  }
10297  inOutPrevSuballocType = suballocType;
10298  }
10299  }
10300 
10301  return typeConflictFound || minAlignment >= bufferImageGranularity;
10302 }
10303 
10305 // class VmaBlockMetadata_Linear
10306 
10307 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
10308  VmaBlockMetadata(hAllocator),
10309  m_SumFreeSize(0),
10310  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10311  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10312  m_1stVectorIndex(0),
10313  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
10314  m_1stNullItemsBeginCount(0),
10315  m_1stNullItemsMiddleCount(0),
10316  m_2ndNullItemsCount(0)
10317 {
10318 }
10319 
10320 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
10321 {
10322 }
10323 
10324 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
10325 {
10326  VmaBlockMetadata::Init(size);
10327  m_SumFreeSize = size;
10328 }
10329 
10330 bool VmaBlockMetadata_Linear::Validate() const
10331 {
10332  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10333  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10334 
10335  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
10336  VMA_VALIDATE(!suballocations1st.empty() ||
10337  suballocations2nd.empty() ||
10338  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
10339 
10340  if(!suballocations1st.empty())
10341  {
10342  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
10343  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
10344  // Null item at the end should be just pop_back().
10345  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
10346  }
10347  if(!suballocations2nd.empty())
10348  {
10349  // Null item at the end should be just pop_back().
10350  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
10351  }
10352 
10353  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
10354  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
10355 
10356  VkDeviceSize sumUsedSize = 0;
10357  const size_t suballoc1stCount = suballocations1st.size();
10358  VkDeviceSize offset = VMA_DEBUG_MARGIN;
10359 
10360  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10361  {
10362  const size_t suballoc2ndCount = suballocations2nd.size();
10363  size_t nullItem2ndCount = 0;
10364  for(size_t i = 0; i < suballoc2ndCount; ++i)
10365  {
10366  const VmaSuballocation& suballoc = suballocations2nd[i];
10367  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10368 
10369  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10370  VMA_VALIDATE(suballoc.offset >= offset);
10371 
10372  if(!currFree)
10373  {
10374  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10375  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10376  sumUsedSize += suballoc.size;
10377  }
10378  else
10379  {
10380  ++nullItem2ndCount;
10381  }
10382 
10383  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10384  }
10385 
10386  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10387  }
10388 
10389  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
10390  {
10391  const VmaSuballocation& suballoc = suballocations1st[i];
10392  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
10393  suballoc.hAllocation == VK_NULL_HANDLE);
10394  }
10395 
10396  size_t nullItem1stCount = m_1stNullItemsBeginCount;
10397 
10398  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
10399  {
10400  const VmaSuballocation& suballoc = suballocations1st[i];
10401  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10402 
10403  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10404  VMA_VALIDATE(suballoc.offset >= offset);
10405  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10406 
10407  if(!currFree)
10408  {
10409  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10410  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10411  sumUsedSize += suballoc.size;
10412  }
10413  else
10414  {
10415  ++nullItem1stCount;
10416  }
10417 
10418  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10419  }
10420  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10421 
10422  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10423  {
10424  const size_t suballoc2ndCount = suballocations2nd.size();
10425  size_t nullItem2ndCount = 0;
10426  for(size_t i = suballoc2ndCount; i--; )
10427  {
10428  const VmaSuballocation& suballoc = suballocations2nd[i];
10429  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10430 
10431  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10432  VMA_VALIDATE(suballoc.offset >= offset);
10433 
10434  if(!currFree)
10435  {
10436  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10437  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10438  sumUsedSize += suballoc.size;
10439  }
10440  else
10441  {
10442  ++nullItem2ndCount;
10443  }
10444 
10445  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10446  }
10447 
10448  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10449  }
10450 
10451  VMA_VALIDATE(offset <= GetSize());
10452  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10453 
10454  return true;
10455 }
10456 
10457 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10458 {
10459  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10460  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10461 }
10462 
10463 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10464 {
10465  const VkDeviceSize size = GetSize();
10466 
10467  /*
10468  We don't consider gaps inside allocation vectors with freed allocations because
10469  they are not suitable for reuse in linear allocator. We consider only space that
10470  is available for new allocations.
10471  */
10472  if(IsEmpty())
10473  {
10474  return size;
10475  }
10476 
10477  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10478 
10479  switch(m_2ndVectorMode)
10480  {
10481  case SECOND_VECTOR_EMPTY:
10482  /*
10483  Available space is after end of 1st, as well as before beginning of 1st (which
10484  would make it a ring buffer).
10485  */
10486  {
10487  const size_t suballocations1stCount = suballocations1st.size();
10488  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10489  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10490  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10491  return VMA_MAX(
10492  firstSuballoc.offset,
10493  size - (lastSuballoc.offset + lastSuballoc.size));
10494  }
10495  break;
10496 
10497  case SECOND_VECTOR_RING_BUFFER:
10498  /*
10499  Available space is only between end of 2nd and beginning of 1st.
10500  */
10501  {
10502  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10503  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10504  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10505  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10506  }
10507  break;
10508 
10509  case SECOND_VECTOR_DOUBLE_STACK:
10510  /*
10511  Available space is only between end of 1st and top of 2nd.
10512  */
10513  {
10514  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10515  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10516  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10517  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10518  }
10519  break;
10520 
10521  default:
10522  VMA_ASSERT(0);
10523  return 0;
10524  }
10525 }
10526 
10527 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10528 {
10529  const VkDeviceSize size = GetSize();
10530  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10531  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10532  const size_t suballoc1stCount = suballocations1st.size();
10533  const size_t suballoc2ndCount = suballocations2nd.size();
10534 
10535  outInfo.blockCount = 1;
10536  outInfo.allocationCount = (uint32_t)GetAllocationCount();
10537  outInfo.unusedRangeCount = 0;
10538  outInfo.usedBytes = 0;
10539  outInfo.allocationSizeMin = UINT64_MAX;
10540  outInfo.allocationSizeMax = 0;
10541  outInfo.unusedRangeSizeMin = UINT64_MAX;
10542  outInfo.unusedRangeSizeMax = 0;
10543 
10544  VkDeviceSize lastOffset = 0;
10545 
10546  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10547  {
10548  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10549  size_t nextAlloc2ndIndex = 0;
10550  while(lastOffset < freeSpace2ndTo1stEnd)
10551  {
10552  // Find next non-null allocation or move nextAllocIndex to the end.
10553  while(nextAlloc2ndIndex < suballoc2ndCount &&
10554  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10555  {
10556  ++nextAlloc2ndIndex;
10557  }
10558 
10559  // Found non-null allocation.
10560  if(nextAlloc2ndIndex < suballoc2ndCount)
10561  {
10562  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10563 
10564  // 1. Process free space before this allocation.
10565  if(lastOffset < suballoc.offset)
10566  {
10567  // There is free space from lastOffset to suballoc.offset.
10568  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10569  ++outInfo.unusedRangeCount;
10570  outInfo.unusedBytes += unusedRangeSize;
10571  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10572  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10573  }
10574 
10575  // 2. Process this allocation.
10576  // There is allocation with suballoc.offset, suballoc.size.
10577  outInfo.usedBytes += suballoc.size;
10578  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10579  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10580 
10581  // 3. Prepare for next iteration.
10582  lastOffset = suballoc.offset + suballoc.size;
10583  ++nextAlloc2ndIndex;
10584  }
10585  // We are at the end.
10586  else
10587  {
10588  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10589  if(lastOffset < freeSpace2ndTo1stEnd)
10590  {
10591  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10592  ++outInfo.unusedRangeCount;
10593  outInfo.unusedBytes += unusedRangeSize;
10594  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10595  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10596  }
10597 
10598  // End of loop.
10599  lastOffset = freeSpace2ndTo1stEnd;
10600  }
10601  }
10602  }
10603 
10604  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10605  const VkDeviceSize freeSpace1stTo2ndEnd =
10606  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10607  while(lastOffset < freeSpace1stTo2ndEnd)
10608  {
10609  // Find next non-null allocation or move nextAllocIndex to the end.
10610  while(nextAlloc1stIndex < suballoc1stCount &&
10611  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10612  {
10613  ++nextAlloc1stIndex;
10614  }
10615 
10616  // Found non-null allocation.
10617  if(nextAlloc1stIndex < suballoc1stCount)
10618  {
10619  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10620 
10621  // 1. Process free space before this allocation.
10622  if(lastOffset < suballoc.offset)
10623  {
10624  // There is free space from lastOffset to suballoc.offset.
10625  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10626  ++outInfo.unusedRangeCount;
10627  outInfo.unusedBytes += unusedRangeSize;
10628  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10629  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10630  }
10631 
10632  // 2. Process this allocation.
10633  // There is allocation with suballoc.offset, suballoc.size.
10634  outInfo.usedBytes += suballoc.size;
10635  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10636  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10637 
10638  // 3. Prepare for next iteration.
10639  lastOffset = suballoc.offset + suballoc.size;
10640  ++nextAlloc1stIndex;
10641  }
10642  // We are at the end.
10643  else
10644  {
10645  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10646  if(lastOffset < freeSpace1stTo2ndEnd)
10647  {
10648  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10649  ++outInfo.unusedRangeCount;
10650  outInfo.unusedBytes += unusedRangeSize;
10651  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10652  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10653  }
10654 
10655  // End of loop.
10656  lastOffset = freeSpace1stTo2ndEnd;
10657  }
10658  }
10659 
10660  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10661  {
10662  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10663  while(lastOffset < size)
10664  {
10665  // Find next non-null allocation or move nextAllocIndex to the end.
10666  while(nextAlloc2ndIndex != SIZE_MAX &&
10667  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10668  {
10669  --nextAlloc2ndIndex;
10670  }
10671 
10672  // Found non-null allocation.
10673  if(nextAlloc2ndIndex != SIZE_MAX)
10674  {
10675  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10676 
10677  // 1. Process free space before this allocation.
10678  if(lastOffset < suballoc.offset)
10679  {
10680  // There is free space from lastOffset to suballoc.offset.
10681  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10682  ++outInfo.unusedRangeCount;
10683  outInfo.unusedBytes += unusedRangeSize;
10684  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10685  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10686  }
10687 
10688  // 2. Process this allocation.
10689  // There is allocation with suballoc.offset, suballoc.size.
10690  outInfo.usedBytes += suballoc.size;
10691  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10692  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10693 
10694  // 3. Prepare for next iteration.
10695  lastOffset = suballoc.offset + suballoc.size;
10696  --nextAlloc2ndIndex;
10697  }
10698  // We are at the end.
10699  else
10700  {
10701  // There is free space from lastOffset to size.
10702  if(lastOffset < size)
10703  {
10704  const VkDeviceSize unusedRangeSize = size - lastOffset;
10705  ++outInfo.unusedRangeCount;
10706  outInfo.unusedBytes += unusedRangeSize;
10707  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10708  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10709  }
10710 
10711  // End of loop.
10712  lastOffset = size;
10713  }
10714  }
10715  }
10716 
10717  outInfo.unusedBytes = size - outInfo.usedBytes;
10718 }
10719 
10720 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10721 {
10722  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10723  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10724  const VkDeviceSize size = GetSize();
10725  const size_t suballoc1stCount = suballocations1st.size();
10726  const size_t suballoc2ndCount = suballocations2nd.size();
10727 
10728  inoutStats.size += size;
10729 
10730  VkDeviceSize lastOffset = 0;
10731 
10732  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10733  {
10734  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10735  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10736  while(lastOffset < freeSpace2ndTo1stEnd)
10737  {
10738  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10739  while(nextAlloc2ndIndex < suballoc2ndCount &&
10740  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10741  {
10742  ++nextAlloc2ndIndex;
10743  }
10744 
10745  // Found non-null allocation.
10746  if(nextAlloc2ndIndex < suballoc2ndCount)
10747  {
10748  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10749 
10750  // 1. Process free space before this allocation.
10751  if(lastOffset < suballoc.offset)
10752  {
10753  // There is free space from lastOffset to suballoc.offset.
10754  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10755  inoutStats.unusedSize += unusedRangeSize;
10756  ++inoutStats.unusedRangeCount;
10757  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10758  }
10759 
10760  // 2. Process this allocation.
10761  // There is allocation with suballoc.offset, suballoc.size.
10762  ++inoutStats.allocationCount;
10763 
10764  // 3. Prepare for next iteration.
10765  lastOffset = suballoc.offset + suballoc.size;
10766  ++nextAlloc2ndIndex;
10767  }
10768  // We are at the end.
10769  else
10770  {
10771  if(lastOffset < freeSpace2ndTo1stEnd)
10772  {
10773  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10774  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10775  inoutStats.unusedSize += unusedRangeSize;
10776  ++inoutStats.unusedRangeCount;
10777  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10778  }
10779 
10780  // End of loop.
10781  lastOffset = freeSpace2ndTo1stEnd;
10782  }
10783  }
10784  }
10785 
10786  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10787  const VkDeviceSize freeSpace1stTo2ndEnd =
10788  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10789  while(lastOffset < freeSpace1stTo2ndEnd)
10790  {
10791  // Find next non-null allocation or move nextAllocIndex to the end.
10792  while(nextAlloc1stIndex < suballoc1stCount &&
10793  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10794  {
10795  ++nextAlloc1stIndex;
10796  }
10797 
10798  // Found non-null allocation.
10799  if(nextAlloc1stIndex < suballoc1stCount)
10800  {
10801  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10802 
10803  // 1. Process free space before this allocation.
10804  if(lastOffset < suballoc.offset)
10805  {
10806  // There is free space from lastOffset to suballoc.offset.
10807  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10808  inoutStats.unusedSize += unusedRangeSize;
10809  ++inoutStats.unusedRangeCount;
10810  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10811  }
10812 
10813  // 2. Process this allocation.
10814  // There is allocation with suballoc.offset, suballoc.size.
10815  ++inoutStats.allocationCount;
10816 
10817  // 3. Prepare for next iteration.
10818  lastOffset = suballoc.offset + suballoc.size;
10819  ++nextAlloc1stIndex;
10820  }
10821  // We are at the end.
10822  else
10823  {
10824  if(lastOffset < freeSpace1stTo2ndEnd)
10825  {
10826  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10827  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10828  inoutStats.unusedSize += unusedRangeSize;
10829  ++inoutStats.unusedRangeCount;
10830  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10831  }
10832 
10833  // End of loop.
10834  lastOffset = freeSpace1stTo2ndEnd;
10835  }
10836  }
10837 
10838  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10839  {
10840  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10841  while(lastOffset < size)
10842  {
10843  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10844  while(nextAlloc2ndIndex != SIZE_MAX &&
10845  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10846  {
10847  --nextAlloc2ndIndex;
10848  }
10849 
10850  // Found non-null allocation.
10851  if(nextAlloc2ndIndex != SIZE_MAX)
10852  {
10853  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10854 
10855  // 1. Process free space before this allocation.
10856  if(lastOffset < suballoc.offset)
10857  {
10858  // There is free space from lastOffset to suballoc.offset.
10859  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10860  inoutStats.unusedSize += unusedRangeSize;
10861  ++inoutStats.unusedRangeCount;
10862  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10863  }
10864 
10865  // 2. Process this allocation.
10866  // There is allocation with suballoc.offset, suballoc.size.
10867  ++inoutStats.allocationCount;
10868 
10869  // 3. Prepare for next iteration.
10870  lastOffset = suballoc.offset + suballoc.size;
10871  --nextAlloc2ndIndex;
10872  }
10873  // We are at the end.
10874  else
10875  {
10876  if(lastOffset < size)
10877  {
10878  // There is free space from lastOffset to size.
10879  const VkDeviceSize unusedRangeSize = size - lastOffset;
10880  inoutStats.unusedSize += unusedRangeSize;
10881  ++inoutStats.unusedRangeCount;
10882  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10883  }
10884 
10885  // End of loop.
10886  lastOffset = size;
10887  }
10888  }
10889  }
10890 }
10891 
10892 #if VMA_STATS_STRING_ENABLED
10893 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10894 {
10895  const VkDeviceSize size = GetSize();
10896  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10897  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10898  const size_t suballoc1stCount = suballocations1st.size();
10899  const size_t suballoc2ndCount = suballocations2nd.size();
10900 
10901  // FIRST PASS
10902 
10903  size_t unusedRangeCount = 0;
10904  VkDeviceSize usedBytes = 0;
10905 
10906  VkDeviceSize lastOffset = 0;
10907 
10908  size_t alloc2ndCount = 0;
10909  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10910  {
10911  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10912  size_t nextAlloc2ndIndex = 0;
10913  while(lastOffset < freeSpace2ndTo1stEnd)
10914  {
10915  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10916  while(nextAlloc2ndIndex < suballoc2ndCount &&
10917  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10918  {
10919  ++nextAlloc2ndIndex;
10920  }
10921 
10922  // Found non-null allocation.
10923  if(nextAlloc2ndIndex < suballoc2ndCount)
10924  {
10925  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10926 
10927  // 1. Process free space before this allocation.
10928  if(lastOffset < suballoc.offset)
10929  {
10930  // There is free space from lastOffset to suballoc.offset.
10931  ++unusedRangeCount;
10932  }
10933 
10934  // 2. Process this allocation.
10935  // There is allocation with suballoc.offset, suballoc.size.
10936  ++alloc2ndCount;
10937  usedBytes += suballoc.size;
10938 
10939  // 3. Prepare for next iteration.
10940  lastOffset = suballoc.offset + suballoc.size;
10941  ++nextAlloc2ndIndex;
10942  }
10943  // We are at the end.
10944  else
10945  {
10946  if(lastOffset < freeSpace2ndTo1stEnd)
10947  {
10948  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10949  ++unusedRangeCount;
10950  }
10951 
10952  // End of loop.
10953  lastOffset = freeSpace2ndTo1stEnd;
10954  }
10955  }
10956  }
10957 
10958  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10959  size_t alloc1stCount = 0;
10960  const VkDeviceSize freeSpace1stTo2ndEnd =
10961  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10962  while(lastOffset < freeSpace1stTo2ndEnd)
10963  {
10964  // Find next non-null allocation or move nextAllocIndex to the end.
10965  while(nextAlloc1stIndex < suballoc1stCount &&
10966  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10967  {
10968  ++nextAlloc1stIndex;
10969  }
10970 
10971  // Found non-null allocation.
10972  if(nextAlloc1stIndex < suballoc1stCount)
10973  {
10974  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10975 
10976  // 1. Process free space before this allocation.
10977  if(lastOffset < suballoc.offset)
10978  {
10979  // There is free space from lastOffset to suballoc.offset.
10980  ++unusedRangeCount;
10981  }
10982 
10983  // 2. Process this allocation.
10984  // There is allocation with suballoc.offset, suballoc.size.
10985  ++alloc1stCount;
10986  usedBytes += suballoc.size;
10987 
10988  // 3. Prepare for next iteration.
10989  lastOffset = suballoc.offset + suballoc.size;
10990  ++nextAlloc1stIndex;
10991  }
10992  // We are at the end.
10993  else
10994  {
10995  if(lastOffset < size)
10996  {
10997  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10998  ++unusedRangeCount;
10999  }
11000 
11001  // End of loop.
11002  lastOffset = freeSpace1stTo2ndEnd;
11003  }
11004  }
11005 
11006  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11007  {
11008  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
11009  while(lastOffset < size)
11010  {
11011  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11012  while(nextAlloc2ndIndex != SIZE_MAX &&
11013  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11014  {
11015  --nextAlloc2ndIndex;
11016  }
11017 
11018  // Found non-null allocation.
11019  if(nextAlloc2ndIndex != SIZE_MAX)
11020  {
11021  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11022 
11023  // 1. Process free space before this allocation.
11024  if(lastOffset < suballoc.offset)
11025  {
11026  // There is free space from lastOffset to suballoc.offset.
11027  ++unusedRangeCount;
11028  }
11029 
11030  // 2. Process this allocation.
11031  // There is allocation with suballoc.offset, suballoc.size.
11032  ++alloc2ndCount;
11033  usedBytes += suballoc.size;
11034 
11035  // 3. Prepare for next iteration.
11036  lastOffset = suballoc.offset + suballoc.size;
11037  --nextAlloc2ndIndex;
11038  }
11039  // We are at the end.
11040  else
11041  {
11042  if(lastOffset < size)
11043  {
11044  // There is free space from lastOffset to size.
11045  ++unusedRangeCount;
11046  }
11047 
11048  // End of loop.
11049  lastOffset = size;
11050  }
11051  }
11052  }
11053 
11054  const VkDeviceSize unusedBytes = size - usedBytes;
11055  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
11056 
11057  // SECOND PASS
11058  lastOffset = 0;
11059 
11060  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11061  {
11062  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
11063  size_t nextAlloc2ndIndex = 0;
11064  while(lastOffset < freeSpace2ndTo1stEnd)
11065  {
11066  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11067  while(nextAlloc2ndIndex < suballoc2ndCount &&
11068  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11069  {
11070  ++nextAlloc2ndIndex;
11071  }
11072 
11073  // Found non-null allocation.
11074  if(nextAlloc2ndIndex < suballoc2ndCount)
11075  {
11076  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11077 
11078  // 1. Process free space before this allocation.
11079  if(lastOffset < suballoc.offset)
11080  {
11081  // There is free space from lastOffset to suballoc.offset.
11082  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11083  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11084  }
11085 
11086  // 2. Process this allocation.
11087  // There is allocation with suballoc.offset, suballoc.size.
11088  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11089 
11090  // 3. Prepare for next iteration.
11091  lastOffset = suballoc.offset + suballoc.size;
11092  ++nextAlloc2ndIndex;
11093  }
11094  // We are at the end.
11095  else
11096  {
11097  if(lastOffset < freeSpace2ndTo1stEnd)
11098  {
11099  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
11100  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
11101  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11102  }
11103 
11104  // End of loop.
11105  lastOffset = freeSpace2ndTo1stEnd;
11106  }
11107  }
11108  }
11109 
11110  nextAlloc1stIndex = m_1stNullItemsBeginCount;
11111  while(lastOffset < freeSpace1stTo2ndEnd)
11112  {
11113  // Find next non-null allocation or move nextAllocIndex to the end.
11114  while(nextAlloc1stIndex < suballoc1stCount &&
11115  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
11116  {
11117  ++nextAlloc1stIndex;
11118  }
11119 
11120  // Found non-null allocation.
11121  if(nextAlloc1stIndex < suballoc1stCount)
11122  {
11123  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
11124 
11125  // 1. Process free space before this allocation.
11126  if(lastOffset < suballoc.offset)
11127  {
11128  // There is free space from lastOffset to suballoc.offset.
11129  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11130  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11131  }
11132 
11133  // 2. Process this allocation.
11134  // There is allocation with suballoc.offset, suballoc.size.
11135  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11136 
11137  // 3. Prepare for next iteration.
11138  lastOffset = suballoc.offset + suballoc.size;
11139  ++nextAlloc1stIndex;
11140  }
11141  // We are at the end.
11142  else
11143  {
11144  if(lastOffset < freeSpace1stTo2ndEnd)
11145  {
11146  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
11147  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
11148  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11149  }
11150 
11151  // End of loop.
11152  lastOffset = freeSpace1stTo2ndEnd;
11153  }
11154  }
11155 
11156  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11157  {
11158  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
11159  while(lastOffset < size)
11160  {
11161  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11162  while(nextAlloc2ndIndex != SIZE_MAX &&
11163  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11164  {
11165  --nextAlloc2ndIndex;
11166  }
11167 
11168  // Found non-null allocation.
11169  if(nextAlloc2ndIndex != SIZE_MAX)
11170  {
11171  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11172 
11173  // 1. Process free space before this allocation.
11174  if(lastOffset < suballoc.offset)
11175  {
11176  // There is free space from lastOffset to suballoc.offset.
11177  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11178  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11179  }
11180 
11181  // 2. Process this allocation.
11182  // There is allocation with suballoc.offset, suballoc.size.
11183  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11184 
11185  // 3. Prepare for next iteration.
11186  lastOffset = suballoc.offset + suballoc.size;
11187  --nextAlloc2ndIndex;
11188  }
11189  // We are at the end.
11190  else
11191  {
11192  if(lastOffset < size)
11193  {
11194  // There is free space from lastOffset to size.
11195  const VkDeviceSize unusedRangeSize = size - lastOffset;
11196  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11197  }
11198 
11199  // End of loop.
11200  lastOffset = size;
11201  }
11202  }
11203  }
11204 
11205  PrintDetailedMap_End(json);
11206 }
11207 #endif // #if VMA_STATS_STRING_ENABLED
11208 
11209 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
11210  uint32_t currentFrameIndex,
11211  uint32_t frameInUseCount,
11212  VkDeviceSize bufferImageGranularity,
11213  VkDeviceSize allocSize,
11214  VkDeviceSize allocAlignment,
11215  bool upperAddress,
11216  VmaSuballocationType allocType,
11217  bool canMakeOtherLost,
11218  uint32_t strategy,
11219  VmaAllocationRequest* pAllocationRequest)
11220 {
11221  VMA_ASSERT(allocSize > 0);
11222  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
11223  VMA_ASSERT(pAllocationRequest != VMA_NULL);
11224  VMA_HEAVY_ASSERT(Validate());
11225  return upperAddress ?
11226  CreateAllocationRequest_UpperAddress(
11227  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11228  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
11229  CreateAllocationRequest_LowerAddress(
11230  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11231  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
11232 }
11233 
11234 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
11235  uint32_t currentFrameIndex,
11236  uint32_t frameInUseCount,
11237  VkDeviceSize bufferImageGranularity,
11238  VkDeviceSize allocSize,
11239  VkDeviceSize allocAlignment,
11240  VmaSuballocationType allocType,
11241  bool canMakeOtherLost,
11242  uint32_t strategy,
11243  VmaAllocationRequest* pAllocationRequest)
11244 {
11245  const VkDeviceSize size = GetSize();
11246  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11247  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11248 
11249  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11250  {
11251  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
11252  return false;
11253  }
11254 
11255  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
11256  if(allocSize > size)
11257  {
11258  return false;
11259  }
11260  VkDeviceSize resultBaseOffset = size - allocSize;
11261  if(!suballocations2nd.empty())
11262  {
11263  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11264  resultBaseOffset = lastSuballoc.offset - allocSize;
11265  if(allocSize > lastSuballoc.offset)
11266  {
11267  return false;
11268  }
11269  }
11270 
11271  // Start from offset equal to end of free space.
11272  VkDeviceSize resultOffset = resultBaseOffset;
11273 
11274  // Apply VMA_DEBUG_MARGIN at the end.
11275  if(VMA_DEBUG_MARGIN > 0)
11276  {
11277  if(resultOffset < VMA_DEBUG_MARGIN)
11278  {
11279  return false;
11280  }
11281  resultOffset -= VMA_DEBUG_MARGIN;
11282  }
11283 
11284  // Apply alignment.
11285  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
11286 
11287  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
11288  // Make bigger alignment if necessary.
11289  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11290  {
11291  bool bufferImageGranularityConflict = false;
11292  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11293  {
11294  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11295  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11296  {
11297  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
11298  {
11299  bufferImageGranularityConflict = true;
11300  break;
11301  }
11302  }
11303  else
11304  // Already on previous page.
11305  break;
11306  }
11307  if(bufferImageGranularityConflict)
11308  {
11309  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
11310  }
11311  }
11312 
11313  // There is enough free space.
11314  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
11315  suballocations1st.back().offset + suballocations1st.back().size :
11316  0;
11317  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
11318  {
11319  // Check previous suballocations for BufferImageGranularity conflicts.
11320  // If conflict exists, allocation cannot be made here.
11321  if(bufferImageGranularity > 1)
11322  {
11323  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11324  {
11325  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11326  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11327  {
11328  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
11329  {
11330  return false;
11331  }
11332  }
11333  else
11334  {
11335  // Already on next page.
11336  break;
11337  }
11338  }
11339  }
11340 
11341  // All tests passed: Success.
11342  pAllocationRequest->offset = resultOffset;
11343  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
11344  pAllocationRequest->sumItemSize = 0;
11345  // pAllocationRequest->item unused.
11346  pAllocationRequest->itemsToMakeLostCount = 0;
11347  pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
11348  return true;
11349  }
11350 
11351  return false;
11352 }
11353 
11354 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
11355  uint32_t currentFrameIndex,
11356  uint32_t frameInUseCount,
11357  VkDeviceSize bufferImageGranularity,
11358  VkDeviceSize allocSize,
11359  VkDeviceSize allocAlignment,
11360  VmaSuballocationType allocType,
11361  bool canMakeOtherLost,
11362  uint32_t strategy,
11363  VmaAllocationRequest* pAllocationRequest)
11364 {
11365  const VkDeviceSize size = GetSize();
11366  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11367  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11368 
11369  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11370  {
11371  // Try to allocate at the end of 1st vector.
11372 
11373  VkDeviceSize resultBaseOffset = 0;
11374  if(!suballocations1st.empty())
11375  {
11376  const VmaSuballocation& lastSuballoc = suballocations1st.back();
11377  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11378  }
11379 
11380  // Start from offset equal to beginning of free space.
11381  VkDeviceSize resultOffset = resultBaseOffset;
11382 
11383  // Apply VMA_DEBUG_MARGIN at the beginning.
11384  if(VMA_DEBUG_MARGIN > 0)
11385  {
11386  resultOffset += VMA_DEBUG_MARGIN;
11387  }
11388 
11389  // Apply alignment.
11390  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11391 
11392  // Check previous suballocations for BufferImageGranularity conflicts.
11393  // Make bigger alignment if necessary.
11394  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
11395  {
11396  bool bufferImageGranularityConflict = false;
11397  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11398  {
11399  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11400  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11401  {
11402  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11403  {
11404  bufferImageGranularityConflict = true;
11405  break;
11406  }
11407  }
11408  else
11409  // Already on previous page.
11410  break;
11411  }
11412  if(bufferImageGranularityConflict)
11413  {
11414  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11415  }
11416  }
11417 
11418  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11419  suballocations2nd.back().offset : size;
11420 
11421  // There is enough free space at the end after alignment.
11422  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11423  {
11424  // Check next suballocations for BufferImageGranularity conflicts.
11425  // If conflict exists, allocation cannot be made here.
11426  if((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11427  {
11428  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11429  {
11430  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11431  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11432  {
11433  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11434  {
11435  return false;
11436  }
11437  }
11438  else
11439  {
11440  // Already on previous page.
11441  break;
11442  }
11443  }
11444  }
11445 
11446  // All tests passed: Success.
11447  pAllocationRequest->offset = resultOffset;
11448  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11449  pAllocationRequest->sumItemSize = 0;
11450  // pAllocationRequest->item, customData unused.
11451  pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11452  pAllocationRequest->itemsToMakeLostCount = 0;
11453  return true;
11454  }
11455  }
11456 
11457  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11458  // beginning of 1st vector as the end of free space.
11459  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11460  {
11461  VMA_ASSERT(!suballocations1st.empty());
11462 
11463  VkDeviceSize resultBaseOffset = 0;
11464  if(!suballocations2nd.empty())
11465  {
11466  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11467  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11468  }
11469 
11470  // Start from offset equal to beginning of free space.
11471  VkDeviceSize resultOffset = resultBaseOffset;
11472 
11473  // Apply VMA_DEBUG_MARGIN at the beginning.
11474  if(VMA_DEBUG_MARGIN > 0)
11475  {
11476  resultOffset += VMA_DEBUG_MARGIN;
11477  }
11478 
11479  // Apply alignment.
11480  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11481 
11482  // Check previous suballocations for BufferImageGranularity conflicts.
11483  // Make bigger alignment if necessary.
11484  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11485  {
11486  bool bufferImageGranularityConflict = false;
11487  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11488  {
11489  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11490  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11491  {
11492  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11493  {
11494  bufferImageGranularityConflict = true;
11495  break;
11496  }
11497  }
11498  else
11499  // Already on previous page.
11500  break;
11501  }
11502  if(bufferImageGranularityConflict)
11503  {
11504  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11505  }
11506  }
11507 
11508  pAllocationRequest->itemsToMakeLostCount = 0;
11509  pAllocationRequest->sumItemSize = 0;
11510  size_t index1st = m_1stNullItemsBeginCount;
11511 
11512  if(canMakeOtherLost)
11513  {
11514  while(index1st < suballocations1st.size() &&
11515  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11516  {
11517  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11518  const VmaSuballocation& suballoc = suballocations1st[index1st];
11519  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11520  {
11521  // No problem.
11522  }
11523  else
11524  {
11525  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11526  if(suballoc.hAllocation->CanBecomeLost() &&
11527  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11528  {
11529  ++pAllocationRequest->itemsToMakeLostCount;
11530  pAllocationRequest->sumItemSize += suballoc.size;
11531  }
11532  else
11533  {
11534  return false;
11535  }
11536  }
11537  ++index1st;
11538  }
11539 
11540  // Check next suballocations for BufferImageGranularity conflicts.
11541  // If conflict exists, we must mark more allocations lost or fail.
11542  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11543  {
11544  while(index1st < suballocations1st.size())
11545  {
11546  const VmaSuballocation& suballoc = suballocations1st[index1st];
11547  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11548  {
11549  if(suballoc.hAllocation != VK_NULL_HANDLE)
11550  {
11551  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11552  if(suballoc.hAllocation->CanBecomeLost() &&
11553  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11554  {
11555  ++pAllocationRequest->itemsToMakeLostCount;
11556  pAllocationRequest->sumItemSize += suballoc.size;
11557  }
11558  else
11559  {
11560  return false;
11561  }
11562  }
11563  }
11564  else
11565  {
11566  // Already on next page.
11567  break;
11568  }
11569  ++index1st;
11570  }
11571  }
11572 
11573  // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11574  if(index1st == suballocations1st.size() &&
11575  resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11576  {
11577  // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11578  VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11579  }
11580  }
11581 
11582  // There is enough free space at the end after alignment.
11583  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11584  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11585  {
11586  // Check next suballocations for BufferImageGranularity conflicts.
11587  // If conflict exists, allocation cannot be made here.
11588  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11589  {
11590  for(size_t nextSuballocIndex = index1st;
11591  nextSuballocIndex < suballocations1st.size();
11592  nextSuballocIndex++)
11593  {
11594  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11595  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11596  {
11597  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11598  {
11599  return false;
11600  }
11601  }
11602  else
11603  {
11604  // Already on next page.
11605  break;
11606  }
11607  }
11608  }
11609 
11610  // All tests passed: Success.
11611  pAllocationRequest->offset = resultOffset;
11612  pAllocationRequest->sumFreeSize =
11613  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11614  - resultBaseOffset
11615  - pAllocationRequest->sumItemSize;
11616  pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11617  // pAllocationRequest->item, customData unused.
11618  return true;
11619  }
11620  }
11621 
11622  return false;
11623 }
11624 
11625 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11626  uint32_t currentFrameIndex,
11627  uint32_t frameInUseCount,
11628  VmaAllocationRequest* pAllocationRequest)
11629 {
11630  if(pAllocationRequest->itemsToMakeLostCount == 0)
11631  {
11632  return true;
11633  }
11634 
11635  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11636 
11637  // We always start from 1st.
11638  SuballocationVectorType* suballocations = &AccessSuballocations1st();
11639  size_t index = m_1stNullItemsBeginCount;
11640  size_t madeLostCount = 0;
11641  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11642  {
11643  if(index == suballocations->size())
11644  {
11645  index = 0;
11646  // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11647  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11648  {
11649  suballocations = &AccessSuballocations2nd();
11650  }
11651  // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11652  // suballocations continues pointing at AccessSuballocations1st().
11653  VMA_ASSERT(!suballocations->empty());
11654  }
11655  VmaSuballocation& suballoc = (*suballocations)[index];
11656  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11657  {
11658  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11659  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11660  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11661  {
11662  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11663  suballoc.hAllocation = VK_NULL_HANDLE;
11664  m_SumFreeSize += suballoc.size;
11665  if(suballocations == &AccessSuballocations1st())
11666  {
11667  ++m_1stNullItemsMiddleCount;
11668  }
11669  else
11670  {
11671  ++m_2ndNullItemsCount;
11672  }
11673  ++madeLostCount;
11674  }
11675  else
11676  {
11677  return false;
11678  }
11679  }
11680  ++index;
11681  }
11682 
11683  CleanupAfterFree();
11684  //VMA_HEAVY_ASSERT(Validate()); // Already called by CleanupAfterFree().
11685 
11686  return true;
11687 }
11688 
11689 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11690 {
11691  uint32_t lostAllocationCount = 0;
11692 
11693  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11694  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11695  {
11696  VmaSuballocation& suballoc = suballocations1st[i];
11697  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11698  suballoc.hAllocation->CanBecomeLost() &&
11699  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11700  {
11701  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11702  suballoc.hAllocation = VK_NULL_HANDLE;
11703  ++m_1stNullItemsMiddleCount;
11704  m_SumFreeSize += suballoc.size;
11705  ++lostAllocationCount;
11706  }
11707  }
11708 
11709  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11710  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11711  {
11712  VmaSuballocation& suballoc = suballocations2nd[i];
11713  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11714  suballoc.hAllocation->CanBecomeLost() &&
11715  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11716  {
11717  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11718  suballoc.hAllocation = VK_NULL_HANDLE;
11719  ++m_2ndNullItemsCount;
11720  m_SumFreeSize += suballoc.size;
11721  ++lostAllocationCount;
11722  }
11723  }
11724 
11725  if(lostAllocationCount)
11726  {
11727  CleanupAfterFree();
11728  }
11729 
11730  return lostAllocationCount;
11731 }
11732 
11733 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11734 {
11735  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11736  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11737  {
11738  const VmaSuballocation& suballoc = suballocations1st[i];
11739  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11740  {
11741  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11742  {
11743  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11744  return VK_ERROR_VALIDATION_FAILED_EXT;
11745  }
11746  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11747  {
11748  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11749  return VK_ERROR_VALIDATION_FAILED_EXT;
11750  }
11751  }
11752  }
11753 
11754  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11755  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11756  {
11757  const VmaSuballocation& suballoc = suballocations2nd[i];
11758  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11759  {
11760  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11761  {
11762  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11763  return VK_ERROR_VALIDATION_FAILED_EXT;
11764  }
11765  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11766  {
11767  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11768  return VK_ERROR_VALIDATION_FAILED_EXT;
11769  }
11770  }
11771  }
11772 
11773  return VK_SUCCESS;
11774 }
11775 
11776 void VmaBlockMetadata_Linear::Alloc(
11777  const VmaAllocationRequest& request,
11778  VmaSuballocationType type,
11779  VkDeviceSize allocSize,
11780  VmaAllocation hAllocation)
11781 {
11782  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11783 
11784  switch(request.type)
11785  {
11786  case VmaAllocationRequestType::UpperAddress:
11787  {
11788  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11789  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11790  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11791  suballocations2nd.push_back(newSuballoc);
11792  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11793  }
11794  break;
11795  case VmaAllocationRequestType::EndOf1st:
11796  {
11797  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11798 
11799  VMA_ASSERT(suballocations1st.empty() ||
11800  request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11801  // Check if it fits before the end of the block.
11802  VMA_ASSERT(request.offset + allocSize <= GetSize());
11803 
11804  suballocations1st.push_back(newSuballoc);
11805  }
11806  break;
11807  case VmaAllocationRequestType::EndOf2nd:
11808  {
11809  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11810  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11811  VMA_ASSERT(!suballocations1st.empty() &&
11812  request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11813  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11814 
11815  switch(m_2ndVectorMode)
11816  {
11817  case SECOND_VECTOR_EMPTY:
11818  // First allocation from second part ring buffer.
11819  VMA_ASSERT(suballocations2nd.empty());
11820  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11821  break;
11822  case SECOND_VECTOR_RING_BUFFER:
11823  // 2-part ring buffer is already started.
11824  VMA_ASSERT(!suballocations2nd.empty());
11825  break;
11826  case SECOND_VECTOR_DOUBLE_STACK:
11827  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11828  break;
11829  default:
11830  VMA_ASSERT(0);
11831  }
11832 
11833  suballocations2nd.push_back(newSuballoc);
11834  }
11835  break;
11836  default:
11837  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11838  }
11839 
11840  m_SumFreeSize -= newSuballoc.size;
11841 }
11842 
11843 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11844 {
11845  FreeAtOffset(allocation->GetOffset());
11846 }
11847 
11848 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11849 {
11850  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11851  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11852 
11853  if(!suballocations1st.empty())
11854  {
11855  // First allocation: Mark it as next empty at the beginning.
11856  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11857  if(firstSuballoc.offset == offset)
11858  {
11859  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11860  firstSuballoc.hAllocation = VK_NULL_HANDLE;
11861  m_SumFreeSize += firstSuballoc.size;
11862  ++m_1stNullItemsBeginCount;
11863  CleanupAfterFree();
11864  return;
11865  }
11866  }
11867 
11868  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11869  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11870  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11871  {
11872  VmaSuballocation& lastSuballoc = suballocations2nd.back();
11873  if(lastSuballoc.offset == offset)
11874  {
11875  m_SumFreeSize += lastSuballoc.size;
11876  suballocations2nd.pop_back();
11877  CleanupAfterFree();
11878  return;
11879  }
11880  }
11881  // Last allocation in 1st vector.
11882  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11883  {
11884  VmaSuballocation& lastSuballoc = suballocations1st.back();
11885  if(lastSuballoc.offset == offset)
11886  {
11887  m_SumFreeSize += lastSuballoc.size;
11888  suballocations1st.pop_back();
11889  CleanupAfterFree();
11890  return;
11891  }
11892  }
11893 
11894  // Item from the middle of 1st vector.
11895  {
11896  VmaSuballocation refSuballoc;
11897  refSuballoc.offset = offset;
11898  // Rest of members stays uninitialized intentionally for better performance.
11899  SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11900  suballocations1st.begin() + m_1stNullItemsBeginCount,
11901  suballocations1st.end(),
11902  refSuballoc,
11903  VmaSuballocationOffsetLess());
11904  if(it != suballocations1st.end())
11905  {
11906  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11907  it->hAllocation = VK_NULL_HANDLE;
11908  ++m_1stNullItemsMiddleCount;
11909  m_SumFreeSize += it->size;
11910  CleanupAfterFree();
11911  return;
11912  }
11913  }
11914 
11915  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11916  {
11917  // Item from the middle of 2nd vector.
11918  VmaSuballocation refSuballoc;
11919  refSuballoc.offset = offset;
11920  // Rest of members stays uninitialized intentionally for better performance.
11921  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11922  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11923  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11924  if(it != suballocations2nd.end())
11925  {
11926  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11927  it->hAllocation = VK_NULL_HANDLE;
11928  ++m_2ndNullItemsCount;
11929  m_SumFreeSize += it->size;
11930  CleanupAfterFree();
11931  return;
11932  }
11933  }
11934 
11935  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11936 }
11937 
11938 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11939 {
11940  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11941  const size_t suballocCount = AccessSuballocations1st().size();
11942  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11943 }
11944 
11945 void VmaBlockMetadata_Linear::CleanupAfterFree()
11946 {
11947  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11948  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11949 
11950  if(IsEmpty())
11951  {
11952  suballocations1st.clear();
11953  suballocations2nd.clear();
11954  m_1stNullItemsBeginCount = 0;
11955  m_1stNullItemsMiddleCount = 0;
11956  m_2ndNullItemsCount = 0;
11957  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11958  }
11959  else
11960  {
11961  const size_t suballoc1stCount = suballocations1st.size();
11962  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11963  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11964 
11965  // Find more null items at the beginning of 1st vector.
11966  while(m_1stNullItemsBeginCount < suballoc1stCount &&
11967  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11968  {
11969  ++m_1stNullItemsBeginCount;
11970  --m_1stNullItemsMiddleCount;
11971  }
11972 
11973  // Find more null items at the end of 1st vector.
11974  while(m_1stNullItemsMiddleCount > 0 &&
11975  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11976  {
11977  --m_1stNullItemsMiddleCount;
11978  suballocations1st.pop_back();
11979  }
11980 
11981  // Find more null items at the end of 2nd vector.
11982  while(m_2ndNullItemsCount > 0 &&
11983  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11984  {
11985  --m_2ndNullItemsCount;
11986  suballocations2nd.pop_back();
11987  }
11988 
11989  // Find more null items at the beginning of 2nd vector.
11990  while(m_2ndNullItemsCount > 0 &&
11991  suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11992  {
11993  --m_2ndNullItemsCount;
11994  VmaVectorRemove(suballocations2nd, 0);
11995  }
11996 
11997  if(ShouldCompact1st())
11998  {
11999  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
12000  size_t srcIndex = m_1stNullItemsBeginCount;
12001  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
12002  {
12003  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
12004  {
12005  ++srcIndex;
12006  }
12007  if(dstIndex != srcIndex)
12008  {
12009  suballocations1st[dstIndex] = suballocations1st[srcIndex];
12010  }
12011  ++srcIndex;
12012  }
12013  suballocations1st.resize(nonNullItemCount);
12014  m_1stNullItemsBeginCount = 0;
12015  m_1stNullItemsMiddleCount = 0;
12016  }
12017 
12018  // 2nd vector became empty.
12019  if(suballocations2nd.empty())
12020  {
12021  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
12022  }
12023 
12024  // 1st vector became empty.
12025  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
12026  {
12027  suballocations1st.clear();
12028  m_1stNullItemsBeginCount = 0;
12029 
12030  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
12031  {
12032  // Swap 1st with 2nd. Now 2nd is empty.
12033  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
12034  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
12035  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
12036  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
12037  {
12038  ++m_1stNullItemsBeginCount;
12039  --m_1stNullItemsMiddleCount;
12040  }
12041  m_2ndNullItemsCount = 0;
12042  m_1stVectorIndex ^= 1;
12043  }
12044  }
12045  }
12046 
12047  VMA_HEAVY_ASSERT(Validate());
12048 }
12049 
12050 
12052 // class VmaBlockMetadata_Buddy
12053 
12054 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
12055  VmaBlockMetadata(hAllocator),
12056  m_Root(VMA_NULL),
12057  m_AllocationCount(0),
12058  m_FreeCount(1),
12059  m_SumFreeSize(0)
12060 {
12061  memset(m_FreeList, 0, sizeof(m_FreeList));
12062 }
12063 
12064 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
12065 {
12066  DeleteNode(m_Root);
12067 }
12068 
12069 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
12070 {
12071  VmaBlockMetadata::Init(size);
12072 
12073  m_UsableSize = VmaPrevPow2(size);
12074  m_SumFreeSize = m_UsableSize;
12075 
12076  // Calculate m_LevelCount.
12077  m_LevelCount = 1;
12078  while(m_LevelCount < MAX_LEVELS &&
12079  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
12080  {
12081  ++m_LevelCount;
12082  }
12083 
12084  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
12085  rootNode->offset = 0;
12086  rootNode->type = Node::TYPE_FREE;
12087  rootNode->parent = VMA_NULL;
12088  rootNode->buddy = VMA_NULL;
12089 
12090  m_Root = rootNode;
12091  AddToFreeListFront(0, rootNode);
12092 }
12093 
12094 bool VmaBlockMetadata_Buddy::Validate() const
12095 {
12096  // Validate tree.
12097  ValidationContext ctx;
12098  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
12099  {
12100  VMA_VALIDATE(false && "ValidateNode failed.");
12101  }
12102  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
12103  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
12104 
12105  // Validate free node lists.
12106  for(uint32_t level = 0; level < m_LevelCount; ++level)
12107  {
12108  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
12109  m_FreeList[level].front->free.prev == VMA_NULL);
12110 
12111  for(Node* node = m_FreeList[level].front;
12112  node != VMA_NULL;
12113  node = node->free.next)
12114  {
12115  VMA_VALIDATE(node->type == Node::TYPE_FREE);
12116 
12117  if(node->free.next == VMA_NULL)
12118  {
12119  VMA_VALIDATE(m_FreeList[level].back == node);
12120  }
12121  else
12122  {
12123  VMA_VALIDATE(node->free.next->free.prev == node);
12124  }
12125  }
12126  }
12127 
12128  // Validate that free lists ar higher levels are empty.
12129  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
12130  {
12131  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
12132  }
12133 
12134  return true;
12135 }
12136 
12137 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
12138 {
12139  for(uint32_t level = 0; level < m_LevelCount; ++level)
12140  {
12141  if(m_FreeList[level].front != VMA_NULL)
12142  {
12143  return LevelToNodeSize(level);
12144  }
12145  }
12146  return 0;
12147 }
12148 
12149 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
12150 {
12151  const VkDeviceSize unusableSize = GetUnusableSize();
12152 
12153  outInfo.blockCount = 1;
12154 
12155  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
12156  outInfo.usedBytes = outInfo.unusedBytes = 0;
12157 
12158  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
12159  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
12160  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
12161 
12162  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
12163 
12164  if(unusableSize > 0)
12165  {
12166  ++outInfo.unusedRangeCount;
12167  outInfo.unusedBytes += unusableSize;
12168  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
12169  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
12170  }
12171 }
12172 
12173 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
12174 {
12175  const VkDeviceSize unusableSize = GetUnusableSize();
12176 
12177  inoutStats.size += GetSize();
12178  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
12179  inoutStats.allocationCount += m_AllocationCount;
12180  inoutStats.unusedRangeCount += m_FreeCount;
12181  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
12182 
12183  if(unusableSize > 0)
12184  {
12185  ++inoutStats.unusedRangeCount;
12186  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
12187  }
12188 }
12189 
12190 #if VMA_STATS_STRING_ENABLED
12191 
12192 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
12193 {
12194  // TODO optimize
12195  VmaStatInfo stat;
12196  CalcAllocationStatInfo(stat);
12197 
12198  PrintDetailedMap_Begin(
12199  json,
12200  stat.unusedBytes,
12201  stat.allocationCount,
12202  stat.unusedRangeCount);
12203 
12204  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
12205 
12206  const VkDeviceSize unusableSize = GetUnusableSize();
12207  if(unusableSize > 0)
12208  {
12209  PrintDetailedMap_UnusedRange(json,
12210  m_UsableSize, // offset
12211  unusableSize); // size
12212  }
12213 
12214  PrintDetailedMap_End(json);
12215 }
12216 
12217 #endif // #if VMA_STATS_STRING_ENABLED
12218 
12219 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
12220  uint32_t currentFrameIndex,
12221  uint32_t frameInUseCount,
12222  VkDeviceSize bufferImageGranularity,
12223  VkDeviceSize allocSize,
12224  VkDeviceSize allocAlignment,
12225  bool upperAddress,
12226  VmaSuballocationType allocType,
12227  bool canMakeOtherLost,
12228  uint32_t strategy,
12229  VmaAllocationRequest* pAllocationRequest)
12230 {
12231  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
12232 
12233  // Simple way to respect bufferImageGranularity. May be optimized some day.
12234  // Whenever it might be an OPTIMAL image...
12235  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
12236  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
12237  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
12238  {
12239  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
12240  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
12241  }
12242 
12243  if(allocSize > m_UsableSize)
12244  {
12245  return false;
12246  }
12247 
12248  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12249  for(uint32_t level = targetLevel + 1; level--; )
12250  {
12251  for(Node* freeNode = m_FreeList[level].front;
12252  freeNode != VMA_NULL;
12253  freeNode = freeNode->free.next)
12254  {
12255  if(freeNode->offset % allocAlignment == 0)
12256  {
12257  pAllocationRequest->type = VmaAllocationRequestType::Normal;
12258  pAllocationRequest->offset = freeNode->offset;
12259  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
12260  pAllocationRequest->sumItemSize = 0;
12261  pAllocationRequest->itemsToMakeLostCount = 0;
12262  pAllocationRequest->customData = (void*)(uintptr_t)level;
12263  return true;
12264  }
12265  }
12266  }
12267 
12268  return false;
12269 }
12270 
12271 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
12272  uint32_t currentFrameIndex,
12273  uint32_t frameInUseCount,
12274  VmaAllocationRequest* pAllocationRequest)
12275 {
12276  /*
12277  Lost allocations are not supported in buddy allocator at the moment.
12278  Support might be added in the future.
12279  */
12280  return pAllocationRequest->itemsToMakeLostCount == 0;
12281 }
12282 
12283 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
12284 {
12285  /*
12286  Lost allocations are not supported in buddy allocator at the moment.
12287  Support might be added in the future.
12288  */
12289  return 0;
12290 }
12291 
12292 void VmaBlockMetadata_Buddy::Alloc(
12293  const VmaAllocationRequest& request,
12294  VmaSuballocationType type,
12295  VkDeviceSize allocSize,
12296  VmaAllocation hAllocation)
12297 {
12298  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
12299 
12300  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12301  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
12302 
12303  Node* currNode = m_FreeList[currLevel].front;
12304  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12305  while(currNode->offset != request.offset)
12306  {
12307  currNode = currNode->free.next;
12308  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12309  }
12310 
12311  // Go down, splitting free nodes.
12312  while(currLevel < targetLevel)
12313  {
12314  // currNode is already first free node at currLevel.
12315  // Remove it from list of free nodes at this currLevel.
12316  RemoveFromFreeList(currLevel, currNode);
12317 
12318  const uint32_t childrenLevel = currLevel + 1;
12319 
12320  // Create two free sub-nodes.
12321  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
12322  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
12323 
12324  leftChild->offset = currNode->offset;
12325  leftChild->type = Node::TYPE_FREE;
12326  leftChild->parent = currNode;
12327  leftChild->buddy = rightChild;
12328 
12329  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
12330  rightChild->type = Node::TYPE_FREE;
12331  rightChild->parent = currNode;
12332  rightChild->buddy = leftChild;
12333 
12334  // Convert current currNode to split type.
12335  currNode->type = Node::TYPE_SPLIT;
12336  currNode->split.leftChild = leftChild;
12337 
12338  // Add child nodes to free list. Order is important!
12339  AddToFreeListFront(childrenLevel, rightChild);
12340  AddToFreeListFront(childrenLevel, leftChild);
12341 
12342  ++m_FreeCount;
12343  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
12344  ++currLevel;
12345  currNode = m_FreeList[currLevel].front;
12346 
12347  /*
12348  We can be sure that currNode, as left child of node previously split,
12349  also fullfills the alignment requirement.
12350  */
12351  }
12352 
12353  // Remove from free list.
12354  VMA_ASSERT(currLevel == targetLevel &&
12355  currNode != VMA_NULL &&
12356  currNode->type == Node::TYPE_FREE);
12357  RemoveFromFreeList(currLevel, currNode);
12358 
12359  // Convert to allocation node.
12360  currNode->type = Node::TYPE_ALLOCATION;
12361  currNode->allocation.alloc = hAllocation;
12362 
12363  ++m_AllocationCount;
12364  --m_FreeCount;
12365  m_SumFreeSize -= allocSize;
12366 }
12367 
12368 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
12369 {
12370  if(node->type == Node::TYPE_SPLIT)
12371  {
12372  DeleteNode(node->split.leftChild->buddy);
12373  DeleteNode(node->split.leftChild);
12374  }
12375 
12376  vma_delete(GetAllocationCallbacks(), node);
12377 }
12378 
12379 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
12380 {
12381  VMA_VALIDATE(level < m_LevelCount);
12382  VMA_VALIDATE(curr->parent == parent);
12383  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
12384  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
12385  switch(curr->type)
12386  {
12387  case Node::TYPE_FREE:
12388  // curr->free.prev, next are validated separately.
12389  ctx.calculatedSumFreeSize += levelNodeSize;
12390  ++ctx.calculatedFreeCount;
12391  break;
12392  case Node::TYPE_ALLOCATION:
12393  ++ctx.calculatedAllocationCount;
12394  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
12395  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
12396  break;
12397  case Node::TYPE_SPLIT:
12398  {
12399  const uint32_t childrenLevel = level + 1;
12400  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
12401  const Node* const leftChild = curr->split.leftChild;
12402  VMA_VALIDATE(leftChild != VMA_NULL);
12403  VMA_VALIDATE(leftChild->offset == curr->offset);
12404  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12405  {
12406  VMA_VALIDATE(false && "ValidateNode for left child failed.");
12407  }
12408  const Node* const rightChild = leftChild->buddy;
12409  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12410  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12411  {
12412  VMA_VALIDATE(false && "ValidateNode for right child failed.");
12413  }
12414  }
12415  break;
12416  default:
12417  return false;
12418  }
12419 
12420  return true;
12421 }
12422 
12423 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12424 {
12425  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12426  uint32_t level = 0;
12427  VkDeviceSize currLevelNodeSize = m_UsableSize;
12428  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12429  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12430  {
12431  ++level;
12432  currLevelNodeSize = nextLevelNodeSize;
12433  nextLevelNodeSize = currLevelNodeSize >> 1;
12434  }
12435  return level;
12436 }
12437 
12438 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12439 {
12440  // Find node and level.
12441  Node* node = m_Root;
12442  VkDeviceSize nodeOffset = 0;
12443  uint32_t level = 0;
12444  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12445  while(node->type == Node::TYPE_SPLIT)
12446  {
12447  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12448  if(offset < nodeOffset + nextLevelSize)
12449  {
12450  node = node->split.leftChild;
12451  }
12452  else
12453  {
12454  node = node->split.leftChild->buddy;
12455  nodeOffset += nextLevelSize;
12456  }
12457  ++level;
12458  levelNodeSize = nextLevelSize;
12459  }
12460 
12461  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12462  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12463 
12464  ++m_FreeCount;
12465  --m_AllocationCount;
12466  m_SumFreeSize += alloc->GetSize();
12467 
12468  node->type = Node::TYPE_FREE;
12469 
12470  // Join free nodes if possible.
12471  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12472  {
12473  RemoveFromFreeList(level, node->buddy);
12474  Node* const parent = node->parent;
12475 
12476  vma_delete(GetAllocationCallbacks(), node->buddy);
12477  vma_delete(GetAllocationCallbacks(), node);
12478  parent->type = Node::TYPE_FREE;
12479 
12480  node = parent;
12481  --level;
12482  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12483  --m_FreeCount;
12484  }
12485 
12486  AddToFreeListFront(level, node);
12487 }
12488 
12489 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12490 {
12491  switch(node->type)
12492  {
12493  case Node::TYPE_FREE:
12494  ++outInfo.unusedRangeCount;
12495  outInfo.unusedBytes += levelNodeSize;
12496  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12497  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12498  break;
12499  case Node::TYPE_ALLOCATION:
12500  {
12501  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12502  ++outInfo.allocationCount;
12503  outInfo.usedBytes += allocSize;
12504  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12505  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12506 
12507  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12508  if(unusedRangeSize > 0)
12509  {
12510  ++outInfo.unusedRangeCount;
12511  outInfo.unusedBytes += unusedRangeSize;
12512  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12513  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12514  }
12515  }
12516  break;
12517  case Node::TYPE_SPLIT:
12518  {
12519  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12520  const Node* const leftChild = node->split.leftChild;
12521  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12522  const Node* const rightChild = leftChild->buddy;
12523  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12524  }
12525  break;
12526  default:
12527  VMA_ASSERT(0);
12528  }
12529 }
12530 
12531 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12532 {
12533  VMA_ASSERT(node->type == Node::TYPE_FREE);
12534 
12535  // List is empty.
12536  Node* const frontNode = m_FreeList[level].front;
12537  if(frontNode == VMA_NULL)
12538  {
12539  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12540  node->free.prev = node->free.next = VMA_NULL;
12541  m_FreeList[level].front = m_FreeList[level].back = node;
12542  }
12543  else
12544  {
12545  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12546  node->free.prev = VMA_NULL;
12547  node->free.next = frontNode;
12548  frontNode->free.prev = node;
12549  m_FreeList[level].front = node;
12550  }
12551 }
12552 
12553 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12554 {
12555  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12556 
12557  // It is at the front.
12558  if(node->free.prev == VMA_NULL)
12559  {
12560  VMA_ASSERT(m_FreeList[level].front == node);
12561  m_FreeList[level].front = node->free.next;
12562  }
12563  else
12564  {
12565  Node* const prevFreeNode = node->free.prev;
12566  VMA_ASSERT(prevFreeNode->free.next == node);
12567  prevFreeNode->free.next = node->free.next;
12568  }
12569 
12570  // It is at the back.
12571  if(node->free.next == VMA_NULL)
12572  {
12573  VMA_ASSERT(m_FreeList[level].back == node);
12574  m_FreeList[level].back = node->free.prev;
12575  }
12576  else
12577  {
12578  Node* const nextFreeNode = node->free.next;
12579  VMA_ASSERT(nextFreeNode->free.prev == node);
12580  nextFreeNode->free.prev = node->free.prev;
12581  }
12582 }
12583 
12584 #if VMA_STATS_STRING_ENABLED
12585 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12586 {
12587  switch(node->type)
12588  {
12589  case Node::TYPE_FREE:
12590  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12591  break;
12592  case Node::TYPE_ALLOCATION:
12593  {
12594  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12595  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12596  if(allocSize < levelNodeSize)
12597  {
12598  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12599  }
12600  }
12601  break;
12602  case Node::TYPE_SPLIT:
12603  {
12604  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12605  const Node* const leftChild = node->split.leftChild;
12606  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12607  const Node* const rightChild = leftChild->buddy;
12608  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12609  }
12610  break;
12611  default:
12612  VMA_ASSERT(0);
12613  }
12614 }
12615 #endif // #if VMA_STATS_STRING_ENABLED
12616 
12617 
12619 // class VmaDeviceMemoryBlock
12620 
12621 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12622  m_pMetadata(VMA_NULL),
12623  m_MemoryTypeIndex(UINT32_MAX),
12624  m_Id(0),
12625  m_hMemory(VK_NULL_HANDLE),
12626  m_MapCount(0),
12627  m_pMappedData(VMA_NULL)
12628 {
12629 }
12630 
12631 void VmaDeviceMemoryBlock::Init(
12632  VmaAllocator hAllocator,
12633  VmaPool hParentPool,
12634  uint32_t newMemoryTypeIndex,
12635  VkDeviceMemory newMemory,
12636  VkDeviceSize newSize,
12637  uint32_t id,
12638  uint32_t algorithm)
12639 {
12640  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12641 
12642  m_hParentPool = hParentPool;
12643  m_MemoryTypeIndex = newMemoryTypeIndex;
12644  m_Id = id;
12645  m_hMemory = newMemory;
12646 
12647  switch(algorithm)
12648  {
12650  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12651  break;
12653  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12654  break;
12655  default:
12656  VMA_ASSERT(0);
12657  // Fall-through.
12658  case 0:
12659  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12660  }
12661  m_pMetadata->Init(newSize);
12662 }
12663 
12664 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12665 {
12666  // This is the most important assert in the entire library.
12667  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12668  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12669 
12670  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12671  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12672  m_hMemory = VK_NULL_HANDLE;
12673 
12674  vma_delete(allocator, m_pMetadata);
12675  m_pMetadata = VMA_NULL;
12676 }
12677 
12678 bool VmaDeviceMemoryBlock::Validate() const
12679 {
12680  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12681  (m_pMetadata->GetSize() != 0));
12682 
12683  return m_pMetadata->Validate();
12684 }
12685 
12686 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12687 {
12688  void* pData = nullptr;
12689  VkResult res = Map(hAllocator, 1, &pData);
12690  if(res != VK_SUCCESS)
12691  {
12692  return res;
12693  }
12694 
12695  res = m_pMetadata->CheckCorruption(pData);
12696 
12697  Unmap(hAllocator, 1);
12698 
12699  return res;
12700 }
12701 
12702 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12703 {
12704  if(count == 0)
12705  {
12706  return VK_SUCCESS;
12707  }
12708 
12709  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12710  if(m_MapCount != 0)
12711  {
12712  m_MapCount += count;
12713  VMA_ASSERT(m_pMappedData != VMA_NULL);
12714  if(ppData != VMA_NULL)
12715  {
12716  *ppData = m_pMappedData;
12717  }
12718  return VK_SUCCESS;
12719  }
12720  else
12721  {
12722  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12723  hAllocator->m_hDevice,
12724  m_hMemory,
12725  0, // offset
12726  VK_WHOLE_SIZE,
12727  0, // flags
12728  &m_pMappedData);
12729  if(result == VK_SUCCESS)
12730  {
12731  if(ppData != VMA_NULL)
12732  {
12733  *ppData = m_pMappedData;
12734  }
12735  m_MapCount = count;
12736  }
12737  return result;
12738  }
12739 }
12740 
12741 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12742 {
12743  if(count == 0)
12744  {
12745  return;
12746  }
12747 
12748  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12749  if(m_MapCount >= count)
12750  {
12751  m_MapCount -= count;
12752  if(m_MapCount == 0)
12753  {
12754  m_pMappedData = VMA_NULL;
12755  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12756  }
12757  }
12758  else
12759  {
12760  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12761  }
12762 }
12763 
12764 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12765 {
12766  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12767  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12768 
12769  void* pData;
12770  VkResult res = Map(hAllocator, 1, &pData);
12771  if(res != VK_SUCCESS)
12772  {
12773  return res;
12774  }
12775 
12776  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12777  VmaWriteMagicValue(pData, allocOffset + allocSize);
12778 
12779  Unmap(hAllocator, 1);
12780 
12781  return VK_SUCCESS;
12782 }
12783 
12784 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12785 {
12786  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12787  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12788 
12789  void* pData;
12790  VkResult res = Map(hAllocator, 1, &pData);
12791  if(res != VK_SUCCESS)
12792  {
12793  return res;
12794  }
12795 
12796  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12797  {
12798  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12799  }
12800  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12801  {
12802  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12803  }
12804 
12805  Unmap(hAllocator, 1);
12806 
12807  return VK_SUCCESS;
12808 }
12809 
12810 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12811  const VmaAllocator hAllocator,
12812  const VmaAllocation hAllocation,
12813  VkDeviceSize allocationLocalOffset,
12814  VkBuffer hBuffer,
12815  const void* pNext)
12816 {
12817  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12818  hAllocation->GetBlock() == this);
12819  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12820  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12821  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12822  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12823  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12824  return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12825 }
12826 
12827 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12828  const VmaAllocator hAllocator,
12829  const VmaAllocation hAllocation,
12830  VkDeviceSize allocationLocalOffset,
12831  VkImage hImage,
12832  const void* pNext)
12833 {
12834  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12835  hAllocation->GetBlock() == this);
12836  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12837  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12838  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12839  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12840  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12841  return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12842 }
12843 
12844 static void InitStatInfo(VmaStatInfo& outInfo)
12845 {
12846  memset(&outInfo, 0, sizeof(outInfo));
12847  outInfo.allocationSizeMin = UINT64_MAX;
12848  outInfo.unusedRangeSizeMin = UINT64_MAX;
12849 }
12850 
12851 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12852 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12853 {
12854  inoutInfo.blockCount += srcInfo.blockCount;
12855  inoutInfo.allocationCount += srcInfo.allocationCount;
12856  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12857  inoutInfo.usedBytes += srcInfo.usedBytes;
12858  inoutInfo.unusedBytes += srcInfo.unusedBytes;
12859  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12860  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12861  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12862  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12863 }
12864 
12865 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12866 {
12867  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12868  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12869  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12870  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12871 }
12872 
12873 VmaPool_T::VmaPool_T(
12874  VmaAllocator hAllocator,
12875  const VmaPoolCreateInfo& createInfo,
12876  VkDeviceSize preferredBlockSize) :
12877  m_BlockVector(
12878  hAllocator,
12879  this, // hParentPool
12880  createInfo.memoryTypeIndex,
12881  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12882  createInfo.minBlockCount,
12883  createInfo.maxBlockCount,
12884  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12885  createInfo.frameInUseCount,
12886  createInfo.blockSize != 0, // explicitBlockSize
12887  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
12888  createInfo.priority,
12889  VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment)),
12890  m_Id(0),
12891  m_Name(VMA_NULL)
12892 {
12893 }
12894 
12895 VmaPool_T::~VmaPool_T()
12896 {
12897  VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
12898 }
12899 
12900 void VmaPool_T::SetName(const char* pName)
12901 {
12902  const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12903  VmaFreeString(allocs, m_Name);
12904 
12905  if(pName != VMA_NULL)
12906  {
12907  m_Name = VmaCreateStringCopy(allocs, pName);
12908  }
12909  else
12910  {
12911  m_Name = VMA_NULL;
12912  }
12913 }
12914 
12915 #if VMA_STATS_STRING_ENABLED
12916 
12917 #endif // #if VMA_STATS_STRING_ENABLED
12918 
12919 VmaBlockVector::VmaBlockVector(
12920  VmaAllocator hAllocator,
12921  VmaPool hParentPool,
12922  uint32_t memoryTypeIndex,
12923  VkDeviceSize preferredBlockSize,
12924  size_t minBlockCount,
12925  size_t maxBlockCount,
12926  VkDeviceSize bufferImageGranularity,
12927  uint32_t frameInUseCount,
12928  bool explicitBlockSize,
12929  uint32_t algorithm,
12930  float priority,
12931  VkDeviceSize minAllocationAlignment) :
12932  m_hAllocator(hAllocator),
12933  m_hParentPool(hParentPool),
12934  m_MemoryTypeIndex(memoryTypeIndex),
12935  m_PreferredBlockSize(preferredBlockSize),
12936  m_MinBlockCount(minBlockCount),
12937  m_MaxBlockCount(maxBlockCount),
12938  m_BufferImageGranularity(bufferImageGranularity),
12939  m_FrameInUseCount(frameInUseCount),
12940  m_ExplicitBlockSize(explicitBlockSize),
12941  m_Algorithm(algorithm),
12942  m_Priority(priority),
12943  m_MinAllocationAlignment(minAllocationAlignment),
12944  m_HasEmptyBlock(false),
12945  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12946  m_NextBlockId(0)
12947 {
12948 }
12949 
12950 VmaBlockVector::~VmaBlockVector()
12951 {
12952  for(size_t i = m_Blocks.size(); i--; )
12953  {
12954  m_Blocks[i]->Destroy(m_hAllocator);
12955  vma_delete(m_hAllocator, m_Blocks[i]);
12956  }
12957 }
12958 
12959 VkResult VmaBlockVector::CreateMinBlocks()
12960 {
12961  for(size_t i = 0; i < m_MinBlockCount; ++i)
12962  {
12963  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12964  if(res != VK_SUCCESS)
12965  {
12966  return res;
12967  }
12968  }
12969  return VK_SUCCESS;
12970 }
12971 
12972 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12973 {
12974  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12975 
12976  const size_t blockCount = m_Blocks.size();
12977 
12978  pStats->size = 0;
12979  pStats->unusedSize = 0;
12980  pStats->allocationCount = 0;
12981  pStats->unusedRangeCount = 0;
12982  pStats->unusedRangeSizeMax = 0;
12983  pStats->blockCount = blockCount;
12984 
12985  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12986  {
12987  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12988  VMA_ASSERT(pBlock);
12989  VMA_HEAVY_ASSERT(pBlock->Validate());
12990  pBlock->m_pMetadata->AddPoolStats(*pStats);
12991  }
12992 }
12993 
12994 bool VmaBlockVector::IsEmpty()
12995 {
12996  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12997  return m_Blocks.empty();
12998 }
12999 
13000 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
13001 {
13002  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
13003  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
13004  (VMA_DEBUG_MARGIN > 0) &&
13005  (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
13006  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
13007 }
13008 
13009 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
13010 
13011 VkResult VmaBlockVector::Allocate(
13012  uint32_t currentFrameIndex,
13013  VkDeviceSize size,
13014  VkDeviceSize alignment,
13015  const VmaAllocationCreateInfo& createInfo,
13016  VmaSuballocationType suballocType,
13017  size_t allocationCount,
13018  VmaAllocation* pAllocations)
13019 {
13020  size_t allocIndex;
13021  VkResult res = VK_SUCCESS;
13022 
13023  alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
13024 
13025  if(IsCorruptionDetectionEnabled())
13026  {
13027  size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
13028  alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
13029  }
13030 
13031  {
13032  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13033  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13034  {
13035  res = AllocatePage(
13036  currentFrameIndex,
13037  size,
13038  alignment,
13039  createInfo,
13040  suballocType,
13041  pAllocations + allocIndex);
13042  if(res != VK_SUCCESS)
13043  {
13044  break;
13045  }
13046  }
13047  }
13048 
13049  if(res != VK_SUCCESS)
13050  {
13051  // Free all already created allocations.
13052  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13053  while(allocIndex--)
13054  {
13055  VmaAllocation_T* const alloc = pAllocations[allocIndex];
13056  const VkDeviceSize allocSize = alloc->GetSize();
13057  Free(alloc);
13058  m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize);
13059  }
13060  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
13061  }
13062 
13063  return res;
13064 }
13065 
13066 VkResult VmaBlockVector::AllocatePage(
13067  uint32_t currentFrameIndex,
13068  VkDeviceSize size,
13069  VkDeviceSize alignment,
13070  const VmaAllocationCreateInfo& createInfo,
13071  VmaSuballocationType suballocType,
13072  VmaAllocation* pAllocation)
13073 {
13074  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13075  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
13076  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13077  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13078 
13079  VkDeviceSize freeMemory;
13080  {
13081  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13082  VmaBudget heapBudget = {};
13083  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13084  freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
13085  }
13086 
13087  const bool canFallbackToDedicated = !IsCustomPool();
13088  const bool canCreateNewBlock =
13089  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
13090  (m_Blocks.size() < m_MaxBlockCount) &&
13091  (freeMemory >= size || !canFallbackToDedicated);
13092  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
13093 
13094  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
13095  // Which in turn is available only when maxBlockCount = 1.
13096  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
13097  {
13098  canMakeOtherLost = false;
13099  }
13100 
13101  // Upper address can only be used with linear allocator and within single memory block.
13102  if(isUpperAddress &&
13103  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
13104  {
13105  return VK_ERROR_FEATURE_NOT_PRESENT;
13106  }
13107 
13108  // Validate strategy.
13109  switch(strategy)
13110  {
13111  case 0:
13113  break;
13117  break;
13118  default:
13119  return VK_ERROR_FEATURE_NOT_PRESENT;
13120  }
13121 
13122  // Early reject: requested allocation size is larger that maximum block size for this block vector.
13123  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
13124  {
13125  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13126  }
13127 
13128  /*
13129  Under certain condition, this whole section can be skipped for optimization, so
13130  we move on directly to trying to allocate with canMakeOtherLost. That's the case
13131  e.g. for custom pools with linear algorithm.
13132  */
13133  if(!canMakeOtherLost || canCreateNewBlock)
13134  {
13135  // 1. Search existing allocations. Try to allocate without making other allocations lost.
13136  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
13138 
13139  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13140  {
13141  // Use only last block.
13142  if(!m_Blocks.empty())
13143  {
13144  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
13145  VMA_ASSERT(pCurrBlock);
13146  VkResult res = AllocateFromBlock(
13147  pCurrBlock,
13148  currentFrameIndex,
13149  size,
13150  alignment,
13151  allocFlagsCopy,
13152  createInfo.pUserData,
13153  suballocType,
13154  strategy,
13155  pAllocation);
13156  if(res == VK_SUCCESS)
13157  {
13158  VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
13159  return VK_SUCCESS;
13160  }
13161  }
13162  }
13163  else
13164  {
13166  {
13167  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13168  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13169  {
13170  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13171  VMA_ASSERT(pCurrBlock);
13172  VkResult res = AllocateFromBlock(
13173  pCurrBlock,
13174  currentFrameIndex,
13175  size,
13176  alignment,
13177  allocFlagsCopy,
13178  createInfo.pUserData,
13179  suballocType,
13180  strategy,
13181  pAllocation);
13182  if(res == VK_SUCCESS)
13183  {
13184  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13185  return VK_SUCCESS;
13186  }
13187  }
13188  }
13189  else // WORST_FIT, FIRST_FIT
13190  {
13191  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13192  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13193  {
13194  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13195  VMA_ASSERT(pCurrBlock);
13196  VkResult res = AllocateFromBlock(
13197  pCurrBlock,
13198  currentFrameIndex,
13199  size,
13200  alignment,
13201  allocFlagsCopy,
13202  createInfo.pUserData,
13203  suballocType,
13204  strategy,
13205  pAllocation);
13206  if(res == VK_SUCCESS)
13207  {
13208  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13209  return VK_SUCCESS;
13210  }
13211  }
13212  }
13213  }
13214 
13215  // 2. Try to create new block.
13216  if(canCreateNewBlock)
13217  {
13218  // Calculate optimal size for new block.
13219  VkDeviceSize newBlockSize = m_PreferredBlockSize;
13220  uint32_t newBlockSizeShift = 0;
13221  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
13222 
13223  if(!m_ExplicitBlockSize)
13224  {
13225  // Allocate 1/8, 1/4, 1/2 as first blocks.
13226  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
13227  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
13228  {
13229  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13230  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
13231  {
13232  newBlockSize = smallerNewBlockSize;
13233  ++newBlockSizeShift;
13234  }
13235  else
13236  {
13237  break;
13238  }
13239  }
13240  }
13241 
13242  size_t newBlockIndex = 0;
13243  VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13244  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13245  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
13246  if(!m_ExplicitBlockSize)
13247  {
13248  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
13249  {
13250  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13251  if(smallerNewBlockSize >= size)
13252  {
13253  newBlockSize = smallerNewBlockSize;
13254  ++newBlockSizeShift;
13255  res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13256  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13257  }
13258  else
13259  {
13260  break;
13261  }
13262  }
13263  }
13264 
13265  if(res == VK_SUCCESS)
13266  {
13267  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
13268  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
13269 
13270  res = AllocateFromBlock(
13271  pBlock,
13272  currentFrameIndex,
13273  size,
13274  alignment,
13275  allocFlagsCopy,
13276  createInfo.pUserData,
13277  suballocType,
13278  strategy,
13279  pAllocation);
13280  if(res == VK_SUCCESS)
13281  {
13282  VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
13283  return VK_SUCCESS;
13284  }
13285  else
13286  {
13287  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
13288  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13289  }
13290  }
13291  }
13292  }
13293 
13294  // 3. Try to allocate from existing blocks with making other allocations lost.
13295  if(canMakeOtherLost)
13296  {
13297  uint32_t tryIndex = 0;
13298  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
13299  {
13300  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
13301  VmaAllocationRequest bestRequest = {};
13302  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
13303 
13304  // 1. Search existing allocations.
13306  {
13307  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13308  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13309  {
13310  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13311  VMA_ASSERT(pCurrBlock);
13312  VmaAllocationRequest currRequest = {};
13313  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13314  currentFrameIndex,
13315  m_FrameInUseCount,
13316  m_BufferImageGranularity,
13317  size,
13318  alignment,
13319  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13320  suballocType,
13321  canMakeOtherLost,
13322  strategy,
13323  &currRequest))
13324  {
13325  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13326  if(pBestRequestBlock == VMA_NULL ||
13327  currRequestCost < bestRequestCost)
13328  {
13329  pBestRequestBlock = pCurrBlock;
13330  bestRequest = currRequest;
13331  bestRequestCost = currRequestCost;
13332 
13333  if(bestRequestCost == 0)
13334  {
13335  break;
13336  }
13337  }
13338  }
13339  }
13340  }
13341  else // WORST_FIT, FIRST_FIT
13342  {
13343  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13344  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13345  {
13346  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13347  VMA_ASSERT(pCurrBlock);
13348  VmaAllocationRequest currRequest = {};
13349  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13350  currentFrameIndex,
13351  m_FrameInUseCount,
13352  m_BufferImageGranularity,
13353  size,
13354  alignment,
13355  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13356  suballocType,
13357  canMakeOtherLost,
13358  strategy,
13359  &currRequest))
13360  {
13361  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13362  if(pBestRequestBlock == VMA_NULL ||
13363  currRequestCost < bestRequestCost ||
13365  {
13366  pBestRequestBlock = pCurrBlock;
13367  bestRequest = currRequest;
13368  bestRequestCost = currRequestCost;
13369 
13370  if(bestRequestCost == 0 ||
13372  {
13373  break;
13374  }
13375  }
13376  }
13377  }
13378  }
13379 
13380  if(pBestRequestBlock != VMA_NULL)
13381  {
13382  if(mapped)
13383  {
13384  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13385  if(res != VK_SUCCESS)
13386  {
13387  return res;
13388  }
13389  }
13390 
13391  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13392  currentFrameIndex,
13393  m_FrameInUseCount,
13394  &bestRequest))
13395  {
13396  // Allocate from this pBlock.
13397  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13398  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13399  UpdateHasEmptyBlock();
13400  (*pAllocation)->InitBlockAllocation(
13401  pBestRequestBlock,
13402  bestRequest.offset,
13403  alignment,
13404  size,
13405  m_MemoryTypeIndex,
13406  suballocType,
13407  mapped,
13408  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13409  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13410  VMA_DEBUG_LOG(" Returned from existing block");
13411  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13412  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13413  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13414  {
13415  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13416  }
13417  if(IsCorruptionDetectionEnabled())
13418  {
13419  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13420  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13421  }
13422  return VK_SUCCESS;
13423  }
13424  // else: Some allocations must have been touched while we are here. Next try.
13425  }
13426  else
13427  {
13428  // Could not find place in any of the blocks - break outer loop.
13429  break;
13430  }
13431  }
13432  /* Maximum number of tries exceeded - a very unlike event when many other
13433  threads are simultaneously touching allocations making it impossible to make
13434  lost at the same time as we try to allocate. */
13435  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13436  {
13437  return VK_ERROR_TOO_MANY_OBJECTS;
13438  }
13439  }
13440 
13441  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13442 }
13443 
13444 void VmaBlockVector::Free(
13445  const VmaAllocation hAllocation)
13446 {
13447  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13448 
13449  bool budgetExceeded = false;
13450  {
13451  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13452  VmaBudget heapBudget = {};
13453  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13454  budgetExceeded = heapBudget.usage >= heapBudget.budget;
13455  }
13456 
13457  // Scope for lock.
13458  {
13459  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13460 
13461  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13462 
13463  if(IsCorruptionDetectionEnabled())
13464  {
13465  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13466  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13467  }
13468 
13469  if(hAllocation->IsPersistentMap())
13470  {
13471  pBlock->Unmap(m_hAllocator, 1);
13472  }
13473 
13474  pBlock->m_pMetadata->Free(hAllocation);
13475  VMA_HEAVY_ASSERT(pBlock->Validate());
13476 
13477  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13478 
13479  const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13480  // pBlock became empty after this deallocation.
13481  if(pBlock->m_pMetadata->IsEmpty())
13482  {
13483  // Already has empty block. We don't want to have two, so delete this one.
13484  if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13485  {
13486  pBlockToDelete = pBlock;
13487  Remove(pBlock);
13488  }
13489  // else: We now have an empty block - leave it.
13490  }
13491  // pBlock didn't become empty, but we have another empty block - find and free that one.
13492  // (This is optional, heuristics.)
13493  else if(m_HasEmptyBlock && canDeleteBlock)
13494  {
13495  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13496  if(pLastBlock->m_pMetadata->IsEmpty())
13497  {
13498  pBlockToDelete = pLastBlock;
13499  m_Blocks.pop_back();
13500  }
13501  }
13502 
13503  UpdateHasEmptyBlock();
13504  IncrementallySortBlocks();
13505  }
13506 
13507  // Destruction of a free block. Deferred until this point, outside of mutex
13508  // lock, for performance reason.
13509  if(pBlockToDelete != VMA_NULL)
13510  {
13511  VMA_DEBUG_LOG(" Deleted empty block");
13512  pBlockToDelete->Destroy(m_hAllocator);
13513  vma_delete(m_hAllocator, pBlockToDelete);
13514  }
13515 }
13516 
13517 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13518 {
13519  VkDeviceSize result = 0;
13520  for(size_t i = m_Blocks.size(); i--; )
13521  {
13522  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13523  if(result >= m_PreferredBlockSize)
13524  {
13525  break;
13526  }
13527  }
13528  return result;
13529 }
13530 
13531 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13532 {
13533  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13534  {
13535  if(m_Blocks[blockIndex] == pBlock)
13536  {
13537  VmaVectorRemove(m_Blocks, blockIndex);
13538  return;
13539  }
13540  }
13541  VMA_ASSERT(0);
13542 }
13543 
13544 void VmaBlockVector::IncrementallySortBlocks()
13545 {
13546  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13547  {
13548  // Bubble sort only until first swap.
13549  for(size_t i = 1; i < m_Blocks.size(); ++i)
13550  {
13551  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13552  {
13553  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13554  return;
13555  }
13556  }
13557  }
13558 }
13559 
13560 VkResult VmaBlockVector::AllocateFromBlock(
13561  VmaDeviceMemoryBlock* pBlock,
13562  uint32_t currentFrameIndex,
13563  VkDeviceSize size,
13564  VkDeviceSize alignment,
13565  VmaAllocationCreateFlags allocFlags,
13566  void* pUserData,
13567  VmaSuballocationType suballocType,
13568  uint32_t strategy,
13569  VmaAllocation* pAllocation)
13570 {
13571  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13572  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13573  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13574  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13575 
13576  VmaAllocationRequest currRequest = {};
13577  if(pBlock->m_pMetadata->CreateAllocationRequest(
13578  currentFrameIndex,
13579  m_FrameInUseCount,
13580  m_BufferImageGranularity,
13581  size,
13582  alignment,
13583  isUpperAddress,
13584  suballocType,
13585  false, // canMakeOtherLost
13586  strategy,
13587  &currRequest))
13588  {
13589  // Allocate from pCurrBlock.
13590  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13591 
13592  if(mapped)
13593  {
13594  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13595  if(res != VK_SUCCESS)
13596  {
13597  return res;
13598  }
13599  }
13600 
13601  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13602  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13603  UpdateHasEmptyBlock();
13604  (*pAllocation)->InitBlockAllocation(
13605  pBlock,
13606  currRequest.offset,
13607  alignment,
13608  size,
13609  m_MemoryTypeIndex,
13610  suballocType,
13611  mapped,
13612  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13613  VMA_HEAVY_ASSERT(pBlock->Validate());
13614  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13615  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13616  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13617  {
13618  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13619  }
13620  if(IsCorruptionDetectionEnabled())
13621  {
13622  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13623  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13624  }
13625  return VK_SUCCESS;
13626  }
13627  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13628 }
13629 
13630 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13631 {
13632  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13633  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13634  allocInfo.allocationSize = blockSize;
13635 
13636 #if VMA_BUFFER_DEVICE_ADDRESS
13637  // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13638  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13639  if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13640  {
13641  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13642  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13643  }
13644 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13645 
13646 #if VMA_MEMORY_PRIORITY
13647  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
13648  if(m_hAllocator->m_UseExtMemoryPriority)
13649  {
13650  priorityInfo.priority = m_Priority;
13651  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
13652  }
13653 #endif // #if VMA_MEMORY_PRIORITY
13654 
13655  VkDeviceMemory mem = VK_NULL_HANDLE;
13656  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13657  if(res < 0)
13658  {
13659  return res;
13660  }
13661 
13662  // New VkDeviceMemory successfully created.
13663 
13664  // Create new Allocation for it.
13665  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13666  pBlock->Init(
13667  m_hAllocator,
13668  m_hParentPool,
13669  m_MemoryTypeIndex,
13670  mem,
13671  allocInfo.allocationSize,
13672  m_NextBlockId++,
13673  m_Algorithm);
13674 
13675  m_Blocks.push_back(pBlock);
13676  if(pNewBlockIndex != VMA_NULL)
13677  {
13678  *pNewBlockIndex = m_Blocks.size() - 1;
13679  }
13680 
13681  return VK_SUCCESS;
13682 }
13683 
13684 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13685  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13686  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13687 {
13688  const size_t blockCount = m_Blocks.size();
13689  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13690 
13691  enum BLOCK_FLAG
13692  {
13693  BLOCK_FLAG_USED = 0x00000001,
13694  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13695  };
13696 
13697  struct BlockInfo
13698  {
13699  uint32_t flags;
13700  void* pMappedData;
13701  };
13702  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13703  blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13704  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13705 
13706  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13707  const size_t moveCount = moves.size();
13708  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13709  {
13710  const VmaDefragmentationMove& move = moves[moveIndex];
13711  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13712  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13713  }
13714 
13715  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13716 
13717  // Go over all blocks. Get mapped pointer or map if necessary.
13718  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13719  {
13720  BlockInfo& currBlockInfo = blockInfo[blockIndex];
13721  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13722  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13723  {
13724  currBlockInfo.pMappedData = pBlock->GetMappedData();
13725  // It is not originally mapped - map it.
13726  if(currBlockInfo.pMappedData == VMA_NULL)
13727  {
13728  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13729  if(pDefragCtx->res == VK_SUCCESS)
13730  {
13731  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13732  }
13733  }
13734  }
13735  }
13736 
13737  // Go over all moves. Do actual data transfer.
13738  if(pDefragCtx->res == VK_SUCCESS)
13739  {
13740  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13741  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13742 
13743  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13744  {
13745  const VmaDefragmentationMove& move = moves[moveIndex];
13746 
13747  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13748  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13749 
13750  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13751 
13752  // Invalidate source.
13753  if(isNonCoherent)
13754  {
13755  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13756  memRange.memory = pSrcBlock->GetDeviceMemory();
13757  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13758  memRange.size = VMA_MIN(
13759  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13760  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13761  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13762  }
13763 
13764  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13765  memmove(
13766  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13767  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13768  static_cast<size_t>(move.size));
13769 
13770  if(IsCorruptionDetectionEnabled())
13771  {
13772  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13773  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13774  }
13775 
13776  // Flush destination.
13777  if(isNonCoherent)
13778  {
13779  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13780  memRange.memory = pDstBlock->GetDeviceMemory();
13781  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13782  memRange.size = VMA_MIN(
13783  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13784  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13785  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13786  }
13787  }
13788  }
13789 
13790  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13791  // Regardless of pCtx->res == VK_SUCCESS.
13792  for(size_t blockIndex = blockCount; blockIndex--; )
13793  {
13794  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13795  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13796  {
13797  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13798  pBlock->Unmap(m_hAllocator, 1);
13799  }
13800  }
13801 }
13802 
13803 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13804  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13805  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13806  VkCommandBuffer commandBuffer)
13807 {
13808  const size_t blockCount = m_Blocks.size();
13809 
13810  pDefragCtx->blockContexts.resize(blockCount);
13811  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13812 
13813  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13814  const size_t moveCount = moves.size();
13815  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13816  {
13817  const VmaDefragmentationMove& move = moves[moveIndex];
13818 
13819  //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13820  {
13821  // Old school move still require us to map the whole block
13822  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13823  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13824  }
13825  }
13826 
13827  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13828 
13829  // Go over all blocks. Create and bind buffer for whole block if necessary.
13830  {
13831  VkBufferCreateInfo bufCreateInfo;
13832  VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13833 
13834  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13835  {
13836  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13837  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13838  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13839  {
13840  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13841  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13842  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13843  if(pDefragCtx->res == VK_SUCCESS)
13844  {
13845  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13846  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13847  }
13848  }
13849  }
13850  }
13851 
13852  // Go over all moves. Post data transfer commands to command buffer.
13853  if(pDefragCtx->res == VK_SUCCESS)
13854  {
13855  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13856  {
13857  const VmaDefragmentationMove& move = moves[moveIndex];
13858 
13859  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13860  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13861 
13862  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13863 
13864  VkBufferCopy region = {
13865  move.srcOffset,
13866  move.dstOffset,
13867  move.size };
13868  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13869  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13870  }
13871  }
13872 
13873  // Save buffers to defrag context for later destruction.
13874  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13875  {
13876  pDefragCtx->res = VK_NOT_READY;
13877  }
13878 }
13879 
13880 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13881 {
13882  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13883  {
13884  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13885  if(pBlock->m_pMetadata->IsEmpty())
13886  {
13887  if(m_Blocks.size() > m_MinBlockCount)
13888  {
13889  if(pDefragmentationStats != VMA_NULL)
13890  {
13891  ++pDefragmentationStats->deviceMemoryBlocksFreed;
13892  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13893  }
13894 
13895  VmaVectorRemove(m_Blocks, blockIndex);
13896  pBlock->Destroy(m_hAllocator);
13897  vma_delete(m_hAllocator, pBlock);
13898  }
13899  else
13900  {
13901  break;
13902  }
13903  }
13904  }
13905  UpdateHasEmptyBlock();
13906 }
13907 
13908 void VmaBlockVector::UpdateHasEmptyBlock()
13909 {
13910  m_HasEmptyBlock = false;
13911  for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13912  {
13913  VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13914  if(pBlock->m_pMetadata->IsEmpty())
13915  {
13916  m_HasEmptyBlock = true;
13917  break;
13918  }
13919  }
13920 }
13921 
13922 #if VMA_STATS_STRING_ENABLED
13923 
13924 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13925 {
13926  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13927 
13928  json.BeginObject();
13929 
13930  if(IsCustomPool())
13931  {
13932  const char* poolName = m_hParentPool->GetName();
13933  if(poolName != VMA_NULL && poolName[0] != '\0')
13934  {
13935  json.WriteString("Name");
13936  json.WriteString(poolName);
13937  }
13938 
13939  json.WriteString("MemoryTypeIndex");
13940  json.WriteNumber(m_MemoryTypeIndex);
13941 
13942  json.WriteString("BlockSize");
13943  json.WriteNumber(m_PreferredBlockSize);
13944 
13945  json.WriteString("BlockCount");
13946  json.BeginObject(true);
13947  if(m_MinBlockCount > 0)
13948  {
13949  json.WriteString("Min");
13950  json.WriteNumber((uint64_t)m_MinBlockCount);
13951  }
13952  if(m_MaxBlockCount < SIZE_MAX)
13953  {
13954  json.WriteString("Max");
13955  json.WriteNumber((uint64_t)m_MaxBlockCount);
13956  }
13957  json.WriteString("Cur");
13958  json.WriteNumber((uint64_t)m_Blocks.size());
13959  json.EndObject();
13960 
13961  if(m_FrameInUseCount > 0)
13962  {
13963  json.WriteString("FrameInUseCount");
13964  json.WriteNumber(m_FrameInUseCount);
13965  }
13966 
13967  if(m_Algorithm != 0)
13968  {
13969  json.WriteString("Algorithm");
13970  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13971  }
13972  }
13973  else
13974  {
13975  json.WriteString("PreferredBlockSize");
13976  json.WriteNumber(m_PreferredBlockSize);
13977  }
13978 
13979  json.WriteString("Blocks");
13980  json.BeginObject();
13981  for(size_t i = 0; i < m_Blocks.size(); ++i)
13982  {
13983  json.BeginString();
13984  json.ContinueString(m_Blocks[i]->GetId());
13985  json.EndString();
13986 
13987  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13988  }
13989  json.EndObject();
13990 
13991  json.EndObject();
13992 }
13993 
13994 #endif // #if VMA_STATS_STRING_ENABLED
13995 
13996 void VmaBlockVector::Defragment(
13997  class VmaBlockVectorDefragmentationContext* pCtx,
13999  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
14000  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
14001  VkCommandBuffer commandBuffer)
14002 {
14003  pCtx->res = VK_SUCCESS;
14004 
14005  const VkMemoryPropertyFlags memPropFlags =
14006  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
14007  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
14008 
14009  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
14010  isHostVisible;
14011  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
14012  !IsCorruptionDetectionEnabled() &&
14013  ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
14014 
14015  // There are options to defragment this memory type.
14016  if(canDefragmentOnCpu || canDefragmentOnGpu)
14017  {
14018  bool defragmentOnGpu;
14019  // There is only one option to defragment this memory type.
14020  if(canDefragmentOnGpu != canDefragmentOnCpu)
14021  {
14022  defragmentOnGpu = canDefragmentOnGpu;
14023  }
14024  // Both options are available: Heuristics to choose the best one.
14025  else
14026  {
14027  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
14028  m_hAllocator->IsIntegratedGpu();
14029  }
14030 
14031  bool overlappingMoveSupported = !defragmentOnGpu;
14032 
14033  if(m_hAllocator->m_UseMutex)
14034  {
14036  {
14037  if(!m_Mutex.TryLockWrite())
14038  {
14039  pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
14040  return;
14041  }
14042  }
14043  else
14044  {
14045  m_Mutex.LockWrite();
14046  pCtx->mutexLocked = true;
14047  }
14048  }
14049 
14050  pCtx->Begin(overlappingMoveSupported, flags);
14051 
14052  // Defragment.
14053 
14054  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
14055  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
14056  pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
14057 
14058  // Accumulate statistics.
14059  if(pStats != VMA_NULL)
14060  {
14061  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
14062  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
14063  pStats->bytesMoved += bytesMoved;
14064  pStats->allocationsMoved += allocationsMoved;
14065  VMA_ASSERT(bytesMoved <= maxBytesToMove);
14066  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
14067  if(defragmentOnGpu)
14068  {
14069  maxGpuBytesToMove -= bytesMoved;
14070  maxGpuAllocationsToMove -= allocationsMoved;
14071  }
14072  else
14073  {
14074  maxCpuBytesToMove -= bytesMoved;
14075  maxCpuAllocationsToMove -= allocationsMoved;
14076  }
14077  }
14078 
14080  {
14081  if(m_hAllocator->m_UseMutex)
14082  m_Mutex.UnlockWrite();
14083 
14084  if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
14085  pCtx->res = VK_NOT_READY;
14086 
14087  return;
14088  }
14089 
14090  if(pCtx->res >= VK_SUCCESS)
14091  {
14092  if(defragmentOnGpu)
14093  {
14094  ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
14095  }
14096  else
14097  {
14098  ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
14099  }
14100  }
14101  }
14102 }
14103 
14104 void VmaBlockVector::DefragmentationEnd(
14105  class VmaBlockVectorDefragmentationContext* pCtx,
14106  uint32_t flags,
14107  VmaDefragmentationStats* pStats)
14108 {
14109  if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
14110  {
14111  VMA_ASSERT(pCtx->mutexLocked == false);
14112 
14113  // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
14114  // lock protecting us. Since we mutate state here, we have to take the lock out now
14115  m_Mutex.LockWrite();
14116  pCtx->mutexLocked = true;
14117  }
14118 
14119  // If the mutex isn't locked we didn't do any work and there is nothing to delete.
14120  if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
14121  {
14122  // Destroy buffers.
14123  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
14124  {
14125  VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
14126  if(blockCtx.hBuffer)
14127  {
14128  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
14129  }
14130  }
14131 
14132  if(pCtx->res >= VK_SUCCESS)
14133  {
14134  FreeEmptyBlocks(pStats);
14135  }
14136  }
14137 
14138  if(pCtx->mutexLocked)
14139  {
14140  VMA_ASSERT(m_hAllocator->m_UseMutex);
14141  m_Mutex.UnlockWrite();
14142  }
14143 }
14144 
14145 uint32_t VmaBlockVector::ProcessDefragmentations(
14146  class VmaBlockVectorDefragmentationContext *pCtx,
14147  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
14148 {
14149  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14150 
14151  const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
14152 
14153  for(uint32_t i = 0; i < moveCount; ++ i)
14154  {
14155  VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
14156 
14157  pMove->allocation = move.hAllocation;
14158  pMove->memory = move.pDstBlock->GetDeviceMemory();
14159  pMove->offset = move.dstOffset;
14160 
14161  ++ pMove;
14162  }
14163 
14164  pCtx->defragmentationMovesProcessed += moveCount;
14165 
14166  return moveCount;
14167 }
14168 
14169 void VmaBlockVector::CommitDefragmentations(
14170  class VmaBlockVectorDefragmentationContext *pCtx,
14171  VmaDefragmentationStats* pStats)
14172 {
14173  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14174 
14175  for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
14176  {
14177  const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
14178 
14179  move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
14180  move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
14181  }
14182 
14183  pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
14184  FreeEmptyBlocks(pStats);
14185 }
14186 
14187 size_t VmaBlockVector::CalcAllocationCount() const
14188 {
14189  size_t result = 0;
14190  for(size_t i = 0; i < m_Blocks.size(); ++i)
14191  {
14192  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
14193  }
14194  return result;
14195 }
14196 
14197 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
14198 {
14199  if(m_BufferImageGranularity == 1)
14200  {
14201  return false;
14202  }
14203  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
14204  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
14205  {
14206  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
14207  VMA_ASSERT(m_Algorithm == 0);
14208  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
14209  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
14210  {
14211  return true;
14212  }
14213  }
14214  return false;
14215 }
14216 
14217 void VmaBlockVector::MakePoolAllocationsLost(
14218  uint32_t currentFrameIndex,
14219  size_t* pLostAllocationCount)
14220 {
14221  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14222  size_t lostAllocationCount = 0;
14223  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14224  {
14225  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14226  VMA_ASSERT(pBlock);
14227  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
14228  }
14229  if(pLostAllocationCount != VMA_NULL)
14230  {
14231  *pLostAllocationCount = lostAllocationCount;
14232  }
14233 }
14234 
14235 VkResult VmaBlockVector::CheckCorruption()
14236 {
14237  if(!IsCorruptionDetectionEnabled())
14238  {
14239  return VK_ERROR_FEATURE_NOT_PRESENT;
14240  }
14241 
14242  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14243  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14244  {
14245  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14246  VMA_ASSERT(pBlock);
14247  VkResult res = pBlock->CheckCorruption(m_hAllocator);
14248  if(res != VK_SUCCESS)
14249  {
14250  return res;
14251  }
14252  }
14253  return VK_SUCCESS;
14254 }
14255 
14256 void VmaBlockVector::AddStats(VmaStats* pStats)
14257 {
14258  const uint32_t memTypeIndex = m_MemoryTypeIndex;
14259  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
14260 
14261  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14262 
14263  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14264  {
14265  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14266  VMA_ASSERT(pBlock);
14267  VMA_HEAVY_ASSERT(pBlock->Validate());
14268  VmaStatInfo allocationStatInfo;
14269  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
14270  VmaAddStatInfo(pStats->total, allocationStatInfo);
14271  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14272  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14273  }
14274 }
14275 
14277 // VmaDefragmentationAlgorithm_Generic members definition
14278 
14279 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
14280  VmaAllocator hAllocator,
14281  VmaBlockVector* pBlockVector,
14282  uint32_t currentFrameIndex,
14283  bool overlappingMoveSupported) :
14284  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14285  m_AllocationCount(0),
14286  m_AllAllocations(false),
14287  m_BytesMoved(0),
14288  m_AllocationsMoved(0),
14289  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
14290 {
14291  // Create block info for each block.
14292  const size_t blockCount = m_pBlockVector->m_Blocks.size();
14293  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14294  {
14295  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
14296  pBlockInfo->m_OriginalBlockIndex = blockIndex;
14297  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
14298  m_Blocks.push_back(pBlockInfo);
14299  }
14300 
14301  // Sort them by m_pBlock pointer value.
14302  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
14303 }
14304 
14305 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14306 {
14307  for(size_t i = m_Blocks.size(); i--; )
14308  {
14309  vma_delete(m_hAllocator, m_Blocks[i]);
14310  }
14311 }
14312 
14313 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14314 {
14315  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14316  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14317  {
14318  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14319  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14320  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14321  {
14322  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14323  (*it)->m_Allocations.push_back(allocInfo);
14324  }
14325  else
14326  {
14327  VMA_ASSERT(0);
14328  }
14329 
14330  ++m_AllocationCount;
14331  }
14332 }
14333 
14334 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14335  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14336  VkDeviceSize maxBytesToMove,
14337  uint32_t maxAllocationsToMove,
14338  bool freeOldAllocations)
14339 {
14340  if(m_Blocks.empty())
14341  {
14342  return VK_SUCCESS;
14343  }
14344 
14345  // This is a choice based on research.
14346  // Option 1:
14347  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14348  // Option 2:
14349  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14350  // Option 3:
14351  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14352 
14353  size_t srcBlockMinIndex = 0;
14354  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14355  /*
14356  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14357  {
14358  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14359  if(blocksWithNonMovableCount > 0)
14360  {
14361  srcBlockMinIndex = blocksWithNonMovableCount - 1;
14362  }
14363  }
14364  */
14365 
14366  size_t srcBlockIndex = m_Blocks.size() - 1;
14367  size_t srcAllocIndex = SIZE_MAX;
14368  for(;;)
14369  {
14370  // 1. Find next allocation to move.
14371  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14372  // 1.2. Then start from last to first m_Allocations.
14373  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14374  {
14375  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14376  {
14377  // Finished: no more allocations to process.
14378  if(srcBlockIndex == srcBlockMinIndex)
14379  {
14380  return VK_SUCCESS;
14381  }
14382  else
14383  {
14384  --srcBlockIndex;
14385  srcAllocIndex = SIZE_MAX;
14386  }
14387  }
14388  else
14389  {
14390  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14391  }
14392  }
14393 
14394  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14395  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14396 
14397  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14398  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14399  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14400  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14401 
14402  // 2. Try to find new place for this allocation in preceding or current block.
14403  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14404  {
14405  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14406  VmaAllocationRequest dstAllocRequest;
14407  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14408  m_CurrentFrameIndex,
14409  m_pBlockVector->GetFrameInUseCount(),
14410  m_pBlockVector->GetBufferImageGranularity(),
14411  size,
14412  alignment,
14413  false, // upperAddress
14414  suballocType,
14415  false, // canMakeOtherLost
14416  strategy,
14417  &dstAllocRequest) &&
14418  MoveMakesSense(
14419  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14420  {
14421  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14422 
14423  // Reached limit on number of allocations or bytes to move.
14424  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14425  (m_BytesMoved + size > maxBytesToMove))
14426  {
14427  return VK_SUCCESS;
14428  }
14429 
14430  VmaDefragmentationMove move = {};
14431  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14432  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14433  move.srcOffset = srcOffset;
14434  move.dstOffset = dstAllocRequest.offset;
14435  move.size = size;
14436  move.hAllocation = allocInfo.m_hAllocation;
14437  move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14438  move.pDstBlock = pDstBlockInfo->m_pBlock;
14439 
14440  moves.push_back(move);
14441 
14442  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14443  dstAllocRequest,
14444  suballocType,
14445  size,
14446  allocInfo.m_hAllocation);
14447 
14448  if(freeOldAllocations)
14449  {
14450  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14451  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14452  }
14453 
14454  if(allocInfo.m_pChanged != VMA_NULL)
14455  {
14456  *allocInfo.m_pChanged = VK_TRUE;
14457  }
14458 
14459  ++m_AllocationsMoved;
14460  m_BytesMoved += size;
14461 
14462  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14463 
14464  break;
14465  }
14466  }
14467 
14468  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14469 
14470  if(srcAllocIndex > 0)
14471  {
14472  --srcAllocIndex;
14473  }
14474  else
14475  {
14476  if(srcBlockIndex > 0)
14477  {
14478  --srcBlockIndex;
14479  srcAllocIndex = SIZE_MAX;
14480  }
14481  else
14482  {
14483  return VK_SUCCESS;
14484  }
14485  }
14486  }
14487 }
14488 
14489 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14490 {
14491  size_t result = 0;
14492  for(size_t i = 0; i < m_Blocks.size(); ++i)
14493  {
14494  if(m_Blocks[i]->m_HasNonMovableAllocations)
14495  {
14496  ++result;
14497  }
14498  }
14499  return result;
14500 }
14501 
14502 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14503  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14504  VkDeviceSize maxBytesToMove,
14505  uint32_t maxAllocationsToMove,
14507 {
14508  if(!m_AllAllocations && m_AllocationCount == 0)
14509  {
14510  return VK_SUCCESS;
14511  }
14512 
14513  const size_t blockCount = m_Blocks.size();
14514  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14515  {
14516  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14517 
14518  if(m_AllAllocations)
14519  {
14520  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14521  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14522  it != pMetadata->m_Suballocations.end();
14523  ++it)
14524  {
14525  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14526  {
14527  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14528  pBlockInfo->m_Allocations.push_back(allocInfo);
14529  }
14530  }
14531  }
14532 
14533  pBlockInfo->CalcHasNonMovableAllocations();
14534 
14535  // This is a choice based on research.
14536  // Option 1:
14537  pBlockInfo->SortAllocationsByOffsetDescending();
14538  // Option 2:
14539  //pBlockInfo->SortAllocationsBySizeDescending();
14540  }
14541 
14542  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14543  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14544 
14545  // This is a choice based on research.
14546  const uint32_t roundCount = 2;
14547 
14548  // Execute defragmentation rounds (the main part).
14549  VkResult result = VK_SUCCESS;
14550  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14551  {
14552  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14553  }
14554 
14555  return result;
14556 }
14557 
14558 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14559  size_t dstBlockIndex, VkDeviceSize dstOffset,
14560  size_t srcBlockIndex, VkDeviceSize srcOffset)
14561 {
14562  if(dstBlockIndex < srcBlockIndex)
14563  {
14564  return true;
14565  }
14566  if(dstBlockIndex > srcBlockIndex)
14567  {
14568  return false;
14569  }
14570  if(dstOffset < srcOffset)
14571  {
14572  return true;
14573  }
14574  return false;
14575 }
14576 
14578 // VmaDefragmentationAlgorithm_Fast
14579 
14580 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14581  VmaAllocator hAllocator,
14582  VmaBlockVector* pBlockVector,
14583  uint32_t currentFrameIndex,
14584  bool overlappingMoveSupported) :
14585  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14586  m_OverlappingMoveSupported(overlappingMoveSupported),
14587  m_AllocationCount(0),
14588  m_AllAllocations(false),
14589  m_BytesMoved(0),
14590  m_AllocationsMoved(0),
14591  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14592 {
14593  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14594 
14595 }
14596 
14597 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14598 {
14599 }
14600 
14601 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14602  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14603  VkDeviceSize maxBytesToMove,
14604  uint32_t maxAllocationsToMove,
14606 {
14607  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14608 
14609  const size_t blockCount = m_pBlockVector->GetBlockCount();
14610  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14611  {
14612  return VK_SUCCESS;
14613  }
14614 
14615  PreprocessMetadata();
14616 
14617  // Sort blocks in order from most destination.
14618 
14619  m_BlockInfos.resize(blockCount);
14620  for(size_t i = 0; i < blockCount; ++i)
14621  {
14622  m_BlockInfos[i].origBlockIndex = i;
14623  }
14624 
14625  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14626  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14627  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14628  });
14629 
14630  // THE MAIN ALGORITHM
14631 
14632  FreeSpaceDatabase freeSpaceDb;
14633 
14634  size_t dstBlockInfoIndex = 0;
14635  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14636  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14637  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14638  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14639  VkDeviceSize dstOffset = 0;
14640 
14641  bool end = false;
14642  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14643  {
14644  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14645  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14646  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14647  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14648  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14649  {
14650  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14651  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14652  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14653  if(m_AllocationsMoved == maxAllocationsToMove ||
14654  m_BytesMoved + srcAllocSize > maxBytesToMove)
14655  {
14656  end = true;
14657  break;
14658  }
14659  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14660 
14661  VmaDefragmentationMove move = {};
14662  // Try to place it in one of free spaces from the database.
14663  size_t freeSpaceInfoIndex;
14664  VkDeviceSize dstAllocOffset;
14665  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14666  freeSpaceInfoIndex, dstAllocOffset))
14667  {
14668  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14669  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14670  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14671 
14672  // Same block
14673  if(freeSpaceInfoIndex == srcBlockInfoIndex)
14674  {
14675  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14676 
14677  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14678 
14679  VmaSuballocation suballoc = *srcSuballocIt;
14680  suballoc.offset = dstAllocOffset;
14681  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14682  m_BytesMoved += srcAllocSize;
14683  ++m_AllocationsMoved;
14684 
14685  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14686  ++nextSuballocIt;
14687  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14688  srcSuballocIt = nextSuballocIt;
14689 
14690  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14691 
14692  move.srcBlockIndex = srcOrigBlockIndex;
14693  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14694  move.srcOffset = srcAllocOffset;
14695  move.dstOffset = dstAllocOffset;
14696  move.size = srcAllocSize;
14697 
14698  moves.push_back(move);
14699  }
14700  // Different block
14701  else
14702  {
14703  // MOVE OPTION 2: Move the allocation to a different block.
14704 
14705  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14706 
14707  VmaSuballocation suballoc = *srcSuballocIt;
14708  suballoc.offset = dstAllocOffset;
14709  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14710  m_BytesMoved += srcAllocSize;
14711  ++m_AllocationsMoved;
14712 
14713  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14714  ++nextSuballocIt;
14715  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14716  srcSuballocIt = nextSuballocIt;
14717 
14718  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14719 
14720  move.srcBlockIndex = srcOrigBlockIndex;
14721  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14722  move.srcOffset = srcAllocOffset;
14723  move.dstOffset = dstAllocOffset;
14724  move.size = srcAllocSize;
14725 
14726  moves.push_back(move);
14727  }
14728  }
14729  else
14730  {
14731  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14732 
14733  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14734  while(dstBlockInfoIndex < srcBlockInfoIndex &&
14735  dstAllocOffset + srcAllocSize > dstBlockSize)
14736  {
14737  // But before that, register remaining free space at the end of dst block.
14738  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14739 
14740  ++dstBlockInfoIndex;
14741  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14742  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14743  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14744  dstBlockSize = pDstMetadata->GetSize();
14745  dstOffset = 0;
14746  dstAllocOffset = 0;
14747  }
14748 
14749  // Same block
14750  if(dstBlockInfoIndex == srcBlockInfoIndex)
14751  {
14752  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14753 
14754  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14755 
14756  bool skipOver = overlap;
14757  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14758  {
14759  // If destination and source place overlap, skip if it would move it
14760  // by only < 1/64 of its size.
14761  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14762  }
14763 
14764  if(skipOver)
14765  {
14766  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14767 
14768  dstOffset = srcAllocOffset + srcAllocSize;
14769  ++srcSuballocIt;
14770  }
14771  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14772  else
14773  {
14774  srcSuballocIt->offset = dstAllocOffset;
14775  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14776  dstOffset = dstAllocOffset + srcAllocSize;
14777  m_BytesMoved += srcAllocSize;
14778  ++m_AllocationsMoved;
14779  ++srcSuballocIt;
14780 
14781  move.srcBlockIndex = srcOrigBlockIndex;
14782  move.dstBlockIndex = dstOrigBlockIndex;
14783  move.srcOffset = srcAllocOffset;
14784  move.dstOffset = dstAllocOffset;
14785  move.size = srcAllocSize;
14786 
14787  moves.push_back(move);
14788  }
14789  }
14790  // Different block
14791  else
14792  {
14793  // MOVE OPTION 2: Move the allocation to a different block.
14794 
14795  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14796  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14797 
14798  VmaSuballocation suballoc = *srcSuballocIt;
14799  suballoc.offset = dstAllocOffset;
14800  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14801  dstOffset = dstAllocOffset + srcAllocSize;
14802  m_BytesMoved += srcAllocSize;
14803  ++m_AllocationsMoved;
14804 
14805  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14806  ++nextSuballocIt;
14807  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14808  srcSuballocIt = nextSuballocIt;
14809 
14810  pDstMetadata->m_Suballocations.push_back(suballoc);
14811 
14812  move.srcBlockIndex = srcOrigBlockIndex;
14813  move.dstBlockIndex = dstOrigBlockIndex;
14814  move.srcOffset = srcAllocOffset;
14815  move.dstOffset = dstAllocOffset;
14816  move.size = srcAllocSize;
14817 
14818  moves.push_back(move);
14819  }
14820  }
14821  }
14822  }
14823 
14824  m_BlockInfos.clear();
14825 
14826  PostprocessMetadata();
14827 
14828  return VK_SUCCESS;
14829 }
14830 
14831 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14832 {
14833  const size_t blockCount = m_pBlockVector->GetBlockCount();
14834  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14835  {
14836  VmaBlockMetadata_Generic* const pMetadata =
14837  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14838  pMetadata->m_FreeCount = 0;
14839  pMetadata->m_SumFreeSize = pMetadata->GetSize();
14840  pMetadata->m_FreeSuballocationsBySize.clear();
14841  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14842  it != pMetadata->m_Suballocations.end(); )
14843  {
14844  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14845  {
14846  VmaSuballocationList::iterator nextIt = it;
14847  ++nextIt;
14848  pMetadata->m_Suballocations.erase(it);
14849  it = nextIt;
14850  }
14851  else
14852  {
14853  ++it;
14854  }
14855  }
14856  }
14857 }
14858 
14859 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14860 {
14861  const size_t blockCount = m_pBlockVector->GetBlockCount();
14862  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14863  {
14864  VmaBlockMetadata_Generic* const pMetadata =
14865  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14866  const VkDeviceSize blockSize = pMetadata->GetSize();
14867 
14868  // No allocations in this block - entire area is free.
14869  if(pMetadata->m_Suballocations.empty())
14870  {
14871  pMetadata->m_FreeCount = 1;
14872  //pMetadata->m_SumFreeSize is already set to blockSize.
14873  VmaSuballocation suballoc = {
14874  0, // offset
14875  blockSize, // size
14876  VMA_NULL, // hAllocation
14877  VMA_SUBALLOCATION_TYPE_FREE };
14878  pMetadata->m_Suballocations.push_back(suballoc);
14879  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14880  }
14881  // There are some allocations in this block.
14882  else
14883  {
14884  VkDeviceSize offset = 0;
14885  VmaSuballocationList::iterator it;
14886  for(it = pMetadata->m_Suballocations.begin();
14887  it != pMetadata->m_Suballocations.end();
14888  ++it)
14889  {
14890  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14891  VMA_ASSERT(it->offset >= offset);
14892 
14893  // Need to insert preceding free space.
14894  if(it->offset > offset)
14895  {
14896  ++pMetadata->m_FreeCount;
14897  const VkDeviceSize freeSize = it->offset - offset;
14898  VmaSuballocation suballoc = {
14899  offset, // offset
14900  freeSize, // size
14901  VMA_NULL, // hAllocation
14902  VMA_SUBALLOCATION_TYPE_FREE };
14903  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14904  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14905  {
14906  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14907  }
14908  }
14909 
14910  pMetadata->m_SumFreeSize -= it->size;
14911  offset = it->offset + it->size;
14912  }
14913 
14914  // Need to insert trailing free space.
14915  if(offset < blockSize)
14916  {
14917  ++pMetadata->m_FreeCount;
14918  const VkDeviceSize freeSize = blockSize - offset;
14919  VmaSuballocation suballoc = {
14920  offset, // offset
14921  freeSize, // size
14922  VMA_NULL, // hAllocation
14923  VMA_SUBALLOCATION_TYPE_FREE };
14924  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14925  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14926  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14927  {
14928  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14929  }
14930  }
14931 
14932  VMA_SORT(
14933  pMetadata->m_FreeSuballocationsBySize.begin(),
14934  pMetadata->m_FreeSuballocationsBySize.end(),
14935  VmaSuballocationItemSizeLess());
14936  }
14937 
14938  VMA_HEAVY_ASSERT(pMetadata->Validate());
14939  }
14940 }
14941 
14942 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14943 {
14944  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14945  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14946  while(it != pMetadata->m_Suballocations.end())
14947  {
14948  if(it->offset < suballoc.offset)
14949  {
14950  ++it;
14951  }
14952  }
14953  pMetadata->m_Suballocations.insert(it, suballoc);
14954 }
14955 
14957 // VmaBlockVectorDefragmentationContext
14958 
14959 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14960  VmaAllocator hAllocator,
14961  VmaPool hCustomPool,
14962  VmaBlockVector* pBlockVector,
14963  uint32_t currFrameIndex) :
14964  res(VK_SUCCESS),
14965  mutexLocked(false),
14966  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14967  defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14968  defragmentationMovesProcessed(0),
14969  defragmentationMovesCommitted(0),
14970  hasDefragmentationPlan(0),
14971  m_hAllocator(hAllocator),
14972  m_hCustomPool(hCustomPool),
14973  m_pBlockVector(pBlockVector),
14974  m_CurrFrameIndex(currFrameIndex),
14975  m_pAlgorithm(VMA_NULL),
14976  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14977  m_AllAllocations(false)
14978 {
14979 }
14980 
14981 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14982 {
14983  vma_delete(m_hAllocator, m_pAlgorithm);
14984 }
14985 
14986 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14987 {
14988  AllocInfo info = { hAlloc, pChanged };
14989  m_Allocations.push_back(info);
14990 }
14991 
14992 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14993 {
14994  const bool allAllocations = m_AllAllocations ||
14995  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14996 
14997  /********************************
14998  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14999  ********************************/
15000 
15001  /*
15002  Fast algorithm is supported only when certain criteria are met:
15003  - VMA_DEBUG_MARGIN is 0.
15004  - All allocations in this block vector are moveable.
15005  - There is no possibility of image/buffer granularity conflict.
15006  - The defragmentation is not incremental
15007  */
15008  if(VMA_DEBUG_MARGIN == 0 &&
15009  allAllocations &&
15010  !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
15012  {
15013  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
15014  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
15015  }
15016  else
15017  {
15018  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
15019  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
15020  }
15021 
15022  if(allAllocations)
15023  {
15024  m_pAlgorithm->AddAll();
15025  }
15026  else
15027  {
15028  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
15029  {
15030  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
15031  }
15032  }
15033 }
15034 
15036 // VmaDefragmentationContext
15037 
15038 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
15039  VmaAllocator hAllocator,
15040  uint32_t currFrameIndex,
15041  uint32_t flags,
15042  VmaDefragmentationStats* pStats) :
15043  m_hAllocator(hAllocator),
15044  m_CurrFrameIndex(currFrameIndex),
15045  m_Flags(flags),
15046  m_pStats(pStats),
15047  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
15048 {
15049  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
15050 }
15051 
15052 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
15053 {
15054  for(size_t i = m_CustomPoolContexts.size(); i--; )
15055  {
15056  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
15057  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15058  vma_delete(m_hAllocator, pBlockVectorCtx);
15059  }
15060  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
15061  {
15062  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
15063  if(pBlockVectorCtx)
15064  {
15065  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15066  vma_delete(m_hAllocator, pBlockVectorCtx);
15067  }
15068  }
15069 }
15070 
15071 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
15072 {
15073  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15074  {
15075  VmaPool pool = pPools[poolIndex];
15076  VMA_ASSERT(pool);
15077  // Pools with algorithm other than default are not defragmented.
15078  if(pool->m_BlockVector.GetAlgorithm() == 0)
15079  {
15080  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15081 
15082  for(size_t i = m_CustomPoolContexts.size(); i--; )
15083  {
15084  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
15085  {
15086  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15087  break;
15088  }
15089  }
15090 
15091  if(!pBlockVectorDefragCtx)
15092  {
15093  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15094  m_hAllocator,
15095  pool,
15096  &pool->m_BlockVector,
15097  m_CurrFrameIndex);
15098  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15099  }
15100 
15101  pBlockVectorDefragCtx->AddAll();
15102  }
15103  }
15104 }
15105 
15106 void VmaDefragmentationContext_T::AddAllocations(
15107  uint32_t allocationCount,
15108  const VmaAllocation* pAllocations,
15109  VkBool32* pAllocationsChanged)
15110 {
15111  // Dispatch pAllocations among defragmentators. Create them when necessary.
15112  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15113  {
15114  const VmaAllocation hAlloc = pAllocations[allocIndex];
15115  VMA_ASSERT(hAlloc);
15116  // DedicatedAlloc cannot be defragmented.
15117  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
15118  // Lost allocation cannot be defragmented.
15119  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
15120  {
15121  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15122 
15123  const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
15124  // This allocation belongs to custom pool.
15125  if(hAllocPool != VK_NULL_HANDLE)
15126  {
15127  // Pools with algorithm other than default are not defragmented.
15128  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
15129  {
15130  for(size_t i = m_CustomPoolContexts.size(); i--; )
15131  {
15132  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
15133  {
15134  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15135  break;
15136  }
15137  }
15138  if(!pBlockVectorDefragCtx)
15139  {
15140  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15141  m_hAllocator,
15142  hAllocPool,
15143  &hAllocPool->m_BlockVector,
15144  m_CurrFrameIndex);
15145  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15146  }
15147  }
15148  }
15149  // This allocation belongs to default pool.
15150  else
15151  {
15152  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
15153  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
15154  if(!pBlockVectorDefragCtx)
15155  {
15156  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15157  m_hAllocator,
15158  VMA_NULL, // hCustomPool
15159  m_hAllocator->m_pBlockVectors[memTypeIndex],
15160  m_CurrFrameIndex);
15161  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
15162  }
15163  }
15164 
15165  if(pBlockVectorDefragCtx)
15166  {
15167  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
15168  &pAllocationsChanged[allocIndex] : VMA_NULL;
15169  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
15170  }
15171  }
15172  }
15173 }
15174 
15175 VkResult VmaDefragmentationContext_T::Defragment(
15176  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
15177  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
15178  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
15179 {
15180  if(pStats)
15181  {
15182  memset(pStats, 0, sizeof(VmaDefragmentationStats));
15183  }
15184 
15186  {
15187  // For incremental defragmetnations, we just earmark how much we can move
15188  // The real meat is in the defragmentation steps
15189  m_MaxCpuBytesToMove = maxCpuBytesToMove;
15190  m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
15191 
15192  m_MaxGpuBytesToMove = maxGpuBytesToMove;
15193  m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
15194 
15195  if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
15196  m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
15197  return VK_SUCCESS;
15198 
15199  return VK_NOT_READY;
15200  }
15201 
15202  if(commandBuffer == VK_NULL_HANDLE)
15203  {
15204  maxGpuBytesToMove = 0;
15205  maxGpuAllocationsToMove = 0;
15206  }
15207 
15208  VkResult res = VK_SUCCESS;
15209 
15210  // Process default pools.
15211  for(uint32_t memTypeIndex = 0;
15212  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
15213  ++memTypeIndex)
15214  {
15215  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15216  if(pBlockVectorCtx)
15217  {
15218  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15219  pBlockVectorCtx->GetBlockVector()->Defragment(
15220  pBlockVectorCtx,
15221  pStats, flags,
15222  maxCpuBytesToMove, maxCpuAllocationsToMove,
15223  maxGpuBytesToMove, maxGpuAllocationsToMove,
15224  commandBuffer);
15225  if(pBlockVectorCtx->res != VK_SUCCESS)
15226  {
15227  res = pBlockVectorCtx->res;
15228  }
15229  }
15230  }
15231 
15232  // Process custom pools.
15233  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15234  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
15235  ++customCtxIndex)
15236  {
15237  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15238  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15239  pBlockVectorCtx->GetBlockVector()->Defragment(
15240  pBlockVectorCtx,
15241  pStats, flags,
15242  maxCpuBytesToMove, maxCpuAllocationsToMove,
15243  maxGpuBytesToMove, maxGpuAllocationsToMove,
15244  commandBuffer);
15245  if(pBlockVectorCtx->res != VK_SUCCESS)
15246  {
15247  res = pBlockVectorCtx->res;
15248  }
15249  }
15250 
15251  return res;
15252 }
15253 
15254 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
15255 {
15256  VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
15257  uint32_t movesLeft = pInfo->moveCount;
15258 
15259  // Process default pools.
15260  for(uint32_t memTypeIndex = 0;
15261  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15262  ++memTypeIndex)
15263  {
15264  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15265  if(pBlockVectorCtx)
15266  {
15267  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15268 
15269  if(!pBlockVectorCtx->hasDefragmentationPlan)
15270  {
15271  pBlockVectorCtx->GetBlockVector()->Defragment(
15272  pBlockVectorCtx,
15273  m_pStats, m_Flags,
15274  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15275  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15276  VK_NULL_HANDLE);
15277 
15278  if(pBlockVectorCtx->res < VK_SUCCESS)
15279  continue;
15280 
15281  pBlockVectorCtx->hasDefragmentationPlan = true;
15282  }
15283 
15284  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15285  pBlockVectorCtx,
15286  pCurrentMove, movesLeft);
15287 
15288  movesLeft -= processed;
15289  pCurrentMove += processed;
15290  }
15291  }
15292 
15293  // Process custom pools.
15294  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15295  customCtxIndex < customCtxCount;
15296  ++customCtxIndex)
15297  {
15298  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15299  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15300 
15301  if(!pBlockVectorCtx->hasDefragmentationPlan)
15302  {
15303  pBlockVectorCtx->GetBlockVector()->Defragment(
15304  pBlockVectorCtx,
15305  m_pStats, m_Flags,
15306  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15307  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15308  VK_NULL_HANDLE);
15309 
15310  if(pBlockVectorCtx->res < VK_SUCCESS)
15311  continue;
15312 
15313  pBlockVectorCtx->hasDefragmentationPlan = true;
15314  }
15315 
15316  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15317  pBlockVectorCtx,
15318  pCurrentMove, movesLeft);
15319 
15320  movesLeft -= processed;
15321  pCurrentMove += processed;
15322  }
15323 
15324  pInfo->moveCount = pInfo->moveCount - movesLeft;
15325 
15326  return VK_SUCCESS;
15327 }
15328 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15329 {
15330  VkResult res = VK_SUCCESS;
15331 
15332  // Process default pools.
15333  for(uint32_t memTypeIndex = 0;
15334  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15335  ++memTypeIndex)
15336  {
15337  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15338  if(pBlockVectorCtx)
15339  {
15340  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15341 
15342  if(!pBlockVectorCtx->hasDefragmentationPlan)
15343  {
15344  res = VK_NOT_READY;
15345  continue;
15346  }
15347 
15348  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15349  pBlockVectorCtx, m_pStats);
15350 
15351  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15352  res = VK_NOT_READY;
15353  }
15354  }
15355 
15356  // Process custom pools.
15357  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15358  customCtxIndex < customCtxCount;
15359  ++customCtxIndex)
15360  {
15361  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15362  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15363 
15364  if(!pBlockVectorCtx->hasDefragmentationPlan)
15365  {
15366  res = VK_NOT_READY;
15367  continue;
15368  }
15369 
15370  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15371  pBlockVectorCtx, m_pStats);
15372 
15373  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15374  res = VK_NOT_READY;
15375  }
15376 
15377  return res;
15378 }
15379 
15381 // VmaRecorder
15382 
15383 #if VMA_RECORDING_ENABLED
15384 
15385 VmaRecorder::VmaRecorder() :
15386  m_UseMutex(true),
15387  m_Flags(0),
15388  m_File(VMA_NULL),
15389  m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15390 {
15391 }
15392 
15393 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15394 {
15395  m_UseMutex = useMutex;
15396  m_Flags = settings.flags;
15397 
15398 #if defined(_WIN32)
15399  // Open file for writing.
15400  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15401 
15402  if(err != 0)
15403  {
15404  return VK_ERROR_INITIALIZATION_FAILED;
15405  }
15406 #else
15407  // Open file for writing.
15408  m_File = fopen(settings.pFilePath, "wb");
15409 
15410  if(m_File == 0)
15411  {
15412  return VK_ERROR_INITIALIZATION_FAILED;
15413  }
15414 #endif
15415 
15416  // Write header.
15417  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15418  fprintf(m_File, "%s\n", "1,8");
15419 
15420  return VK_SUCCESS;
15421 }
15422 
15423 VmaRecorder::~VmaRecorder()
15424 {
15425  if(m_File != VMA_NULL)
15426  {
15427  fclose(m_File);
15428  }
15429 }
15430 
15431 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15432 {
15433  CallParams callParams;
15434  GetBasicParams(callParams);
15435 
15436  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15437  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15438  Flush();
15439 }
15440 
15441 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15442 {
15443  CallParams callParams;
15444  GetBasicParams(callParams);
15445 
15446  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15447  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15448  Flush();
15449 }
15450 
15451 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15452 {
15453  CallParams callParams;
15454  GetBasicParams(callParams);
15455 
15456  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15457  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15458  createInfo.memoryTypeIndex,
15459  createInfo.flags,
15460  createInfo.blockSize,
15461  (uint64_t)createInfo.minBlockCount,
15462  (uint64_t)createInfo.maxBlockCount,
15463  createInfo.frameInUseCount,
15464  pool);
15465  Flush();
15466 }
15467 
15468 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15469 {
15470  CallParams callParams;
15471  GetBasicParams(callParams);
15472 
15473  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15474  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15475  pool);
15476  Flush();
15477 }
15478 
15479 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15480  const VkMemoryRequirements& vkMemReq,
15481  const VmaAllocationCreateInfo& createInfo,
15482  VmaAllocation allocation)
15483 {
15484  CallParams callParams;
15485  GetBasicParams(callParams);
15486 
15487  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15488  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15489  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15490  vkMemReq.size,
15491  vkMemReq.alignment,
15492  vkMemReq.memoryTypeBits,
15493  createInfo.flags,
15494  createInfo.usage,
15495  createInfo.requiredFlags,
15496  createInfo.preferredFlags,
15497  createInfo.memoryTypeBits,
15498  createInfo.pool,
15499  allocation,
15500  userDataStr.GetString());
15501  Flush();
15502 }
15503 
15504 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15505  const VkMemoryRequirements& vkMemReq,
15506  const VmaAllocationCreateInfo& createInfo,
15507  uint64_t allocationCount,
15508  const VmaAllocation* pAllocations)
15509 {
15510  CallParams callParams;
15511  GetBasicParams(callParams);
15512 
15513  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15514  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15515  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15516  vkMemReq.size,
15517  vkMemReq.alignment,
15518  vkMemReq.memoryTypeBits,
15519  createInfo.flags,
15520  createInfo.usage,
15521  createInfo.requiredFlags,
15522  createInfo.preferredFlags,
15523  createInfo.memoryTypeBits,
15524  createInfo.pool);
15525  PrintPointerList(allocationCount, pAllocations);
15526  fprintf(m_File, ",%s\n", userDataStr.GetString());
15527  Flush();
15528 }
15529 
15530 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15531  const VkMemoryRequirements& vkMemReq,
15532  bool requiresDedicatedAllocation,
15533  bool prefersDedicatedAllocation,
15534  const VmaAllocationCreateInfo& createInfo,
15535  VmaAllocation allocation)
15536 {
15537  CallParams callParams;
15538  GetBasicParams(callParams);
15539 
15540  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15541  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15542  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,
15543  vkMemReq.size,
15544  vkMemReq.alignment,
15545  vkMemReq.memoryTypeBits,
15546  requiresDedicatedAllocation ? 1 : 0,
15547  prefersDedicatedAllocation ? 1 : 0,
15548  createInfo.flags,
15549  createInfo.usage,
15550  createInfo.requiredFlags,
15551  createInfo.preferredFlags,
15552  createInfo.memoryTypeBits,
15553  createInfo.pool,
15554  allocation,
15555  userDataStr.GetString());
15556  Flush();
15557 }
15558 
15559 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15560  const VkMemoryRequirements& vkMemReq,
15561  bool requiresDedicatedAllocation,
15562  bool prefersDedicatedAllocation,
15563  const VmaAllocationCreateInfo& createInfo,
15564  VmaAllocation allocation)
15565 {
15566  CallParams callParams;
15567  GetBasicParams(callParams);
15568 
15569  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15570  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15571  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,
15572  vkMemReq.size,
15573  vkMemReq.alignment,
15574  vkMemReq.memoryTypeBits,
15575  requiresDedicatedAllocation ? 1 : 0,
15576  prefersDedicatedAllocation ? 1 : 0,
15577  createInfo.flags,
15578  createInfo.usage,
15579  createInfo.requiredFlags,
15580  createInfo.preferredFlags,
15581  createInfo.memoryTypeBits,
15582  createInfo.pool,
15583  allocation,
15584  userDataStr.GetString());
15585  Flush();
15586 }
15587 
15588 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15589  VmaAllocation allocation)
15590 {
15591  CallParams callParams;
15592  GetBasicParams(callParams);
15593 
15594  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15595  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15596  allocation);
15597  Flush();
15598 }
15599 
15600 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15601  uint64_t allocationCount,
15602  const VmaAllocation* pAllocations)
15603 {
15604  CallParams callParams;
15605  GetBasicParams(callParams);
15606 
15607  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15608  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15609  PrintPointerList(allocationCount, pAllocations);
15610  fprintf(m_File, "\n");
15611  Flush();
15612 }
15613 
15614 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15615  VmaAllocation allocation,
15616  const void* pUserData)
15617 {
15618  CallParams callParams;
15619  GetBasicParams(callParams);
15620 
15621  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15622  UserDataString userDataStr(
15623  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15624  pUserData);
15625  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15626  allocation,
15627  userDataStr.GetString());
15628  Flush();
15629 }
15630 
15631 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15632  VmaAllocation allocation)
15633 {
15634  CallParams callParams;
15635  GetBasicParams(callParams);
15636 
15637  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15638  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15639  allocation);
15640  Flush();
15641 }
15642 
15643 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15644  VmaAllocation allocation)
15645 {
15646  CallParams callParams;
15647  GetBasicParams(callParams);
15648 
15649  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15650  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15651  allocation);
15652  Flush();
15653 }
15654 
15655 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15656  VmaAllocation allocation)
15657 {
15658  CallParams callParams;
15659  GetBasicParams(callParams);
15660 
15661  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15662  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15663  allocation);
15664  Flush();
15665 }
15666 
15667 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15668  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15669 {
15670  CallParams callParams;
15671  GetBasicParams(callParams);
15672 
15673  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15674  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15675  allocation,
15676  offset,
15677  size);
15678  Flush();
15679 }
15680 
15681 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15682  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15683 {
15684  CallParams callParams;
15685  GetBasicParams(callParams);
15686 
15687  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15688  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15689  allocation,
15690  offset,
15691  size);
15692  Flush();
15693 }
15694 
15695 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15696  const VkBufferCreateInfo& bufCreateInfo,
15697  const VmaAllocationCreateInfo& allocCreateInfo,
15698  VmaAllocation allocation)
15699 {
15700  CallParams callParams;
15701  GetBasicParams(callParams);
15702 
15703  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15704  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15705  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,
15706  bufCreateInfo.flags,
15707  bufCreateInfo.size,
15708  bufCreateInfo.usage,
15709  bufCreateInfo.sharingMode,
15710  allocCreateInfo.flags,
15711  allocCreateInfo.usage,
15712  allocCreateInfo.requiredFlags,
15713  allocCreateInfo.preferredFlags,
15714  allocCreateInfo.memoryTypeBits,
15715  allocCreateInfo.pool,
15716  allocation,
15717  userDataStr.GetString());
15718  Flush();
15719 }
15720 
15721 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15722  const VkImageCreateInfo& imageCreateInfo,
15723  const VmaAllocationCreateInfo& allocCreateInfo,
15724  VmaAllocation allocation)
15725 {
15726  CallParams callParams;
15727  GetBasicParams(callParams);
15728 
15729  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15730  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15731  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,
15732  imageCreateInfo.flags,
15733  imageCreateInfo.imageType,
15734  imageCreateInfo.format,
15735  imageCreateInfo.extent.width,
15736  imageCreateInfo.extent.height,
15737  imageCreateInfo.extent.depth,
15738  imageCreateInfo.mipLevels,
15739  imageCreateInfo.arrayLayers,
15740  imageCreateInfo.samples,
15741  imageCreateInfo.tiling,
15742  imageCreateInfo.usage,
15743  imageCreateInfo.sharingMode,
15744  imageCreateInfo.initialLayout,
15745  allocCreateInfo.flags,
15746  allocCreateInfo.usage,
15747  allocCreateInfo.requiredFlags,
15748  allocCreateInfo.preferredFlags,
15749  allocCreateInfo.memoryTypeBits,
15750  allocCreateInfo.pool,
15751  allocation,
15752  userDataStr.GetString());
15753  Flush();
15754 }
15755 
15756 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15757  VmaAllocation allocation)
15758 {
15759  CallParams callParams;
15760  GetBasicParams(callParams);
15761 
15762  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15763  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15764  allocation);
15765  Flush();
15766 }
15767 
15768 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15769  VmaAllocation allocation)
15770 {
15771  CallParams callParams;
15772  GetBasicParams(callParams);
15773 
15774  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15775  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15776  allocation);
15777  Flush();
15778 }
15779 
15780 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15781  VmaAllocation allocation)
15782 {
15783  CallParams callParams;
15784  GetBasicParams(callParams);
15785 
15786  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15787  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15788  allocation);
15789  Flush();
15790 }
15791 
15792 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15793  VmaAllocation allocation)
15794 {
15795  CallParams callParams;
15796  GetBasicParams(callParams);
15797 
15798  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15799  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15800  allocation);
15801  Flush();
15802 }
15803 
15804 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15805  VmaPool pool)
15806 {
15807  CallParams callParams;
15808  GetBasicParams(callParams);
15809 
15810  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15811  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15812  pool);
15813  Flush();
15814 }
15815 
15816 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15817  const VmaDefragmentationInfo2& info,
15819 {
15820  CallParams callParams;
15821  GetBasicParams(callParams);
15822 
15823  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15824  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15825  info.flags);
15826  PrintPointerList(info.allocationCount, info.pAllocations);
15827  fprintf(m_File, ",");
15828  PrintPointerList(info.poolCount, info.pPools);
15829  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15830  info.maxCpuBytesToMove,
15832  info.maxGpuBytesToMove,
15834  info.commandBuffer,
15835  ctx);
15836  Flush();
15837 }
15838 
15839 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15841 {
15842  CallParams callParams;
15843  GetBasicParams(callParams);
15844 
15845  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15846  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15847  ctx);
15848  Flush();
15849 }
15850 
15851 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15852  VmaPool pool,
15853  const char* name)
15854 {
15855  CallParams callParams;
15856  GetBasicParams(callParams);
15857 
15858  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15859  fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15860  pool, name != VMA_NULL ? name : "");
15861  Flush();
15862 }
15863 
15864 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15865 {
15866  if(pUserData != VMA_NULL)
15867  {
15868  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15869  {
15870  m_Str = (const char*)pUserData;
15871  }
15872  else
15873  {
15874  // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15875  snprintf(m_PtrStr, 17, "%p", pUserData);
15876  m_Str = m_PtrStr;
15877  }
15878  }
15879  else
15880  {
15881  m_Str = "";
15882  }
15883 }
15884 
15885 void VmaRecorder::WriteConfiguration(
15886  const VkPhysicalDeviceProperties& devProps,
15887  const VkPhysicalDeviceMemoryProperties& memProps,
15888  uint32_t vulkanApiVersion,
15889  bool dedicatedAllocationExtensionEnabled,
15890  bool bindMemory2ExtensionEnabled,
15891  bool memoryBudgetExtensionEnabled,
15892  bool deviceCoherentMemoryExtensionEnabled)
15893 {
15894  fprintf(m_File, "Config,Begin\n");
15895 
15896  fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15897 
15898  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15899  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15900  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15901  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15902  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15903  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15904 
15905  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15906  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15907  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15908 
15909  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15910  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15911  {
15912  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15913  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15914  }
15915  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15916  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15917  {
15918  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15919  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15920  }
15921 
15922  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15923  fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15924  fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15925  fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15926 
15927  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15928  fprintf(m_File, "Macro,VMA_MIN_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_MIN_ALIGNMENT);
15929  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15930  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15931  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15932  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15933  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15934  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15935  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15936 
15937  fprintf(m_File, "Config,End\n");
15938 }
15939 
15940 void VmaRecorder::GetBasicParams(CallParams& outParams)
15941 {
15942  #if defined(_WIN32)
15943  outParams.threadId = GetCurrentThreadId();
15944  #else
15945  // Use C++11 features to get thread id and convert it to uint32_t.
15946  // There is room for optimization since sstream is quite slow.
15947  // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15948  std::thread::id thread_id = std::this_thread::get_id();
15949  std::stringstream thread_id_to_string_converter;
15950  thread_id_to_string_converter << thread_id;
15951  std::string thread_id_as_string = thread_id_to_string_converter.str();
15952  outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15953  #endif
15954 
15955  auto current_time = std::chrono::high_resolution_clock::now();
15956 
15957  outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15958 }
15959 
15960 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15961 {
15962  if(count)
15963  {
15964  fprintf(m_File, "%p", pItems[0]);
15965  for(uint64_t i = 1; i < count; ++i)
15966  {
15967  fprintf(m_File, " %p", pItems[i]);
15968  }
15969  }
15970 }
15971 
15972 void VmaRecorder::Flush()
15973 {
15974  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15975  {
15976  fflush(m_File);
15977  }
15978 }
15979 
15980 #endif // #if VMA_RECORDING_ENABLED
15981 
15983 // VmaAllocationObjectAllocator
15984 
15985 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15986  m_Allocator(pAllocationCallbacks, 1024)
15987 {
15988 }
15989 
15990 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15991 {
15992  VmaMutexLock mutexLock(m_Mutex);
15993  return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15994 }
15995 
15996 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15997 {
15998  VmaMutexLock mutexLock(m_Mutex);
15999  m_Allocator.Free(hAlloc);
16000 }
16001 
16003 // VmaAllocator_T
16004 
16005 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
16006  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
16007  m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
16008  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
16009  m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
16010  m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
16011  m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
16012  m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
16013  m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
16014  m_hDevice(pCreateInfo->device),
16015  m_hInstance(pCreateInfo->instance),
16016  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
16017  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
16018  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
16019  m_AllocationObjectAllocator(&m_AllocationCallbacks),
16020  m_HeapSizeLimitMask(0),
16021  m_DeviceMemoryCount(0),
16022  m_PreferredLargeHeapBlockSize(0),
16023  m_PhysicalDevice(pCreateInfo->physicalDevice),
16024  m_CurrentFrameIndex(0),
16025  m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
16026  m_NextPoolId(0),
16027  m_GlobalMemoryTypeBits(UINT32_MAX)
16029  ,m_pRecorder(VMA_NULL)
16030 #endif
16031 {
16032  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16033  {
16034  m_UseKhrDedicatedAllocation = false;
16035  m_UseKhrBindMemory2 = false;
16036  }
16037 
16038  if(VMA_DEBUG_DETECT_CORRUPTION)
16039  {
16040  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
16041  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
16042  }
16043 
16044  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
16045 
16046  if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16047  {
16048 #if !(VMA_DEDICATED_ALLOCATION)
16050  {
16051  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
16052  }
16053 #endif
16054 #if !(VMA_BIND_MEMORY2)
16055  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
16056  {
16057  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
16058  }
16059 #endif
16060  }
16061 #if !(VMA_MEMORY_BUDGET)
16062  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
16063  {
16064  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
16065  }
16066 #endif
16067 #if !(VMA_BUFFER_DEVICE_ADDRESS)
16068  if(m_UseKhrBufferDeviceAddress)
16069  {
16070  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.");
16071  }
16072 #endif
16073 #if VMA_VULKAN_VERSION < 1002000
16074  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
16075  {
16076  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
16077  }
16078 #endif
16079 #if VMA_VULKAN_VERSION < 1001000
16080  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16081  {
16082  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
16083  }
16084 #endif
16085 #if !(VMA_MEMORY_PRIORITY)
16086  if(m_UseExtMemoryPriority)
16087  {
16088  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.");
16089  }
16090 #endif
16091 
16092  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
16093  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
16094  memset(&m_MemProps, 0, sizeof(m_MemProps));
16095 
16096  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
16097  memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
16098 
16099  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
16100  {
16101  m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
16102  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
16103  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
16104  }
16105 
16106  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
16107 
16108  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
16109  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
16110 
16111  VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
16112  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
16113  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
16114  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
16115 
16116  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
16117  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
16118 
16119  m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
16120 
16121  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
16122  {
16123  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
16124  {
16125  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
16126  if(limit != VK_WHOLE_SIZE)
16127  {
16128  m_HeapSizeLimitMask |= 1u << heapIndex;
16129  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
16130  {
16131  m_MemProps.memoryHeaps[heapIndex].size = limit;
16132  }
16133  }
16134  }
16135  }
16136 
16137  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16138  {
16139  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
16140 
16141  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
16142  this,
16143  VK_NULL_HANDLE, // hParentPool
16144  memTypeIndex,
16145  preferredBlockSize,
16146  0,
16147  SIZE_MAX,
16148  GetBufferImageGranularity(),
16149  pCreateInfo->frameInUseCount,
16150  false, // explicitBlockSize
16151  false, // linearAlgorithm
16152  0.5f, // priority (0.5 is the default per Vulkan spec)
16153  GetMemoryTypeMinAlignment(memTypeIndex)); // minAllocationAlignment
16154  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
16155  // becase minBlockCount is 0.
16156  }
16157 }
16158 
16159 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
16160 {
16161  VkResult res = VK_SUCCESS;
16162 
16163  if(pCreateInfo->pRecordSettings != VMA_NULL &&
16164  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
16165  {
16166 #if VMA_RECORDING_ENABLED
16167  m_pRecorder = vma_new(this, VmaRecorder)();
16168  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
16169  if(res != VK_SUCCESS)
16170  {
16171  return res;
16172  }
16173  m_pRecorder->WriteConfiguration(
16174  m_PhysicalDeviceProperties,
16175  m_MemProps,
16176  m_VulkanApiVersion,
16177  m_UseKhrDedicatedAllocation,
16178  m_UseKhrBindMemory2,
16179  m_UseExtMemoryBudget,
16180  m_UseAmdDeviceCoherentMemory);
16181  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
16182 #else
16183  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
16184  return VK_ERROR_FEATURE_NOT_PRESENT;
16185 #endif
16186  }
16187 
16188 #if VMA_MEMORY_BUDGET
16189  if(m_UseExtMemoryBudget)
16190  {
16191  UpdateVulkanBudget();
16192  }
16193 #endif // #if VMA_MEMORY_BUDGET
16194 
16195  return res;
16196 }
16197 
16198 VmaAllocator_T::~VmaAllocator_T()
16199 {
16200 #if VMA_RECORDING_ENABLED
16201  if(m_pRecorder != VMA_NULL)
16202  {
16203  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
16204  vma_delete(this, m_pRecorder);
16205  }
16206 #endif
16207 
16208  VMA_ASSERT(m_Pools.IsEmpty());
16209 
16210  for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
16211  {
16212  if(!m_DedicatedAllocations[memTypeIndex].IsEmpty())
16213  {
16214  VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
16215  }
16216 
16217  vma_delete(this, m_pBlockVectors[memTypeIndex]);
16218  }
16219 }
16220 
16221 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
16222 {
16223 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16224  ImportVulkanFunctions_Static();
16225 #endif
16226 
16227  if(pVulkanFunctions != VMA_NULL)
16228  {
16229  ImportVulkanFunctions_Custom(pVulkanFunctions);
16230  }
16231 
16232 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16233  ImportVulkanFunctions_Dynamic();
16234 #endif
16235 
16236  ValidateVulkanFunctions();
16237 }
16238 
16239 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16240 
16241 void VmaAllocator_T::ImportVulkanFunctions_Static()
16242 {
16243  // Vulkan 1.0
16244  m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
16245  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
16246  m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
16247  m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
16248  m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
16249  m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
16250  m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
16251  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
16252  m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
16253  m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
16254  m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
16255  m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
16256  m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
16257  m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
16258  m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
16259  m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
16260  m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
16261 
16262  // Vulkan 1.1
16263 #if VMA_VULKAN_VERSION >= 1001000
16264  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16265  {
16266  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
16267  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
16268  m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
16269  m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
16270  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
16271  }
16272 #endif
16273 }
16274 
16275 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16276 
16277 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
16278 {
16279  VMA_ASSERT(pVulkanFunctions != VMA_NULL);
16280 
16281 #define VMA_COPY_IF_NOT_NULL(funcName) \
16282  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
16283 
16284  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
16285  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
16286  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
16287  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
16288  VMA_COPY_IF_NOT_NULL(vkMapMemory);
16289  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
16290  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
16291  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
16292  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
16293  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
16294  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
16295  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
16296  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16297  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16298  VMA_COPY_IF_NOT_NULL(vkCreateImage);
16299  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16300  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16301 
16302 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16303  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16304  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16305 #endif
16306 
16307 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16308  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16309  VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16310 #endif
16311 
16312 #if VMA_MEMORY_BUDGET
16313  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16314 #endif
16315 
16316 #undef VMA_COPY_IF_NOT_NULL
16317 }
16318 
16319 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16320 
16321 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16322 {
16323 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16324  if(m_VulkanFunctions.memberName == VMA_NULL) \
16325  m_VulkanFunctions.memberName = \
16326  (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16327 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16328  if(m_VulkanFunctions.memberName == VMA_NULL) \
16329  m_VulkanFunctions.memberName = \
16330  (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16331 
16332  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16333  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16334  VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16335  VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16336  VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16337  VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16338  VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16339  VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16340  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16341  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16342  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16343  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16344  VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16345  VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16346  VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16347  VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16348  VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16349 
16350 #if VMA_VULKAN_VERSION >= 1001000
16351  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16352  {
16353  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
16354  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
16355  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
16356  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
16357  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
16358  }
16359 #endif
16360 
16361 #if VMA_DEDICATED_ALLOCATION
16362  if(m_UseKhrDedicatedAllocation)
16363  {
16364  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16365  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16366  }
16367 #endif
16368 
16369 #if VMA_BIND_MEMORY2
16370  if(m_UseKhrBindMemory2)
16371  {
16372  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16373  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16374  }
16375 #endif // #if VMA_BIND_MEMORY2
16376 
16377 #if VMA_MEMORY_BUDGET
16378  if(m_UseExtMemoryBudget)
16379  {
16380  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16381  }
16382 #endif // #if VMA_MEMORY_BUDGET
16383 
16384 #undef VMA_FETCH_DEVICE_FUNC
16385 #undef VMA_FETCH_INSTANCE_FUNC
16386 }
16387 
16388 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16389 
16390 void VmaAllocator_T::ValidateVulkanFunctions()
16391 {
16392  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16393  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16394  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16395  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16396  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16397  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16398  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16399  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16400  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16401  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16402  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16403  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16404  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16405  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16406  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16407  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16408  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16409 
16410 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16411  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16412  {
16413  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16414  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16415  }
16416 #endif
16417 
16418 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16419  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16420  {
16421  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16422  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16423  }
16424 #endif
16425 
16426 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16427  if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16428  {
16429  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16430  }
16431 #endif
16432 }
16433 
16434 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16435 {
16436  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16437  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16438  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16439  return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16440 }
16441 
16442 VkResult VmaAllocator_T::AllocateMemoryOfType(
16443  VkDeviceSize size,
16444  VkDeviceSize alignment,
16445  bool dedicatedAllocation,
16446  VkBuffer dedicatedBuffer,
16447  VkBufferUsageFlags dedicatedBufferUsage,
16448  VkImage dedicatedImage,
16449  const VmaAllocationCreateInfo& createInfo,
16450  uint32_t memTypeIndex,
16451  VmaSuballocationType suballocType,
16452  size_t allocationCount,
16453  VmaAllocation* pAllocations)
16454 {
16455  VMA_ASSERT(pAllocations != VMA_NULL);
16456  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16457 
16458  VmaAllocationCreateInfo finalCreateInfo = createInfo;
16459 
16460  // If memory type is not HOST_VISIBLE, disable MAPPED.
16461  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16462  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16463  {
16464  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16465  }
16466  // If memory is lazily allocated, it should be always dedicated.
16467  if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16468  {
16470  }
16471 
16472  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16473  VMA_ASSERT(blockVector);
16474 
16475  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16476  bool preferDedicatedMemory =
16477  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16478  dedicatedAllocation ||
16479  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16480  size > preferredBlockSize / 2;
16481 
16482  if(preferDedicatedMemory &&
16483  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16484  finalCreateInfo.pool == VK_NULL_HANDLE)
16485  {
16487  }
16488 
16489  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16490  {
16491  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16492  {
16493  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16494  }
16495  else
16496  {
16497  return AllocateDedicatedMemory(
16498  size,
16499  suballocType,
16500  memTypeIndex,
16501  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16502  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16503  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16504  finalCreateInfo.pUserData,
16505  finalCreateInfo.priority,
16506  dedicatedBuffer,
16507  dedicatedBufferUsage,
16508  dedicatedImage,
16509  allocationCount,
16510  pAllocations);
16511  }
16512  }
16513  else
16514  {
16515  VkResult res = blockVector->Allocate(
16516  m_CurrentFrameIndex.load(),
16517  size,
16518  alignment,
16519  finalCreateInfo,
16520  suballocType,
16521  allocationCount,
16522  pAllocations);
16523  if(res == VK_SUCCESS)
16524  {
16525  return res;
16526  }
16527 
16528  // 5. Try dedicated memory.
16529  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16530  {
16531  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16532  }
16533 
16534  // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
16535  // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
16536  // 3/4 of the maximum allocation count.
16537  if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
16538  {
16539  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16540  }
16541 
16542  res = AllocateDedicatedMemory(
16543  size,
16544  suballocType,
16545  memTypeIndex,
16546  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16547  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16548  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16549  finalCreateInfo.pUserData,
16550  finalCreateInfo.priority,
16551  dedicatedBuffer,
16552  dedicatedBufferUsage,
16553  dedicatedImage,
16554  allocationCount,
16555  pAllocations);
16556  if(res == VK_SUCCESS)
16557  {
16558  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16559  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16560  return VK_SUCCESS;
16561  }
16562  else
16563  {
16564  // Everything failed: Return error code.
16565  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16566  return res;
16567  }
16568  }
16569 }
16570 
16571 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16572  VkDeviceSize size,
16573  VmaSuballocationType suballocType,
16574  uint32_t memTypeIndex,
16575  bool withinBudget,
16576  bool map,
16577  bool isUserDataString,
16578  void* pUserData,
16579  float priority,
16580  VkBuffer dedicatedBuffer,
16581  VkBufferUsageFlags dedicatedBufferUsage,
16582  VkImage dedicatedImage,
16583  size_t allocationCount,
16584  VmaAllocation* pAllocations)
16585 {
16586  VMA_ASSERT(allocationCount > 0 && pAllocations);
16587 
16588  if(withinBudget)
16589  {
16590  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16591  VmaBudget heapBudget = {};
16592  GetBudget(&heapBudget, heapIndex, 1);
16593  if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16594  {
16595  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16596  }
16597  }
16598 
16599  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16600  allocInfo.memoryTypeIndex = memTypeIndex;
16601  allocInfo.allocationSize = size;
16602 
16603 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16604  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16605  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16606  {
16607  if(dedicatedBuffer != VK_NULL_HANDLE)
16608  {
16609  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16610  dedicatedAllocInfo.buffer = dedicatedBuffer;
16611  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16612  }
16613  else if(dedicatedImage != VK_NULL_HANDLE)
16614  {
16615  dedicatedAllocInfo.image = dedicatedImage;
16616  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16617  }
16618  }
16619 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16620 
16621 #if VMA_BUFFER_DEVICE_ADDRESS
16622  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16623  if(m_UseKhrBufferDeviceAddress)
16624  {
16625  bool canContainBufferWithDeviceAddress = true;
16626  if(dedicatedBuffer != VK_NULL_HANDLE)
16627  {
16628  canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16629  (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16630  }
16631  else if(dedicatedImage != VK_NULL_HANDLE)
16632  {
16633  canContainBufferWithDeviceAddress = false;
16634  }
16635  if(canContainBufferWithDeviceAddress)
16636  {
16637  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16638  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16639  }
16640  }
16641 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16642 
16643 #if VMA_MEMORY_PRIORITY
16644  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
16645  if(m_UseExtMemoryPriority)
16646  {
16647  priorityInfo.priority = priority;
16648  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
16649  }
16650 #endif // #if VMA_MEMORY_PRIORITY
16651 
16652  size_t allocIndex;
16653  VkResult res = VK_SUCCESS;
16654  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16655  {
16656  res = AllocateDedicatedMemoryPage(
16657  size,
16658  suballocType,
16659  memTypeIndex,
16660  allocInfo,
16661  map,
16662  isUserDataString,
16663  pUserData,
16664  pAllocations + allocIndex);
16665  if(res != VK_SUCCESS)
16666  {
16667  break;
16668  }
16669  }
16670 
16671  if(res == VK_SUCCESS)
16672  {
16673  // Register them in m_DedicatedAllocations.
16674  {
16675  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16676  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
16677  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16678  {
16679  dedicatedAllocations.PushBack(pAllocations[allocIndex]);
16680  }
16681  }
16682 
16683  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16684  }
16685  else
16686  {
16687  // Free all already created allocations.
16688  while(allocIndex--)
16689  {
16690  VmaAllocation currAlloc = pAllocations[allocIndex];
16691  VkDeviceMemory hMemory = currAlloc->GetMemory();
16692 
16693  /*
16694  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16695  before vkFreeMemory.
16696 
16697  if(currAlloc->GetMappedData() != VMA_NULL)
16698  {
16699  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16700  }
16701  */
16702 
16703  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16704  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16705  currAlloc->SetUserData(this, VMA_NULL);
16706  m_AllocationObjectAllocator.Free(currAlloc);
16707  }
16708 
16709  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16710  }
16711 
16712  return res;
16713 }
16714 
16715 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16716  VkDeviceSize size,
16717  VmaSuballocationType suballocType,
16718  uint32_t memTypeIndex,
16719  const VkMemoryAllocateInfo& allocInfo,
16720  bool map,
16721  bool isUserDataString,
16722  void* pUserData,
16723  VmaAllocation* pAllocation)
16724 {
16725  VkDeviceMemory hMemory = VK_NULL_HANDLE;
16726  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16727  if(res < 0)
16728  {
16729  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16730  return res;
16731  }
16732 
16733  void* pMappedData = VMA_NULL;
16734  if(map)
16735  {
16736  res = (*m_VulkanFunctions.vkMapMemory)(
16737  m_hDevice,
16738  hMemory,
16739  0,
16740  VK_WHOLE_SIZE,
16741  0,
16742  &pMappedData);
16743  if(res < 0)
16744  {
16745  VMA_DEBUG_LOG(" vkMapMemory FAILED");
16746  FreeVulkanMemory(memTypeIndex, size, hMemory);
16747  return res;
16748  }
16749  }
16750 
16751  *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16752  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16753  (*pAllocation)->SetUserData(this, pUserData);
16754  m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16755  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16756  {
16757  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16758  }
16759 
16760  return VK_SUCCESS;
16761 }
16762 
16763 void VmaAllocator_T::GetBufferMemoryRequirements(
16764  VkBuffer hBuffer,
16765  VkMemoryRequirements& memReq,
16766  bool& requiresDedicatedAllocation,
16767  bool& prefersDedicatedAllocation) const
16768 {
16769 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16770  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16771  {
16772  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16773  memReqInfo.buffer = hBuffer;
16774 
16775  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16776 
16777  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16778  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16779 
16780  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16781 
16782  memReq = memReq2.memoryRequirements;
16783  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16784  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16785  }
16786  else
16787 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16788  {
16789  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16790  requiresDedicatedAllocation = false;
16791  prefersDedicatedAllocation = false;
16792  }
16793 }
16794 
16795 void VmaAllocator_T::GetImageMemoryRequirements(
16796  VkImage hImage,
16797  VkMemoryRequirements& memReq,
16798  bool& requiresDedicatedAllocation,
16799  bool& prefersDedicatedAllocation) const
16800 {
16801 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16802  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16803  {
16804  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16805  memReqInfo.image = hImage;
16806 
16807  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16808 
16809  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16810  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16811 
16812  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16813 
16814  memReq = memReq2.memoryRequirements;
16815  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16816  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16817  }
16818  else
16819 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16820  {
16821  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16822  requiresDedicatedAllocation = false;
16823  prefersDedicatedAllocation = false;
16824  }
16825 }
16826 
16827 VkResult VmaAllocator_T::AllocateMemory(
16828  const VkMemoryRequirements& vkMemReq,
16829  bool requiresDedicatedAllocation,
16830  bool prefersDedicatedAllocation,
16831  VkBuffer dedicatedBuffer,
16832  VkBufferUsageFlags dedicatedBufferUsage,
16833  VkImage dedicatedImage,
16834  const VmaAllocationCreateInfo& createInfo,
16835  VmaSuballocationType suballocType,
16836  size_t allocationCount,
16837  VmaAllocation* pAllocations)
16838 {
16839  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16840 
16841  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16842 
16843  if(vkMemReq.size == 0)
16844  {
16845  return VK_ERROR_VALIDATION_FAILED_EXT;
16846  }
16847  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16848  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16849  {
16850  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16851  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16852  }
16853  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16855  {
16856  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16857  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16858  }
16859  if(requiresDedicatedAllocation)
16860  {
16861  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16862  {
16863  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16864  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16865  }
16866  if(createInfo.pool != VK_NULL_HANDLE)
16867  {
16868  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16869  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16870  }
16871  }
16872  if((createInfo.pool != VK_NULL_HANDLE) &&
16873  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16874  {
16875  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16876  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16877  }
16878 
16879  if(createInfo.pool != VK_NULL_HANDLE)
16880  {
16881  VmaAllocationCreateInfo createInfoForPool = createInfo;
16882  // If memory type is not HOST_VISIBLE, disable MAPPED.
16883  if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16884  (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16885  {
16886  createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16887  }
16888 
16889  return createInfo.pool->m_BlockVector.Allocate(
16890  m_CurrentFrameIndex.load(),
16891  vkMemReq.size,
16892  vkMemReq.alignment,
16893  createInfoForPool,
16894  suballocType,
16895  allocationCount,
16896  pAllocations);
16897  }
16898  else
16899  {
16900  // Bit mask of memory Vulkan types acceptable for this allocation.
16901  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16902  uint32_t memTypeIndex = UINT32_MAX;
16903  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16904  if(res == VK_SUCCESS)
16905  {
16906  res = AllocateMemoryOfType(
16907  vkMemReq.size,
16908  vkMemReq.alignment,
16909  requiresDedicatedAllocation || prefersDedicatedAllocation,
16910  dedicatedBuffer,
16911  dedicatedBufferUsage,
16912  dedicatedImage,
16913  createInfo,
16914  memTypeIndex,
16915  suballocType,
16916  allocationCount,
16917  pAllocations);
16918  // Succeeded on first try.
16919  if(res == VK_SUCCESS)
16920  {
16921  return res;
16922  }
16923  // Allocation from this memory type failed. Try other compatible memory types.
16924  else
16925  {
16926  for(;;)
16927  {
16928  // Remove old memTypeIndex from list of possibilities.
16929  memoryTypeBits &= ~(1u << memTypeIndex);
16930  // Find alternative memTypeIndex.
16931  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16932  if(res == VK_SUCCESS)
16933  {
16934  res = AllocateMemoryOfType(
16935  vkMemReq.size,
16936  vkMemReq.alignment,
16937  requiresDedicatedAllocation || prefersDedicatedAllocation,
16938  dedicatedBuffer,
16939  dedicatedBufferUsage,
16940  dedicatedImage,
16941  createInfo,
16942  memTypeIndex,
16943  suballocType,
16944  allocationCount,
16945  pAllocations);
16946  // Allocation from this alternative memory type succeeded.
16947  if(res == VK_SUCCESS)
16948  {
16949  return res;
16950  }
16951  // else: Allocation from this memory type failed. Try next one - next loop iteration.
16952  }
16953  // No other matching memory type index could be found.
16954  else
16955  {
16956  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16957  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16958  }
16959  }
16960  }
16961  }
16962  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16963  else
16964  return res;
16965  }
16966 }
16967 
16968 void VmaAllocator_T::FreeMemory(
16969  size_t allocationCount,
16970  const VmaAllocation* pAllocations)
16971 {
16972  VMA_ASSERT(pAllocations);
16973 
16974  for(size_t allocIndex = allocationCount; allocIndex--; )
16975  {
16976  VmaAllocation allocation = pAllocations[allocIndex];
16977 
16978  if(allocation != VK_NULL_HANDLE)
16979  {
16980  if(TouchAllocation(allocation))
16981  {
16982  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16983  {
16984  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16985  }
16986 
16987  switch(allocation->GetType())
16988  {
16989  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16990  {
16991  VmaBlockVector* pBlockVector = VMA_NULL;
16992  VmaPool hPool = allocation->GetBlock()->GetParentPool();
16993  if(hPool != VK_NULL_HANDLE)
16994  {
16995  pBlockVector = &hPool->m_BlockVector;
16996  }
16997  else
16998  {
16999  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17000  pBlockVector = m_pBlockVectors[memTypeIndex];
17001  }
17002  pBlockVector->Free(allocation);
17003  }
17004  break;
17005  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17006  FreeDedicatedMemory(allocation);
17007  break;
17008  default:
17009  VMA_ASSERT(0);
17010  }
17011  }
17012 
17013  // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
17014  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
17015  allocation->SetUserData(this, VMA_NULL);
17016  m_AllocationObjectAllocator.Free(allocation);
17017  }
17018  }
17019 }
17020 
17021 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
17022 {
17023  // Initialize.
17024  InitStatInfo(pStats->total);
17025  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
17026  InitStatInfo(pStats->memoryType[i]);
17027  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
17028  InitStatInfo(pStats->memoryHeap[i]);
17029 
17030  // Process default pools.
17031  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17032  {
17033  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17034  VMA_ASSERT(pBlockVector);
17035  pBlockVector->AddStats(pStats);
17036  }
17037 
17038  // Process custom pools.
17039  {
17040  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17041  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17042  {
17043  pool->m_BlockVector.AddStats(pStats);
17044  }
17045  }
17046 
17047  // Process dedicated allocations.
17048  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17049  {
17050  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
17051  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17052  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17053  for(VmaAllocation alloc = dedicatedAllocList.Front();
17054  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
17055  {
17056  VmaStatInfo allocationStatInfo;
17057  alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo);
17058  VmaAddStatInfo(pStats->total, allocationStatInfo);
17059  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
17060  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
17061  }
17062  }
17063 
17064  // Postprocess.
17065  VmaPostprocessCalcStatInfo(pStats->total);
17066  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
17067  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
17068  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
17069  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
17070 }
17071 
17072 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
17073 {
17074 #if VMA_MEMORY_BUDGET
17075  if(m_UseExtMemoryBudget)
17076  {
17077  if(m_Budget.m_OperationsSinceBudgetFetch < 30)
17078  {
17079  VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
17080  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17081  {
17082  const uint32_t heapIndex = firstHeap + i;
17083 
17084  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17085  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17086 
17087  if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
17088  {
17089  outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
17090  outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17091  }
17092  else
17093  {
17094  outBudget->usage = 0;
17095  }
17096 
17097  // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
17098  outBudget->budget = VMA_MIN(
17099  m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
17100  }
17101  }
17102  else
17103  {
17104  UpdateVulkanBudget(); // Outside of mutex lock
17105  GetBudget(outBudget, firstHeap, heapCount); // Recursion
17106  }
17107  }
17108  else
17109 #endif
17110  {
17111  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17112  {
17113  const uint32_t heapIndex = firstHeap + i;
17114 
17115  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17116  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17117 
17118  outBudget->usage = outBudget->blockBytes;
17119  outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17120  }
17121  }
17122 }
17123 
17124 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
17125 
17126 VkResult VmaAllocator_T::DefragmentationBegin(
17127  const VmaDefragmentationInfo2& info,
17128  VmaDefragmentationStats* pStats,
17129  VmaDefragmentationContext* pContext)
17130 {
17131  if(info.pAllocationsChanged != VMA_NULL)
17132  {
17133  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
17134  }
17135 
17136  *pContext = vma_new(this, VmaDefragmentationContext_T)(
17137  this, m_CurrentFrameIndex.load(), info.flags, pStats);
17138 
17139  (*pContext)->AddPools(info.poolCount, info.pPools);
17140  (*pContext)->AddAllocations(
17142 
17143  VkResult res = (*pContext)->Defragment(
17146  info.commandBuffer, pStats, info.flags);
17147 
17148  if(res != VK_NOT_READY)
17149  {
17150  vma_delete(this, *pContext);
17151  *pContext = VMA_NULL;
17152  }
17153 
17154  return res;
17155 }
17156 
17157 VkResult VmaAllocator_T::DefragmentationEnd(
17158  VmaDefragmentationContext context)
17159 {
17160  vma_delete(this, context);
17161  return VK_SUCCESS;
17162 }
17163 
17164 VkResult VmaAllocator_T::DefragmentationPassBegin(
17166  VmaDefragmentationContext context)
17167 {
17168  return context->DefragmentPassBegin(pInfo);
17169 }
17170 VkResult VmaAllocator_T::DefragmentationPassEnd(
17171  VmaDefragmentationContext context)
17172 {
17173  return context->DefragmentPassEnd();
17174 
17175 }
17176 
17177 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17178 {
17179  if(hAllocation->CanBecomeLost())
17180  {
17181  /*
17182  Warning: This is a carefully designed algorithm.
17183  Do not modify unless you really know what you're doing :)
17184  */
17185  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17186  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17187  for(;;)
17188  {
17189  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17190  {
17191  pAllocationInfo->memoryType = UINT32_MAX;
17192  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17193  pAllocationInfo->offset = 0;
17194  pAllocationInfo->size = hAllocation->GetSize();
17195  pAllocationInfo->pMappedData = VMA_NULL;
17196  pAllocationInfo->pUserData = hAllocation->GetUserData();
17197  return;
17198  }
17199  else if(localLastUseFrameIndex == localCurrFrameIndex)
17200  {
17201  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17202  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17203  pAllocationInfo->offset = hAllocation->GetOffset();
17204  pAllocationInfo->size = hAllocation->GetSize();
17205  pAllocationInfo->pMappedData = VMA_NULL;
17206  pAllocationInfo->pUserData = hAllocation->GetUserData();
17207  return;
17208  }
17209  else // Last use time earlier than current time.
17210  {
17211  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17212  {
17213  localLastUseFrameIndex = localCurrFrameIndex;
17214  }
17215  }
17216  }
17217  }
17218  else
17219  {
17220 #if VMA_STATS_STRING_ENABLED
17221  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17222  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17223  for(;;)
17224  {
17225  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17226  if(localLastUseFrameIndex == localCurrFrameIndex)
17227  {
17228  break;
17229  }
17230  else // Last use time earlier than current time.
17231  {
17232  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17233  {
17234  localLastUseFrameIndex = localCurrFrameIndex;
17235  }
17236  }
17237  }
17238 #endif
17239 
17240  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17241  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17242  pAllocationInfo->offset = hAllocation->GetOffset();
17243  pAllocationInfo->size = hAllocation->GetSize();
17244  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17245  pAllocationInfo->pUserData = hAllocation->GetUserData();
17246  }
17247 }
17248 
17249 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17250 {
17251  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17252  if(hAllocation->CanBecomeLost())
17253  {
17254  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17255  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17256  for(;;)
17257  {
17258  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17259  {
17260  return false;
17261  }
17262  else if(localLastUseFrameIndex == localCurrFrameIndex)
17263  {
17264  return true;
17265  }
17266  else // Last use time earlier than current time.
17267  {
17268  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17269  {
17270  localLastUseFrameIndex = localCurrFrameIndex;
17271  }
17272  }
17273  }
17274  }
17275  else
17276  {
17277 #if VMA_STATS_STRING_ENABLED
17278  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17279  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17280  for(;;)
17281  {
17282  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17283  if(localLastUseFrameIndex == localCurrFrameIndex)
17284  {
17285  break;
17286  }
17287  else // Last use time earlier than current time.
17288  {
17289  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17290  {
17291  localLastUseFrameIndex = localCurrFrameIndex;
17292  }
17293  }
17294  }
17295 #endif
17296 
17297  return true;
17298  }
17299 }
17300 
17301 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17302 {
17303  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17304 
17305  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17306 
17307  if(newCreateInfo.maxBlockCount == 0)
17308  {
17309  newCreateInfo.maxBlockCount = SIZE_MAX;
17310  }
17311  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17312  {
17313  return VK_ERROR_INITIALIZATION_FAILED;
17314  }
17315  // Memory type index out of range or forbidden.
17316  if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17317  ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17318  {
17319  return VK_ERROR_FEATURE_NOT_PRESENT;
17320  }
17321  if(newCreateInfo.minAllocationAlignment > 0)
17322  {
17323  VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
17324  }
17325 
17326  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17327 
17328  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17329 
17330  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17331  if(res != VK_SUCCESS)
17332  {
17333  vma_delete(this, *pPool);
17334  *pPool = VMA_NULL;
17335  return res;
17336  }
17337 
17338  // Add to m_Pools.
17339  {
17340  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17341  (*pPool)->SetId(m_NextPoolId++);
17342  m_Pools.PushBack(*pPool);
17343  }
17344 
17345  return VK_SUCCESS;
17346 }
17347 
17348 void VmaAllocator_T::DestroyPool(VmaPool pool)
17349 {
17350  // Remove from m_Pools.
17351  {
17352  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17353  m_Pools.Remove(pool);
17354  }
17355 
17356  vma_delete(this, pool);
17357 }
17358 
17359 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17360 {
17361  pool->m_BlockVector.GetPoolStats(pPoolStats);
17362 }
17363 
17364 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17365 {
17366  m_CurrentFrameIndex.store(frameIndex);
17367 
17368 #if VMA_MEMORY_BUDGET
17369  if(m_UseExtMemoryBudget)
17370  {
17371  UpdateVulkanBudget();
17372  }
17373 #endif // #if VMA_MEMORY_BUDGET
17374 }
17375 
17376 void VmaAllocator_T::MakePoolAllocationsLost(
17377  VmaPool hPool,
17378  size_t* pLostAllocationCount)
17379 {
17380  hPool->m_BlockVector.MakePoolAllocationsLost(
17381  m_CurrentFrameIndex.load(),
17382  pLostAllocationCount);
17383 }
17384 
17385 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17386 {
17387  return hPool->m_BlockVector.CheckCorruption();
17388 }
17389 
17390 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17391 {
17392  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17393 
17394  // Process default pools.
17395  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17396  {
17397  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17398  {
17399  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17400  VMA_ASSERT(pBlockVector);
17401  VkResult localRes = pBlockVector->CheckCorruption();
17402  switch(localRes)
17403  {
17404  case VK_ERROR_FEATURE_NOT_PRESENT:
17405  break;
17406  case VK_SUCCESS:
17407  finalRes = VK_SUCCESS;
17408  break;
17409  default:
17410  return localRes;
17411  }
17412  }
17413  }
17414 
17415  // Process custom pools.
17416  {
17417  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17418  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17419  {
17420  if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17421  {
17422  VkResult localRes = pool->m_BlockVector.CheckCorruption();
17423  switch(localRes)
17424  {
17425  case VK_ERROR_FEATURE_NOT_PRESENT:
17426  break;
17427  case VK_SUCCESS:
17428  finalRes = VK_SUCCESS;
17429  break;
17430  default:
17431  return localRes;
17432  }
17433  }
17434  }
17435  }
17436 
17437  return finalRes;
17438 }
17439 
17440 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17441 {
17442  *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17443  (*pAllocation)->InitLost();
17444 }
17445 
17446 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
17447 template<typename T>
17448 struct AtomicTransactionalIncrement
17449 {
17450 public:
17451  typedef std::atomic<T> AtomicT;
17452  ~AtomicTransactionalIncrement()
17453  {
17454  if(m_Atomic)
17455  --(*m_Atomic);
17456  }
17457  T Increment(AtomicT* atomic)
17458  {
17459  m_Atomic = atomic;
17460  return m_Atomic->fetch_add(1);
17461  }
17462  void Commit()
17463  {
17464  m_Atomic = nullptr;
17465  }
17466 
17467 private:
17468  AtomicT* m_Atomic = nullptr;
17469 };
17470 
17471 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17472 {
17473  AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
17474  const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
17475 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
17476  if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
17477  {
17478  return VK_ERROR_TOO_MANY_OBJECTS;
17479  }
17480 #endif
17481 
17482  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17483 
17484  // HeapSizeLimit is in effect for this heap.
17485  if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17486  {
17487  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17488  VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17489  for(;;)
17490  {
17491  const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17492  if(blockBytesAfterAllocation > heapSize)
17493  {
17494  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17495  }
17496  if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17497  {
17498  break;
17499  }
17500  }
17501  }
17502  else
17503  {
17504  m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17505  }
17506 
17507  // VULKAN CALL vkAllocateMemory.
17508  VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17509 
17510  if(res == VK_SUCCESS)
17511  {
17512 #if VMA_MEMORY_BUDGET
17513  ++m_Budget.m_OperationsSinceBudgetFetch;
17514 #endif
17515 
17516  // Informative callback.
17517  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17518  {
17519  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17520  }
17521 
17522  deviceMemoryCountIncrement.Commit();
17523  }
17524  else
17525  {
17526  m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17527  }
17528 
17529  return res;
17530 }
17531 
17532 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17533 {
17534  // Informative callback.
17535  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17536  {
17537  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17538  }
17539 
17540  // VULKAN CALL vkFreeMemory.
17541  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17542 
17543  m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17544 
17545  --m_DeviceMemoryCount;
17546 }
17547 
17548 VkResult VmaAllocator_T::BindVulkanBuffer(
17549  VkDeviceMemory memory,
17550  VkDeviceSize memoryOffset,
17551  VkBuffer buffer,
17552  const void* pNext)
17553 {
17554  if(pNext != VMA_NULL)
17555  {
17556 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17557  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17558  m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17559  {
17560  VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17561  bindBufferMemoryInfo.pNext = pNext;
17562  bindBufferMemoryInfo.buffer = buffer;
17563  bindBufferMemoryInfo.memory = memory;
17564  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17565  return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17566  }
17567  else
17568 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17569  {
17570  return VK_ERROR_EXTENSION_NOT_PRESENT;
17571  }
17572  }
17573  else
17574  {
17575  return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17576  }
17577 }
17578 
17579 VkResult VmaAllocator_T::BindVulkanImage(
17580  VkDeviceMemory memory,
17581  VkDeviceSize memoryOffset,
17582  VkImage image,
17583  const void* pNext)
17584 {
17585  if(pNext != VMA_NULL)
17586  {
17587 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17588  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17589  m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17590  {
17591  VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17592  bindBufferMemoryInfo.pNext = pNext;
17593  bindBufferMemoryInfo.image = image;
17594  bindBufferMemoryInfo.memory = memory;
17595  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17596  return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17597  }
17598  else
17599 #endif // #if VMA_BIND_MEMORY2
17600  {
17601  return VK_ERROR_EXTENSION_NOT_PRESENT;
17602  }
17603  }
17604  else
17605  {
17606  return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17607  }
17608 }
17609 
17610 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17611 {
17612  if(hAllocation->CanBecomeLost())
17613  {
17614  return VK_ERROR_MEMORY_MAP_FAILED;
17615  }
17616 
17617  switch(hAllocation->GetType())
17618  {
17619  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17620  {
17621  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17622  char *pBytes = VMA_NULL;
17623  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17624  if(res == VK_SUCCESS)
17625  {
17626  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17627  hAllocation->BlockAllocMap();
17628  }
17629  return res;
17630  }
17631  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17632  return hAllocation->DedicatedAllocMap(this, ppData);
17633  default:
17634  VMA_ASSERT(0);
17635  return VK_ERROR_MEMORY_MAP_FAILED;
17636  }
17637 }
17638 
17639 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17640 {
17641  switch(hAllocation->GetType())
17642  {
17643  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17644  {
17645  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17646  hAllocation->BlockAllocUnmap();
17647  pBlock->Unmap(this, 1);
17648  }
17649  break;
17650  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17651  hAllocation->DedicatedAllocUnmap(this);
17652  break;
17653  default:
17654  VMA_ASSERT(0);
17655  }
17656 }
17657 
17658 VkResult VmaAllocator_T::BindBufferMemory(
17659  VmaAllocation hAllocation,
17660  VkDeviceSize allocationLocalOffset,
17661  VkBuffer hBuffer,
17662  const void* pNext)
17663 {
17664  VkResult res = VK_SUCCESS;
17665  switch(hAllocation->GetType())
17666  {
17667  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17668  res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17669  break;
17670  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17671  {
17672  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17673  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17674  res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17675  break;
17676  }
17677  default:
17678  VMA_ASSERT(0);
17679  }
17680  return res;
17681 }
17682 
17683 VkResult VmaAllocator_T::BindImageMemory(
17684  VmaAllocation hAllocation,
17685  VkDeviceSize allocationLocalOffset,
17686  VkImage hImage,
17687  const void* pNext)
17688 {
17689  VkResult res = VK_SUCCESS;
17690  switch(hAllocation->GetType())
17691  {
17692  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17693  res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17694  break;
17695  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17696  {
17697  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17698  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17699  res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17700  break;
17701  }
17702  default:
17703  VMA_ASSERT(0);
17704  }
17705  return res;
17706 }
17707 
17708 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17709  VmaAllocation hAllocation,
17710  VkDeviceSize offset, VkDeviceSize size,
17711  VMA_CACHE_OPERATION op)
17712 {
17713  VkResult res = VK_SUCCESS;
17714 
17715  VkMappedMemoryRange memRange = {};
17716  if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17717  {
17718  switch(op)
17719  {
17720  case VMA_CACHE_FLUSH:
17721  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17722  break;
17723  case VMA_CACHE_INVALIDATE:
17724  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17725  break;
17726  default:
17727  VMA_ASSERT(0);
17728  }
17729  }
17730  // else: Just ignore this call.
17731  return res;
17732 }
17733 
17734 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17735  uint32_t allocationCount,
17736  const VmaAllocation* allocations,
17737  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17738  VMA_CACHE_OPERATION op)
17739 {
17740  typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17741  typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17742  RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17743 
17744  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17745  {
17746  const VmaAllocation alloc = allocations[allocIndex];
17747  const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17748  const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17749  VkMappedMemoryRange newRange;
17750  if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17751  {
17752  ranges.push_back(newRange);
17753  }
17754  }
17755 
17756  VkResult res = VK_SUCCESS;
17757  if(!ranges.empty())
17758  {
17759  switch(op)
17760  {
17761  case VMA_CACHE_FLUSH:
17762  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17763  break;
17764  case VMA_CACHE_INVALIDATE:
17765  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17766  break;
17767  default:
17768  VMA_ASSERT(0);
17769  }
17770  }
17771  // else: Just ignore this call.
17772  return res;
17773 }
17774 
17775 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17776 {
17777  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17778 
17779  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17780  {
17781  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17782  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
17783  dedicatedAllocations.Remove(allocation);
17784  }
17785 
17786  VkDeviceMemory hMemory = allocation->GetMemory();
17787 
17788  /*
17789  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17790  before vkFreeMemory.
17791 
17792  if(allocation->GetMappedData() != VMA_NULL)
17793  {
17794  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17795  }
17796  */
17797 
17798  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17799 
17800  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17801 }
17802 
17803 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17804 {
17805  VkBufferCreateInfo dummyBufCreateInfo;
17806  VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17807 
17808  uint32_t memoryTypeBits = 0;
17809 
17810  // Create buffer.
17811  VkBuffer buf = VK_NULL_HANDLE;
17812  VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17813  m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17814  if(res == VK_SUCCESS)
17815  {
17816  // Query for supported memory types.
17817  VkMemoryRequirements memReq;
17818  (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17819  memoryTypeBits = memReq.memoryTypeBits;
17820 
17821  // Destroy buffer.
17822  (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17823  }
17824 
17825  return memoryTypeBits;
17826 }
17827 
17828 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17829 {
17830  // Make sure memory information is already fetched.
17831  VMA_ASSERT(GetMemoryTypeCount() > 0);
17832 
17833  uint32_t memoryTypeBits = UINT32_MAX;
17834 
17835  if(!m_UseAmdDeviceCoherentMemory)
17836  {
17837  // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17838  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17839  {
17840  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17841  {
17842  memoryTypeBits &= ~(1u << memTypeIndex);
17843  }
17844  }
17845  }
17846 
17847  return memoryTypeBits;
17848 }
17849 
17850 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17851  VmaAllocation allocation,
17852  VkDeviceSize offset, VkDeviceSize size,
17853  VkMappedMemoryRange& outRange) const
17854 {
17855  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17856  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17857  {
17858  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17859  const VkDeviceSize allocationSize = allocation->GetSize();
17860  VMA_ASSERT(offset <= allocationSize);
17861 
17862  outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17863  outRange.pNext = VMA_NULL;
17864  outRange.memory = allocation->GetMemory();
17865 
17866  switch(allocation->GetType())
17867  {
17868  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17869  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17870  if(size == VK_WHOLE_SIZE)
17871  {
17872  outRange.size = allocationSize - outRange.offset;
17873  }
17874  else
17875  {
17876  VMA_ASSERT(offset + size <= allocationSize);
17877  outRange.size = VMA_MIN(
17878  VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17879  allocationSize - outRange.offset);
17880  }
17881  break;
17882  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17883  {
17884  // 1. Still within this allocation.
17885  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17886  if(size == VK_WHOLE_SIZE)
17887  {
17888  size = allocationSize - offset;
17889  }
17890  else
17891  {
17892  VMA_ASSERT(offset + size <= allocationSize);
17893  }
17894  outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17895 
17896  // 2. Adjust to whole block.
17897  const VkDeviceSize allocationOffset = allocation->GetOffset();
17898  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17899  const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17900  outRange.offset += allocationOffset;
17901  outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17902 
17903  break;
17904  }
17905  default:
17906  VMA_ASSERT(0);
17907  }
17908  return true;
17909  }
17910  return false;
17911 }
17912 
17913 #if VMA_MEMORY_BUDGET
17914 
17915 void VmaAllocator_T::UpdateVulkanBudget()
17916 {
17917  VMA_ASSERT(m_UseExtMemoryBudget);
17918 
17919  VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17920 
17921  VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17922  VmaPnextChainPushFront(&memProps, &budgetProps);
17923 
17924  GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17925 
17926  {
17927  VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17928 
17929  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17930  {
17931  m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17932  m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17933  m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17934 
17935  // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17936  if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17937  {
17938  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17939  }
17940  else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17941  {
17942  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17943  }
17944  if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17945  {
17946  m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17947  }
17948  }
17949  m_Budget.m_OperationsSinceBudgetFetch = 0;
17950  }
17951 }
17952 
17953 #endif // #if VMA_MEMORY_BUDGET
17954 
17955 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17956 {
17957  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17958  !hAllocation->CanBecomeLost() &&
17959  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17960  {
17961  void* pData = VMA_NULL;
17962  VkResult res = Map(hAllocation, &pData);
17963  if(res == VK_SUCCESS)
17964  {
17965  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17966  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17967  Unmap(hAllocation);
17968  }
17969  else
17970  {
17971  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17972  }
17973  }
17974 }
17975 
17976 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17977 {
17978  uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17979  if(memoryTypeBits == UINT32_MAX)
17980  {
17981  memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17982  m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17983  }
17984  return memoryTypeBits;
17985 }
17986 
17987 #if VMA_STATS_STRING_ENABLED
17988 
17989 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17990 {
17991  bool dedicatedAllocationsStarted = false;
17992  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17993  {
17994  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17995  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17996  if(!dedicatedAllocList.IsEmpty())
17997  {
17998  if(dedicatedAllocationsStarted == false)
17999  {
18000  dedicatedAllocationsStarted = true;
18001  json.WriteString("DedicatedAllocations");
18002  json.BeginObject();
18003  }
18004 
18005  json.BeginString("Type ");
18006  json.ContinueString(memTypeIndex);
18007  json.EndString();
18008 
18009  json.BeginArray();
18010 
18011  for(VmaAllocation alloc = dedicatedAllocList.Front();
18012  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
18013  {
18014  json.BeginObject(true);
18015  alloc->PrintParameters(json);
18016  json.EndObject();
18017  }
18018 
18019  json.EndArray();
18020  }
18021  }
18022  if(dedicatedAllocationsStarted)
18023  {
18024  json.EndObject();
18025  }
18026 
18027  {
18028  bool allocationsStarted = false;
18029  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
18030  {
18031  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
18032  {
18033  if(allocationsStarted == false)
18034  {
18035  allocationsStarted = true;
18036  json.WriteString("DefaultPools");
18037  json.BeginObject();
18038  }
18039 
18040  json.BeginString("Type ");
18041  json.ContinueString(memTypeIndex);
18042  json.EndString();
18043 
18044  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
18045  }
18046  }
18047  if(allocationsStarted)
18048  {
18049  json.EndObject();
18050  }
18051  }
18052 
18053  // Custom pools
18054  {
18055  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
18056  if(!m_Pools.IsEmpty())
18057  {
18058  json.WriteString("Pools");
18059  json.BeginObject();
18060  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
18061  {
18062  json.BeginString();
18063  json.ContinueString(pool->GetId());
18064  json.EndString();
18065 
18066  pool->m_BlockVector.PrintDetailedMap(json);
18067  }
18068  json.EndObject();
18069  }
18070  }
18071 }
18072 
18073 #endif // #if VMA_STATS_STRING_ENABLED
18074 
18076 // Public interface
18077 
18078 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
18079  const VmaAllocatorCreateInfo* pCreateInfo,
18080  VmaAllocator* pAllocator)
18081 {
18082  VMA_ASSERT(pCreateInfo && pAllocator);
18083  VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
18084  (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
18085  VMA_DEBUG_LOG("vmaCreateAllocator");
18086  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
18087  return (*pAllocator)->Init(pCreateInfo);
18088 }
18089 
18090 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
18091  VmaAllocator allocator)
18092 {
18093  if(allocator != VK_NULL_HANDLE)
18094  {
18095  VMA_DEBUG_LOG("vmaDestroyAllocator");
18096  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
18097  vma_delete(&allocationCallbacks, allocator);
18098  }
18099 }
18100 
18101 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
18102 {
18103  VMA_ASSERT(allocator && pAllocatorInfo);
18104  pAllocatorInfo->instance = allocator->m_hInstance;
18105  pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
18106  pAllocatorInfo->device = allocator->m_hDevice;
18107 }
18108 
18109 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
18110  VmaAllocator allocator,
18111  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
18112 {
18113  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
18114  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
18115 }
18116 
18117 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
18118  VmaAllocator allocator,
18119  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
18120 {
18121  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
18122  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
18123 }
18124 
18125 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
18126  VmaAllocator allocator,
18127  uint32_t memoryTypeIndex,
18128  VkMemoryPropertyFlags* pFlags)
18129 {
18130  VMA_ASSERT(allocator && pFlags);
18131  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
18132  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
18133 }
18134 
18135 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
18136  VmaAllocator allocator,
18137  uint32_t frameIndex)
18138 {
18139  VMA_ASSERT(allocator);
18140  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
18141 
18142  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18143 
18144  allocator->SetCurrentFrameIndex(frameIndex);
18145 }
18146 
18147 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
18148  VmaAllocator allocator,
18149  VmaStats* pStats)
18150 {
18151  VMA_ASSERT(allocator && pStats);
18152  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18153  allocator->CalculateStats(pStats);
18154 }
18155 
18156 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
18157  VmaAllocator allocator,
18158  VmaBudget* pBudget)
18159 {
18160  VMA_ASSERT(allocator && pBudget);
18161  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18162  allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
18163 }
18164 
18165 #if VMA_STATS_STRING_ENABLED
18166 
18167 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
18168  VmaAllocator allocator,
18169  char** ppStatsString,
18170  VkBool32 detailedMap)
18171 {
18172  VMA_ASSERT(allocator && ppStatsString);
18173  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18174 
18175  VmaStringBuilder sb(allocator);
18176  {
18177  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
18178  json.BeginObject();
18179 
18180  VmaBudget budget[VK_MAX_MEMORY_HEAPS];
18181  allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
18182 
18183  VmaStats stats;
18184  allocator->CalculateStats(&stats);
18185 
18186  json.WriteString("Total");
18187  VmaPrintStatInfo(json, stats.total);
18188 
18189  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18190  {
18191  json.BeginString("Heap ");
18192  json.ContinueString(heapIndex);
18193  json.EndString();
18194  json.BeginObject();
18195 
18196  json.WriteString("Size");
18197  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18198 
18199  json.WriteString("Flags");
18200  json.BeginArray(true);
18201  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18202  {
18203  json.WriteString("DEVICE_LOCAL");
18204  }
18205  json.EndArray();
18206 
18207  json.WriteString("Budget");
18208  json.BeginObject();
18209  {
18210  json.WriteString("BlockBytes");
18211  json.WriteNumber(budget[heapIndex].blockBytes);
18212  json.WriteString("AllocationBytes");
18213  json.WriteNumber(budget[heapIndex].allocationBytes);
18214  json.WriteString("Usage");
18215  json.WriteNumber(budget[heapIndex].usage);
18216  json.WriteString("Budget");
18217  json.WriteNumber(budget[heapIndex].budget);
18218  }
18219  json.EndObject();
18220 
18221  if(stats.memoryHeap[heapIndex].blockCount > 0)
18222  {
18223  json.WriteString("Stats");
18224  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18225  }
18226 
18227  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18228  {
18229  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18230  {
18231  json.BeginString("Type ");
18232  json.ContinueString(typeIndex);
18233  json.EndString();
18234 
18235  json.BeginObject();
18236 
18237  json.WriteString("Flags");
18238  json.BeginArray(true);
18239  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18240  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18241  {
18242  json.WriteString("DEVICE_LOCAL");
18243  }
18244  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18245  {
18246  json.WriteString("HOST_VISIBLE");
18247  }
18248  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18249  {
18250  json.WriteString("HOST_COHERENT");
18251  }
18252  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18253  {
18254  json.WriteString("HOST_CACHED");
18255  }
18256  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18257  {
18258  json.WriteString("LAZILY_ALLOCATED");
18259  }
18260 #if VMA_VULKAN_VERSION >= 1001000
18261  if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18262  {
18263  json.WriteString("PROTECTED");
18264  }
18265 #endif // #if VMA_VULKAN_VERSION >= 1001000
18266 #if VK_AMD_device_coherent_memory
18267  if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18268  {
18269  json.WriteString("DEVICE_COHERENT");
18270  }
18271  if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18272  {
18273  json.WriteString("DEVICE_UNCACHED");
18274  }
18275 #endif // #if VK_AMD_device_coherent_memory
18276  json.EndArray();
18277 
18278  if(stats.memoryType[typeIndex].blockCount > 0)
18279  {
18280  json.WriteString("Stats");
18281  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18282  }
18283 
18284  json.EndObject();
18285  }
18286  }
18287 
18288  json.EndObject();
18289  }
18290  if(detailedMap == VK_TRUE)
18291  {
18292  allocator->PrintDetailedMap(json);
18293  }
18294 
18295  json.EndObject();
18296  }
18297 
18298  const size_t len = sb.GetLength();
18299  char* const pChars = vma_new_array(allocator, char, len + 1);
18300  if(len > 0)
18301  {
18302  memcpy(pChars, sb.GetData(), len);
18303  }
18304  pChars[len] = '\0';
18305  *ppStatsString = pChars;
18306 }
18307 
18308 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18309  VmaAllocator allocator,
18310  char* pStatsString)
18311 {
18312  if(pStatsString != VMA_NULL)
18313  {
18314  VMA_ASSERT(allocator);
18315  size_t len = strlen(pStatsString);
18316  vma_delete_array(allocator, pStatsString, len + 1);
18317  }
18318 }
18319 
18320 #endif // #if VMA_STATS_STRING_ENABLED
18321 
18322 /*
18323 This function is not protected by any mutex because it just reads immutable data.
18324 */
18325 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18326  VmaAllocator allocator,
18327  uint32_t memoryTypeBits,
18328  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18329  uint32_t* pMemoryTypeIndex)
18330 {
18331  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18332  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18333  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18334 
18335  memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18336 
18337  if(pAllocationCreateInfo->memoryTypeBits != 0)
18338  {
18339  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18340  }
18341 
18342  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18343  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18344  uint32_t notPreferredFlags = 0;
18345 
18346  // Convert usage to requiredFlags and preferredFlags.
18347  switch(pAllocationCreateInfo->usage)
18348  {
18350  break;
18352  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18353  {
18354  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18355  }
18356  break;
18358  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18359  break;
18361  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18362  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18363  {
18364  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18365  }
18366  break;
18368  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18369  preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18370  break;
18372  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18373  break;
18375  requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18376  break;
18377  default:
18378  VMA_ASSERT(0);
18379  break;
18380  }
18381 
18382  // Avoid DEVICE_COHERENT unless explicitly requested.
18383  if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18384  (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18385  {
18386  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18387  }
18388 
18389  *pMemoryTypeIndex = UINT32_MAX;
18390  uint32_t minCost = UINT32_MAX;
18391  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18392  memTypeIndex < allocator->GetMemoryTypeCount();
18393  ++memTypeIndex, memTypeBit <<= 1)
18394  {
18395  // This memory type is acceptable according to memoryTypeBits bitmask.
18396  if((memTypeBit & memoryTypeBits) != 0)
18397  {
18398  const VkMemoryPropertyFlags currFlags =
18399  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18400  // This memory type contains requiredFlags.
18401  if((requiredFlags & ~currFlags) == 0)
18402  {
18403  // Calculate cost as number of bits from preferredFlags not present in this memory type.
18404  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18405  VmaCountBitsSet(currFlags & notPreferredFlags);
18406  // Remember memory type with lowest cost.
18407  if(currCost < minCost)
18408  {
18409  *pMemoryTypeIndex = memTypeIndex;
18410  if(currCost == 0)
18411  {
18412  return VK_SUCCESS;
18413  }
18414  minCost = currCost;
18415  }
18416  }
18417  }
18418  }
18419  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18420 }
18421 
18422 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18423  VmaAllocator allocator,
18424  const VkBufferCreateInfo* pBufferCreateInfo,
18425  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18426  uint32_t* pMemoryTypeIndex)
18427 {
18428  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18429  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18430  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18431  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18432 
18433  const VkDevice hDev = allocator->m_hDevice;
18434  VkBuffer hBuffer = VK_NULL_HANDLE;
18435  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18436  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18437  if(res == VK_SUCCESS)
18438  {
18439  VkMemoryRequirements memReq = {};
18440  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18441  hDev, hBuffer, &memReq);
18442 
18443  res = vmaFindMemoryTypeIndex(
18444  allocator,
18445  memReq.memoryTypeBits,
18446  pAllocationCreateInfo,
18447  pMemoryTypeIndex);
18448 
18449  allocator->GetVulkanFunctions().vkDestroyBuffer(
18450  hDev, hBuffer, allocator->GetAllocationCallbacks());
18451  }
18452  return res;
18453 }
18454 
18455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18456  VmaAllocator allocator,
18457  const VkImageCreateInfo* pImageCreateInfo,
18458  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18459  uint32_t* pMemoryTypeIndex)
18460 {
18461  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18462  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18463  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18464  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18465 
18466  const VkDevice hDev = allocator->m_hDevice;
18467  VkImage hImage = VK_NULL_HANDLE;
18468  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18469  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18470  if(res == VK_SUCCESS)
18471  {
18472  VkMemoryRequirements memReq = {};
18473  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18474  hDev, hImage, &memReq);
18475 
18476  res = vmaFindMemoryTypeIndex(
18477  allocator,
18478  memReq.memoryTypeBits,
18479  pAllocationCreateInfo,
18480  pMemoryTypeIndex);
18481 
18482  allocator->GetVulkanFunctions().vkDestroyImage(
18483  hDev, hImage, allocator->GetAllocationCallbacks());
18484  }
18485  return res;
18486 }
18487 
18488 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18489  VmaAllocator allocator,
18490  const VmaPoolCreateInfo* pCreateInfo,
18491  VmaPool* pPool)
18492 {
18493  VMA_ASSERT(allocator && pCreateInfo && pPool);
18494 
18495  VMA_DEBUG_LOG("vmaCreatePool");
18496 
18497  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18498 
18499  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18500 
18501 #if VMA_RECORDING_ENABLED
18502  if(allocator->GetRecorder() != VMA_NULL)
18503  {
18504  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18505  }
18506 #endif
18507 
18508  return res;
18509 }
18510 
18511 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18512  VmaAllocator allocator,
18513  VmaPool pool)
18514 {
18515  VMA_ASSERT(allocator);
18516 
18517  if(pool == VK_NULL_HANDLE)
18518  {
18519  return;
18520  }
18521 
18522  VMA_DEBUG_LOG("vmaDestroyPool");
18523 
18524  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18525 
18526 #if VMA_RECORDING_ENABLED
18527  if(allocator->GetRecorder() != VMA_NULL)
18528  {
18529  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18530  }
18531 #endif
18532 
18533  allocator->DestroyPool(pool);
18534 }
18535 
18536 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18537  VmaAllocator allocator,
18538  VmaPool pool,
18539  VmaPoolStats* pPoolStats)
18540 {
18541  VMA_ASSERT(allocator && pool && pPoolStats);
18542 
18543  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18544 
18545  allocator->GetPoolStats(pool, pPoolStats);
18546 }
18547 
18548 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18549  VmaAllocator allocator,
18550  VmaPool pool,
18551  size_t* pLostAllocationCount)
18552 {
18553  VMA_ASSERT(allocator && pool);
18554 
18555  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18556 
18557 #if VMA_RECORDING_ENABLED
18558  if(allocator->GetRecorder() != VMA_NULL)
18559  {
18560  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18561  }
18562 #endif
18563 
18564  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18565 }
18566 
18567 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18568 {
18569  VMA_ASSERT(allocator && pool);
18570 
18571  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18572 
18573  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18574 
18575  return allocator->CheckPoolCorruption(pool);
18576 }
18577 
18578 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18579  VmaAllocator allocator,
18580  VmaPool pool,
18581  const char** ppName)
18582 {
18583  VMA_ASSERT(allocator && pool && ppName);
18584 
18585  VMA_DEBUG_LOG("vmaGetPoolName");
18586 
18587  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18588 
18589  *ppName = pool->GetName();
18590 }
18591 
18592 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18593  VmaAllocator allocator,
18594  VmaPool pool,
18595  const char* pName)
18596 {
18597  VMA_ASSERT(allocator && pool);
18598 
18599  VMA_DEBUG_LOG("vmaSetPoolName");
18600 
18601  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18602 
18603  pool->SetName(pName);
18604 
18605 #if VMA_RECORDING_ENABLED
18606  if(allocator->GetRecorder() != VMA_NULL)
18607  {
18608  allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18609  }
18610 #endif
18611 }
18612 
18613 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18614  VmaAllocator allocator,
18615  const VkMemoryRequirements* pVkMemoryRequirements,
18616  const VmaAllocationCreateInfo* pCreateInfo,
18617  VmaAllocation* pAllocation,
18618  VmaAllocationInfo* pAllocationInfo)
18619 {
18620  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18621 
18622  VMA_DEBUG_LOG("vmaAllocateMemory");
18623 
18624  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18625 
18626  VkResult result = allocator->AllocateMemory(
18627  *pVkMemoryRequirements,
18628  false, // requiresDedicatedAllocation
18629  false, // prefersDedicatedAllocation
18630  VK_NULL_HANDLE, // dedicatedBuffer
18631  UINT32_MAX, // dedicatedBufferUsage
18632  VK_NULL_HANDLE, // dedicatedImage
18633  *pCreateInfo,
18634  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18635  1, // allocationCount
18636  pAllocation);
18637 
18638 #if VMA_RECORDING_ENABLED
18639  if(allocator->GetRecorder() != VMA_NULL)
18640  {
18641  allocator->GetRecorder()->RecordAllocateMemory(
18642  allocator->GetCurrentFrameIndex(),
18643  *pVkMemoryRequirements,
18644  *pCreateInfo,
18645  *pAllocation);
18646  }
18647 #endif
18648 
18649  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18650  {
18651  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18652  }
18653 
18654  return result;
18655 }
18656 
18657 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18658  VmaAllocator allocator,
18659  const VkMemoryRequirements* pVkMemoryRequirements,
18660  const VmaAllocationCreateInfo* pCreateInfo,
18661  size_t allocationCount,
18662  VmaAllocation* pAllocations,
18663  VmaAllocationInfo* pAllocationInfo)
18664 {
18665  if(allocationCount == 0)
18666  {
18667  return VK_SUCCESS;
18668  }
18669 
18670  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18671 
18672  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18673 
18674  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18675 
18676  VkResult result = allocator->AllocateMemory(
18677  *pVkMemoryRequirements,
18678  false, // requiresDedicatedAllocation
18679  false, // prefersDedicatedAllocation
18680  VK_NULL_HANDLE, // dedicatedBuffer
18681  UINT32_MAX, // dedicatedBufferUsage
18682  VK_NULL_HANDLE, // dedicatedImage
18683  *pCreateInfo,
18684  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18685  allocationCount,
18686  pAllocations);
18687 
18688 #if VMA_RECORDING_ENABLED
18689  if(allocator->GetRecorder() != VMA_NULL)
18690  {
18691  allocator->GetRecorder()->RecordAllocateMemoryPages(
18692  allocator->GetCurrentFrameIndex(),
18693  *pVkMemoryRequirements,
18694  *pCreateInfo,
18695  (uint64_t)allocationCount,
18696  pAllocations);
18697  }
18698 #endif
18699 
18700  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18701  {
18702  for(size_t i = 0; i < allocationCount; ++i)
18703  {
18704  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18705  }
18706  }
18707 
18708  return result;
18709 }
18710 
18711 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18712  VmaAllocator allocator,
18713  VkBuffer buffer,
18714  const VmaAllocationCreateInfo* pCreateInfo,
18715  VmaAllocation* pAllocation,
18716  VmaAllocationInfo* pAllocationInfo)
18717 {
18718  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18719 
18720  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18721 
18722  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18723 
18724  VkMemoryRequirements vkMemReq = {};
18725  bool requiresDedicatedAllocation = false;
18726  bool prefersDedicatedAllocation = false;
18727  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18728  requiresDedicatedAllocation,
18729  prefersDedicatedAllocation);
18730 
18731  VkResult result = allocator->AllocateMemory(
18732  vkMemReq,
18733  requiresDedicatedAllocation,
18734  prefersDedicatedAllocation,
18735  buffer, // dedicatedBuffer
18736  UINT32_MAX, // dedicatedBufferUsage
18737  VK_NULL_HANDLE, // dedicatedImage
18738  *pCreateInfo,
18739  VMA_SUBALLOCATION_TYPE_BUFFER,
18740  1, // allocationCount
18741  pAllocation);
18742 
18743 #if VMA_RECORDING_ENABLED
18744  if(allocator->GetRecorder() != VMA_NULL)
18745  {
18746  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18747  allocator->GetCurrentFrameIndex(),
18748  vkMemReq,
18749  requiresDedicatedAllocation,
18750  prefersDedicatedAllocation,
18751  *pCreateInfo,
18752  *pAllocation);
18753  }
18754 #endif
18755 
18756  if(pAllocationInfo && result == VK_SUCCESS)
18757  {
18758  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18759  }
18760 
18761  return result;
18762 }
18763 
18764 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18765  VmaAllocator allocator,
18766  VkImage image,
18767  const VmaAllocationCreateInfo* pCreateInfo,
18768  VmaAllocation* pAllocation,
18769  VmaAllocationInfo* pAllocationInfo)
18770 {
18771  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18772 
18773  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18774 
18775  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18776 
18777  VkMemoryRequirements vkMemReq = {};
18778  bool requiresDedicatedAllocation = false;
18779  bool prefersDedicatedAllocation = false;
18780  allocator->GetImageMemoryRequirements(image, vkMemReq,
18781  requiresDedicatedAllocation, prefersDedicatedAllocation);
18782 
18783  VkResult result = allocator->AllocateMemory(
18784  vkMemReq,
18785  requiresDedicatedAllocation,
18786  prefersDedicatedAllocation,
18787  VK_NULL_HANDLE, // dedicatedBuffer
18788  UINT32_MAX, // dedicatedBufferUsage
18789  image, // dedicatedImage
18790  *pCreateInfo,
18791  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18792  1, // allocationCount
18793  pAllocation);
18794 
18795 #if VMA_RECORDING_ENABLED
18796  if(allocator->GetRecorder() != VMA_NULL)
18797  {
18798  allocator->GetRecorder()->RecordAllocateMemoryForImage(
18799  allocator->GetCurrentFrameIndex(),
18800  vkMemReq,
18801  requiresDedicatedAllocation,
18802  prefersDedicatedAllocation,
18803  *pCreateInfo,
18804  *pAllocation);
18805  }
18806 #endif
18807 
18808  if(pAllocationInfo && result == VK_SUCCESS)
18809  {
18810  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18811  }
18812 
18813  return result;
18814 }
18815 
18816 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18817  VmaAllocator allocator,
18818  VmaAllocation allocation)
18819 {
18820  VMA_ASSERT(allocator);
18821 
18822  if(allocation == VK_NULL_HANDLE)
18823  {
18824  return;
18825  }
18826 
18827  VMA_DEBUG_LOG("vmaFreeMemory");
18828 
18829  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18830 
18831 #if VMA_RECORDING_ENABLED
18832  if(allocator->GetRecorder() != VMA_NULL)
18833  {
18834  allocator->GetRecorder()->RecordFreeMemory(
18835  allocator->GetCurrentFrameIndex(),
18836  allocation);
18837  }
18838 #endif
18839 
18840  allocator->FreeMemory(
18841  1, // allocationCount
18842  &allocation);
18843 }
18844 
18845 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18846  VmaAllocator allocator,
18847  size_t allocationCount,
18848  const VmaAllocation* pAllocations)
18849 {
18850  if(allocationCount == 0)
18851  {
18852  return;
18853  }
18854 
18855  VMA_ASSERT(allocator);
18856 
18857  VMA_DEBUG_LOG("vmaFreeMemoryPages");
18858 
18859  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18860 
18861 #if VMA_RECORDING_ENABLED
18862  if(allocator->GetRecorder() != VMA_NULL)
18863  {
18864  allocator->GetRecorder()->RecordFreeMemoryPages(
18865  allocator->GetCurrentFrameIndex(),
18866  (uint64_t)allocationCount,
18867  pAllocations);
18868  }
18869 #endif
18870 
18871  allocator->FreeMemory(allocationCount, pAllocations);
18872 }
18873 
18874 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18875  VmaAllocator allocator,
18876  VmaAllocation allocation,
18877  VmaAllocationInfo* pAllocationInfo)
18878 {
18879  VMA_ASSERT(allocator && allocation && pAllocationInfo);
18880 
18881  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18882 
18883 #if VMA_RECORDING_ENABLED
18884  if(allocator->GetRecorder() != VMA_NULL)
18885  {
18886  allocator->GetRecorder()->RecordGetAllocationInfo(
18887  allocator->GetCurrentFrameIndex(),
18888  allocation);
18889  }
18890 #endif
18891 
18892  allocator->GetAllocationInfo(allocation, pAllocationInfo);
18893 }
18894 
18895 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18896  VmaAllocator allocator,
18897  VmaAllocation allocation)
18898 {
18899  VMA_ASSERT(allocator && allocation);
18900 
18901  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18902 
18903 #if VMA_RECORDING_ENABLED
18904  if(allocator->GetRecorder() != VMA_NULL)
18905  {
18906  allocator->GetRecorder()->RecordTouchAllocation(
18907  allocator->GetCurrentFrameIndex(),
18908  allocation);
18909  }
18910 #endif
18911 
18912  return allocator->TouchAllocation(allocation);
18913 }
18914 
18915 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18916  VmaAllocator allocator,
18917  VmaAllocation allocation,
18918  void* pUserData)
18919 {
18920  VMA_ASSERT(allocator && allocation);
18921 
18922  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18923 
18924  allocation->SetUserData(allocator, pUserData);
18925 
18926 #if VMA_RECORDING_ENABLED
18927  if(allocator->GetRecorder() != VMA_NULL)
18928  {
18929  allocator->GetRecorder()->RecordSetAllocationUserData(
18930  allocator->GetCurrentFrameIndex(),
18931  allocation,
18932  pUserData);
18933  }
18934 #endif
18935 }
18936 
18937 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18938  VmaAllocator allocator,
18939  VmaAllocation* pAllocation)
18940 {
18941  VMA_ASSERT(allocator && pAllocation);
18942 
18943  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18944 
18945  allocator->CreateLostAllocation(pAllocation);
18946 
18947 #if VMA_RECORDING_ENABLED
18948  if(allocator->GetRecorder() != VMA_NULL)
18949  {
18950  allocator->GetRecorder()->RecordCreateLostAllocation(
18951  allocator->GetCurrentFrameIndex(),
18952  *pAllocation);
18953  }
18954 #endif
18955 }
18956 
18957 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18958  VmaAllocator allocator,
18959  VmaAllocation allocation,
18960  void** ppData)
18961 {
18962  VMA_ASSERT(allocator && allocation && ppData);
18963 
18964  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18965 
18966  VkResult res = allocator->Map(allocation, ppData);
18967 
18968 #if VMA_RECORDING_ENABLED
18969  if(allocator->GetRecorder() != VMA_NULL)
18970  {
18971  allocator->GetRecorder()->RecordMapMemory(
18972  allocator->GetCurrentFrameIndex(),
18973  allocation);
18974  }
18975 #endif
18976 
18977  return res;
18978 }
18979 
18980 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18981  VmaAllocator allocator,
18982  VmaAllocation allocation)
18983 {
18984  VMA_ASSERT(allocator && allocation);
18985 
18986  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18987 
18988 #if VMA_RECORDING_ENABLED
18989  if(allocator->GetRecorder() != VMA_NULL)
18990  {
18991  allocator->GetRecorder()->RecordUnmapMemory(
18992  allocator->GetCurrentFrameIndex(),
18993  allocation);
18994  }
18995 #endif
18996 
18997  allocator->Unmap(allocation);
18998 }
18999 
19000 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
19001 {
19002  VMA_ASSERT(allocator && allocation);
19003 
19004  VMA_DEBUG_LOG("vmaFlushAllocation");
19005 
19006  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19007 
19008  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
19009 
19010 #if VMA_RECORDING_ENABLED
19011  if(allocator->GetRecorder() != VMA_NULL)
19012  {
19013  allocator->GetRecorder()->RecordFlushAllocation(
19014  allocator->GetCurrentFrameIndex(),
19015  allocation, offset, size);
19016  }
19017 #endif
19018 
19019  return res;
19020 }
19021 
19022 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
19023 {
19024  VMA_ASSERT(allocator && allocation);
19025 
19026  VMA_DEBUG_LOG("vmaInvalidateAllocation");
19027 
19028  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19029 
19030  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
19031 
19032 #if VMA_RECORDING_ENABLED
19033  if(allocator->GetRecorder() != VMA_NULL)
19034  {
19035  allocator->GetRecorder()->RecordInvalidateAllocation(
19036  allocator->GetCurrentFrameIndex(),
19037  allocation, offset, size);
19038  }
19039 #endif
19040 
19041  return res;
19042 }
19043 
19044 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
19045  VmaAllocator allocator,
19046  uint32_t allocationCount,
19047  const VmaAllocation* allocations,
19048  const VkDeviceSize* offsets,
19049  const VkDeviceSize* sizes)
19050 {
19051  VMA_ASSERT(allocator);
19052 
19053  if(allocationCount == 0)
19054  {
19055  return VK_SUCCESS;
19056  }
19057 
19058  VMA_ASSERT(allocations);
19059 
19060  VMA_DEBUG_LOG("vmaFlushAllocations");
19061 
19062  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19063 
19064  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
19065 
19066 #if VMA_RECORDING_ENABLED
19067  if(allocator->GetRecorder() != VMA_NULL)
19068  {
19069  //TODO
19070  }
19071 #endif
19072 
19073  return res;
19074 }
19075 
19076 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19077  VmaAllocator allocator,
19078  uint32_t allocationCount,
19079  const VmaAllocation* allocations,
19080  const VkDeviceSize* offsets,
19081  const VkDeviceSize* sizes)
19082 {
19083  VMA_ASSERT(allocator);
19084 
19085  if(allocationCount == 0)
19086  {
19087  return VK_SUCCESS;
19088  }
19089 
19090  VMA_ASSERT(allocations);
19091 
19092  VMA_DEBUG_LOG("vmaInvalidateAllocations");
19093 
19094  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19095 
19096  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19097 
19098 #if VMA_RECORDING_ENABLED
19099  if(allocator->GetRecorder() != VMA_NULL)
19100  {
19101  //TODO
19102  }
19103 #endif
19104 
19105  return res;
19106 }
19107 
19108 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19109 {
19110  VMA_ASSERT(allocator);
19111 
19112  VMA_DEBUG_LOG("vmaCheckCorruption");
19113 
19114  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19115 
19116  return allocator->CheckCorruption(memoryTypeBits);
19117 }
19118 
19119 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19120  VmaAllocator allocator,
19121  const VmaAllocation* pAllocations,
19122  size_t allocationCount,
19123  VkBool32* pAllocationsChanged,
19124  const VmaDefragmentationInfo *pDefragmentationInfo,
19125  VmaDefragmentationStats* pDefragmentationStats)
19126 {
19127  // Deprecated interface, reimplemented using new one.
19128 
19129  VmaDefragmentationInfo2 info2 = {};
19130  info2.allocationCount = (uint32_t)allocationCount;
19131  info2.pAllocations = pAllocations;
19132  info2.pAllocationsChanged = pAllocationsChanged;
19133  if(pDefragmentationInfo != VMA_NULL)
19134  {
19135  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19136  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19137  }
19138  else
19139  {
19140  info2.maxCpuAllocationsToMove = UINT32_MAX;
19141  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19142  }
19143  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19144 
19146  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19147  if(res == VK_NOT_READY)
19148  {
19149  res = vmaDefragmentationEnd( allocator, ctx);
19150  }
19151  return res;
19152 }
19153 
19154 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19155  VmaAllocator allocator,
19156  const VmaDefragmentationInfo2* pInfo,
19157  VmaDefragmentationStats* pStats,
19158  VmaDefragmentationContext *pContext)
19159 {
19160  VMA_ASSERT(allocator && pInfo && pContext);
19161 
19162  // Degenerate case: Nothing to defragment.
19163  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19164  {
19165  return VK_SUCCESS;
19166  }
19167 
19168  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19169  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19170  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19171  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19172 
19173  VMA_DEBUG_LOG("vmaDefragmentationBegin");
19174 
19175  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19176 
19177  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19178 
19179 #if VMA_RECORDING_ENABLED
19180  if(allocator->GetRecorder() != VMA_NULL)
19181  {
19182  allocator->GetRecorder()->RecordDefragmentationBegin(
19183  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19184  }
19185 #endif
19186 
19187  return res;
19188 }
19189 
19190 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19191  VmaAllocator allocator,
19192  VmaDefragmentationContext context)
19193 {
19194  VMA_ASSERT(allocator);
19195 
19196  VMA_DEBUG_LOG("vmaDefragmentationEnd");
19197 
19198  if(context != VK_NULL_HANDLE)
19199  {
19200  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19201 
19202 #if VMA_RECORDING_ENABLED
19203  if(allocator->GetRecorder() != VMA_NULL)
19204  {
19205  allocator->GetRecorder()->RecordDefragmentationEnd(
19206  allocator->GetCurrentFrameIndex(), context);
19207  }
19208 #endif
19209 
19210  return allocator->DefragmentationEnd(context);
19211  }
19212  else
19213  {
19214  return VK_SUCCESS;
19215  }
19216 }
19217 
19218 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19219  VmaAllocator allocator,
19220  VmaDefragmentationContext context,
19222  )
19223 {
19224  VMA_ASSERT(allocator);
19225  VMA_ASSERT(pInfo);
19226 
19227  VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19228 
19229  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19230 
19231  if(context == VK_NULL_HANDLE)
19232  {
19233  pInfo->moveCount = 0;
19234  return VK_SUCCESS;
19235  }
19236 
19237  return allocator->DefragmentationPassBegin(pInfo, context);
19238 }
19239 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19240  VmaAllocator allocator,
19241  VmaDefragmentationContext context)
19242 {
19243  VMA_ASSERT(allocator);
19244 
19245  VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19246  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19247 
19248  if(context == VK_NULL_HANDLE)
19249  return VK_SUCCESS;
19250 
19251  return allocator->DefragmentationPassEnd(context);
19252 }
19253 
19254 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19255  VmaAllocator allocator,
19256  VmaAllocation allocation,
19257  VkBuffer buffer)
19258 {
19259  VMA_ASSERT(allocator && allocation && buffer);
19260 
19261  VMA_DEBUG_LOG("vmaBindBufferMemory");
19262 
19263  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19264 
19265  return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19266 }
19267 
19268 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19269  VmaAllocator allocator,
19270  VmaAllocation allocation,
19271  VkDeviceSize allocationLocalOffset,
19272  VkBuffer buffer,
19273  const void* pNext)
19274 {
19275  VMA_ASSERT(allocator && allocation && buffer);
19276 
19277  VMA_DEBUG_LOG("vmaBindBufferMemory2");
19278 
19279  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19280 
19281  return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19282 }
19283 
19284 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19285  VmaAllocator allocator,
19286  VmaAllocation allocation,
19287  VkImage image)
19288 {
19289  VMA_ASSERT(allocator && allocation && image);
19290 
19291  VMA_DEBUG_LOG("vmaBindImageMemory");
19292 
19293  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19294 
19295  return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19296 }
19297 
19298 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19299  VmaAllocator allocator,
19300  VmaAllocation allocation,
19301  VkDeviceSize allocationLocalOffset,
19302  VkImage image,
19303  const void* pNext)
19304 {
19305  VMA_ASSERT(allocator && allocation && image);
19306 
19307  VMA_DEBUG_LOG("vmaBindImageMemory2");
19308 
19309  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19310 
19311  return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19312 }
19313 
19314 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19315  VmaAllocator allocator,
19316  const VkBufferCreateInfo* pBufferCreateInfo,
19317  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19318  VkBuffer* pBuffer,
19319  VmaAllocation* pAllocation,
19320  VmaAllocationInfo* pAllocationInfo)
19321 {
19322  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19323 
19324  if(pBufferCreateInfo->size == 0)
19325  {
19326  return VK_ERROR_VALIDATION_FAILED_EXT;
19327  }
19328  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19329  !allocator->m_UseKhrBufferDeviceAddress)
19330  {
19331  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.");
19332  return VK_ERROR_VALIDATION_FAILED_EXT;
19333  }
19334 
19335  VMA_DEBUG_LOG("vmaCreateBuffer");
19336 
19337  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19338 
19339  *pBuffer = VK_NULL_HANDLE;
19340  *pAllocation = VK_NULL_HANDLE;
19341 
19342  // 1. Create VkBuffer.
19343  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19344  allocator->m_hDevice,
19345  pBufferCreateInfo,
19346  allocator->GetAllocationCallbacks(),
19347  pBuffer);
19348  if(res >= 0)
19349  {
19350  // 2. vkGetBufferMemoryRequirements.
19351  VkMemoryRequirements vkMemReq = {};
19352  bool requiresDedicatedAllocation = false;
19353  bool prefersDedicatedAllocation = false;
19354  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19355  requiresDedicatedAllocation, prefersDedicatedAllocation);
19356 
19357  // 3. Allocate memory using allocator.
19358  res = allocator->AllocateMemory(
19359  vkMemReq,
19360  requiresDedicatedAllocation,
19361  prefersDedicatedAllocation,
19362  *pBuffer, // dedicatedBuffer
19363  pBufferCreateInfo->usage, // dedicatedBufferUsage
19364  VK_NULL_HANDLE, // dedicatedImage
19365  *pAllocationCreateInfo,
19366  VMA_SUBALLOCATION_TYPE_BUFFER,
19367  1, // allocationCount
19368  pAllocation);
19369 
19370 #if VMA_RECORDING_ENABLED
19371  if(allocator->GetRecorder() != VMA_NULL)
19372  {
19373  allocator->GetRecorder()->RecordCreateBuffer(
19374  allocator->GetCurrentFrameIndex(),
19375  *pBufferCreateInfo,
19376  *pAllocationCreateInfo,
19377  *pAllocation);
19378  }
19379 #endif
19380 
19381  if(res >= 0)
19382  {
19383  // 3. Bind buffer with memory.
19384  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19385  {
19386  res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19387  }
19388  if(res >= 0)
19389  {
19390  // All steps succeeded.
19391  #if VMA_STATS_STRING_ENABLED
19392  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19393  #endif
19394  if(pAllocationInfo != VMA_NULL)
19395  {
19396  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19397  }
19398 
19399  return VK_SUCCESS;
19400  }
19401  allocator->FreeMemory(
19402  1, // allocationCount
19403  pAllocation);
19404  *pAllocation = VK_NULL_HANDLE;
19405  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19406  *pBuffer = VK_NULL_HANDLE;
19407  return res;
19408  }
19409  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19410  *pBuffer = VK_NULL_HANDLE;
19411  return res;
19412  }
19413  return res;
19414 }
19415 
19416 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19417  VmaAllocator allocator,
19418  VkBuffer buffer,
19419  VmaAllocation allocation)
19420 {
19421  VMA_ASSERT(allocator);
19422 
19423  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19424  {
19425  return;
19426  }
19427 
19428  VMA_DEBUG_LOG("vmaDestroyBuffer");
19429 
19430  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19431 
19432 #if VMA_RECORDING_ENABLED
19433  if(allocator->GetRecorder() != VMA_NULL)
19434  {
19435  allocator->GetRecorder()->RecordDestroyBuffer(
19436  allocator->GetCurrentFrameIndex(),
19437  allocation);
19438  }
19439 #endif
19440 
19441  if(buffer != VK_NULL_HANDLE)
19442  {
19443  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19444  }
19445 
19446  if(allocation != VK_NULL_HANDLE)
19447  {
19448  allocator->FreeMemory(
19449  1, // allocationCount
19450  &allocation);
19451  }
19452 }
19453 
19454 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19455  VmaAllocator allocator,
19456  const VkImageCreateInfo* pImageCreateInfo,
19457  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19458  VkImage* pImage,
19459  VmaAllocation* pAllocation,
19460  VmaAllocationInfo* pAllocationInfo)
19461 {
19462  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19463 
19464  if(pImageCreateInfo->extent.width == 0 ||
19465  pImageCreateInfo->extent.height == 0 ||
19466  pImageCreateInfo->extent.depth == 0 ||
19467  pImageCreateInfo->mipLevels == 0 ||
19468  pImageCreateInfo->arrayLayers == 0)
19469  {
19470  return VK_ERROR_VALIDATION_FAILED_EXT;
19471  }
19472 
19473  VMA_DEBUG_LOG("vmaCreateImage");
19474 
19475  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19476 
19477  *pImage = VK_NULL_HANDLE;
19478  *pAllocation = VK_NULL_HANDLE;
19479 
19480  // 1. Create VkImage.
19481  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19482  allocator->m_hDevice,
19483  pImageCreateInfo,
19484  allocator->GetAllocationCallbacks(),
19485  pImage);
19486  if(res >= 0)
19487  {
19488  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19489  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19490  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19491 
19492  // 2. Allocate memory using allocator.
19493  VkMemoryRequirements vkMemReq = {};
19494  bool requiresDedicatedAllocation = false;
19495  bool prefersDedicatedAllocation = false;
19496  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19497  requiresDedicatedAllocation, prefersDedicatedAllocation);
19498 
19499  res = allocator->AllocateMemory(
19500  vkMemReq,
19501  requiresDedicatedAllocation,
19502  prefersDedicatedAllocation,
19503  VK_NULL_HANDLE, // dedicatedBuffer
19504  UINT32_MAX, // dedicatedBufferUsage
19505  *pImage, // dedicatedImage
19506  *pAllocationCreateInfo,
19507  suballocType,
19508  1, // allocationCount
19509  pAllocation);
19510 
19511 #if VMA_RECORDING_ENABLED
19512  if(allocator->GetRecorder() != VMA_NULL)
19513  {
19514  allocator->GetRecorder()->RecordCreateImage(
19515  allocator->GetCurrentFrameIndex(),
19516  *pImageCreateInfo,
19517  *pAllocationCreateInfo,
19518  *pAllocation);
19519  }
19520 #endif
19521 
19522  if(res >= 0)
19523  {
19524  // 3. Bind image with memory.
19525  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19526  {
19527  res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19528  }
19529  if(res >= 0)
19530  {
19531  // All steps succeeded.
19532  #if VMA_STATS_STRING_ENABLED
19533  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19534  #endif
19535  if(pAllocationInfo != VMA_NULL)
19536  {
19537  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19538  }
19539 
19540  return VK_SUCCESS;
19541  }
19542  allocator->FreeMemory(
19543  1, // allocationCount
19544  pAllocation);
19545  *pAllocation = VK_NULL_HANDLE;
19546  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19547  *pImage = VK_NULL_HANDLE;
19548  return res;
19549  }
19550  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19551  *pImage = VK_NULL_HANDLE;
19552  return res;
19553  }
19554  return res;
19555 }
19556 
19557 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19558  VmaAllocator allocator,
19559  VkImage image,
19560  VmaAllocation allocation)
19561 {
19562  VMA_ASSERT(allocator);
19563 
19564  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19565  {
19566  return;
19567  }
19568 
19569  VMA_DEBUG_LOG("vmaDestroyImage");
19570 
19571  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19572 
19573 #if VMA_RECORDING_ENABLED
19574  if(allocator->GetRecorder() != VMA_NULL)
19575  {
19576  allocator->GetRecorder()->RecordDestroyImage(
19577  allocator->GetCurrentFrameIndex(),
19578  allocation);
19579  }
19580 #endif
19581 
19582  if(image != VK_NULL_HANDLE)
19583  {
19584  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19585  }
19586  if(allocation != VK_NULL_HANDLE)
19587  {
19588  allocator->FreeMemory(
19589  1, // allocationCount
19590  &allocation);
19591  }
19592 }
19593 
19594 #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:3236
VkDeviceSize offset
Offset in VkDeviceMemory object to the beginning of this allocation, in bytes. (deviceMemory,...
Definition: vk_mem_alloc.h:3260
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:3280
uint32_t memoryType
Memory type index that this allocation was allocated from.
Definition: vk_mem_alloc.h:3241
VkDeviceSize size
Size of this allocation, in bytes.
Definition: vk_mem_alloc.h:3271
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:3285
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:3250
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:3635
const VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:3675
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:3641
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:3695
VkDeviceSize maxGpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3690
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:3638
VkBool32 * pAllocationsChanged
Optional, output. Pointer to array that will be filled with information whether the allocation at cer...
Definition: vk_mem_alloc.h:3656
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:3659
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:3704
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:3685
const VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:3650
VkDeviceSize maxCpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3680
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:3726
uint32_t maxAllocationsToMove
Maximum number of allocations that can be moved to different place.
Definition: vk_mem_alloc.h:3736
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3731
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:3717
uint32_t moveCount
Definition: vk_mem_alloc.h:3718
VmaDefragmentationPassMoveInfo * pMoves
Definition: vk_mem_alloc.h:3719
Definition: vk_mem_alloc.h:3707
VkDeviceMemory memory
Definition: vk_mem_alloc.h:3709
VkDeviceSize offset
Definition: vk_mem_alloc.h:3710
VmaAllocation allocation
Definition: vk_mem_alloc.h:3708
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:3740
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:3748
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3742
VkDeviceSize bytesFreed
Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects.
Definition: vk_mem_alloc.h:3744
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:3746
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
VkDeviceSize minAllocationAlignment
Additional minimum alignment to be used for all allocations created from this pool....
Definition: vk_mem_alloc.h:3102
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:3107
VkDeviceSize size
Total amount of VkDeviceMemory allocated from Vulkan for this pool, in bytes.
Definition: vk_mem_alloc.h:3110
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:3129
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:3126
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost.
Definition: vk_mem_alloc.h:3116
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3113
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3119
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:3625
@ VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
Definition: vk_mem_alloc.h:3626
@ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3627
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:3629
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.