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 
2022 #ifdef __cplusplus
2023 extern "C" {
2024 #endif
2025 
2026 /*
2027 Define this macro to 0/1 to disable/enable support for recording functionality,
2028 available through VmaAllocatorCreateInfo::pRecordSettings.
2029 */
2030 #ifndef VMA_RECORDING_ENABLED
2031  #define VMA_RECORDING_ENABLED 0
2032 #endif
2033 
2034 #if !defined(NOMINMAX) && defined(VMA_IMPLEMENTATION)
2035  #define NOMINMAX // For windows.h
2036 #endif
2037 
2038 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
2039  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
2040  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
2041  extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
2042  extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
2043  extern PFN_vkAllocateMemory vkAllocateMemory;
2044  extern PFN_vkFreeMemory vkFreeMemory;
2045  extern PFN_vkMapMemory vkMapMemory;
2046  extern PFN_vkUnmapMemory vkUnmapMemory;
2047  extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
2048  extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
2049  extern PFN_vkBindBufferMemory vkBindBufferMemory;
2050  extern PFN_vkBindImageMemory vkBindImageMemory;
2051  extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
2052  extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
2053  extern PFN_vkCreateBuffer vkCreateBuffer;
2054  extern PFN_vkDestroyBuffer vkDestroyBuffer;
2055  extern PFN_vkCreateImage vkCreateImage;
2056  extern PFN_vkDestroyImage vkDestroyImage;
2057  extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
2058  #if VMA_VULKAN_VERSION >= 1001000
2059  extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
2060  extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
2061  extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
2062  extern PFN_vkBindImageMemory2 vkBindImageMemory2;
2063  extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
2064  #endif // #if VMA_VULKAN_VERSION >= 1001000
2065 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
2066 
2067 #ifndef VULKAN_H_
2068  #include <vulkan/vulkan.h>
2069 #endif
2070 
2071 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
2072 // where AAA = major, BBB = minor, CCC = patch.
2073 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
2074 #if !defined(VMA_VULKAN_VERSION)
2075  #if defined(VK_VERSION_1_2)
2076  #define VMA_VULKAN_VERSION 1002000
2077  #elif defined(VK_VERSION_1_1)
2078  #define VMA_VULKAN_VERSION 1001000
2079  #else
2080  #define VMA_VULKAN_VERSION 1000000
2081  #endif
2082 #endif
2083 
2084 #if !defined(VMA_DEDICATED_ALLOCATION)
2085  #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
2086  #define VMA_DEDICATED_ALLOCATION 1
2087  #else
2088  #define VMA_DEDICATED_ALLOCATION 0
2089  #endif
2090 #endif
2091 
2092 #if !defined(VMA_BIND_MEMORY2)
2093  #if VK_KHR_bind_memory2
2094  #define VMA_BIND_MEMORY2 1
2095  #else
2096  #define VMA_BIND_MEMORY2 0
2097  #endif
2098 #endif
2099 
2100 #if !defined(VMA_MEMORY_BUDGET)
2101  #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
2102  #define VMA_MEMORY_BUDGET 1
2103  #else
2104  #define VMA_MEMORY_BUDGET 0
2105  #endif
2106 #endif
2107 
2108 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
2109 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
2110  #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
2111  #define VMA_BUFFER_DEVICE_ADDRESS 1
2112  #else
2113  #define VMA_BUFFER_DEVICE_ADDRESS 0
2114  #endif
2115 #endif
2116 
2117 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
2118 #if !defined(VMA_MEMORY_PRIORITY)
2119  #if VK_EXT_memory_priority
2120  #define VMA_MEMORY_PRIORITY 1
2121  #else
2122  #define VMA_MEMORY_PRIORITY 0
2123  #endif
2124 #endif
2125 
2126 // Define these macros to decorate all public functions with additional code,
2127 // before and after returned type, appropriately. This may be useful for
2128 // exporting the functions when compiling VMA as a separate library. Example:
2129 // #define VMA_CALL_PRE __declspec(dllexport)
2130 // #define VMA_CALL_POST __cdecl
2131 #ifndef VMA_CALL_PRE
2132  #define VMA_CALL_PRE
2133 #endif
2134 #ifndef VMA_CALL_POST
2135  #define VMA_CALL_POST
2136 #endif
2137 
2138 // Define this macro to decorate pointers with an attribute specifying the
2139 // length of the array they point to if they are not null.
2140 //
2141 // The length may be one of
2142 // - The name of another parameter in the argument list where the pointer is declared
2143 // - The name of another member in the struct where the pointer is declared
2144 // - The name of a member of a struct type, meaning the value of that member in
2145 // the context of the call. For example
2146 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2147 // this means the number of memory heaps available in the device associated
2148 // with the VmaAllocator being dealt with.
2149 #ifndef VMA_LEN_IF_NOT_NULL
2150  #define VMA_LEN_IF_NOT_NULL(len)
2151 #endif
2152 
2153 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2154 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2155 #ifndef VMA_NULLABLE
2156  #ifdef __clang__
2157  #define VMA_NULLABLE _Nullable
2158  #else
2159  #define VMA_NULLABLE
2160  #endif
2161 #endif
2162 
2163 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2164 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2165 #ifndef VMA_NOT_NULL
2166  #ifdef __clang__
2167  #define VMA_NOT_NULL _Nonnull
2168  #else
2169  #define VMA_NOT_NULL
2170  #endif
2171 #endif
2172 
2173 // If non-dispatchable handles are represented as pointers then we can give
2174 // then nullability annotations
2175 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2176  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2177  #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2178  #else
2179  #define VMA_NOT_NULL_NON_DISPATCHABLE
2180  #endif
2181 #endif
2182 
2183 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2184  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2185  #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2186  #else
2187  #define VMA_NULLABLE_NON_DISPATCHABLE
2188  #endif
2189 #endif
2190 
2200 VK_DEFINE_HANDLE(VmaAllocator)
2201 
2202 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2204  VmaAllocator VMA_NOT_NULL allocator,
2205  uint32_t memoryType,
2206  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2207  VkDeviceSize size,
2208  void* VMA_NULLABLE pUserData);
2210 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2211  VmaAllocator VMA_NOT_NULL allocator,
2212  uint32_t memoryType,
2213  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2214  VkDeviceSize size,
2215  void* VMA_NULLABLE pUserData);
2216 
2230  void* VMA_NULLABLE pUserData;
2232 
2345 
2348 typedef VkFlags VmaAllocatorCreateFlags;
2349 
2354 typedef struct VmaVulkanFunctions {
2355  PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2356  PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2357  PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2358  PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2359  PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2360  PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2361  PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2362  PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2363  PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2364  PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2365  PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2366  PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2367  PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2368  PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2369  PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2370  PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2371  PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2372 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2373  PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2374  PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2375 #endif
2376 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2377  PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2378  PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2379 #endif
2380 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2381  PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2382 #endif
2384 
2386 typedef enum VmaRecordFlagBits {
2393 
2394  VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2396 typedef VkFlags VmaRecordFlags;
2397 
2399 typedef struct VmaRecordSettings
2400 {
2410  const char* VMA_NOT_NULL pFilePath;
2412 
2415 {
2419 
2420  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2422 
2423  VkDevice VMA_NOT_NULL device;
2425 
2428 
2429  const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2431 
2471  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2472 
2484  const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2489  VkInstance VMA_NOT_NULL instance;
2500 
2502 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2503  const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2504  VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2505 
2507 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2508  VmaAllocator VMA_NULLABLE allocator);
2509 
2512 typedef struct VmaAllocatorInfo
2513 {
2518  VkInstance VMA_NOT_NULL instance;
2523  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2528  VkDevice VMA_NOT_NULL device;
2530 
2536 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2537 
2542 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2543  VmaAllocator VMA_NOT_NULL allocator,
2544  const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2545 
2550 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2551  VmaAllocator VMA_NOT_NULL allocator,
2552  const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2553 
2560 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2561  VmaAllocator VMA_NOT_NULL allocator,
2562  uint32_t memoryTypeIndex,
2563  VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2564 
2573 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2574  VmaAllocator VMA_NOT_NULL allocator,
2575  uint32_t frameIndex);
2576 
2579 typedef struct VmaStatInfo
2580 {
2582  uint32_t blockCount;
2588  VkDeviceSize usedBytes;
2590  VkDeviceSize unusedBytes;
2591  VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2592  VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2594 
2596 typedef struct VmaStats
2597 {
2598  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2599  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2602 
2612 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2613  VmaAllocator VMA_NOT_NULL allocator,
2614  VmaStats* VMA_NOT_NULL pStats);
2615 
2618 typedef struct VmaBudget
2619 {
2622  VkDeviceSize blockBytes;
2623 
2633  VkDeviceSize allocationBytes;
2634 
2643  VkDeviceSize usage;
2644 
2654  VkDeviceSize budget;
2656 
2667 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2668  VmaAllocator VMA_NOT_NULL allocator,
2669  VmaBudget* VMA_NOT_NULL pBudget);
2670 
2671 #ifndef VMA_STATS_STRING_ENABLED
2672 #define VMA_STATS_STRING_ENABLED 1
2673 #endif
2674 
2675 #if VMA_STATS_STRING_ENABLED
2676 
2678 
2680 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2681  VmaAllocator VMA_NOT_NULL allocator,
2682  char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2683  VkBool32 detailedMap);
2684 
2685 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2686  VmaAllocator VMA_NOT_NULL allocator,
2687  char* VMA_NULLABLE pStatsString);
2688 
2689 #endif // #if VMA_STATS_STRING_ENABLED
2690 
2699 VK_DEFINE_HANDLE(VmaPool)
2700 
2701 typedef enum VmaMemoryUsage
2702 {
2764 
2765  VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2767 
2777 
2842 
2858 
2868 
2875 
2879 
2881 {
2894  VkMemoryPropertyFlags requiredFlags;
2899  VkMemoryPropertyFlags preferredFlags;
2907  uint32_t memoryTypeBits;
2913  VmaPool VMA_NULLABLE pool;
2920  void* VMA_NULLABLE pUserData;
2927  float priority;
2929 
2946 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2947  VmaAllocator VMA_NOT_NULL allocator,
2948  uint32_t memoryTypeBits,
2949  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2950  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2951 
2964 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2965  VmaAllocator VMA_NOT_NULL allocator,
2966  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2967  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2968  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2969 
2982 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2983  VmaAllocator VMA_NOT_NULL allocator,
2984  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2985  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2986  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2987 
3008 
3025 
3036 
3042 
3045 typedef VkFlags VmaPoolCreateFlags;
3046 
3049 typedef struct VmaPoolCreateInfo {
3064  VkDeviceSize blockSize;
3097  float priority;
3099 
3102 typedef struct VmaPoolStats {
3105  VkDeviceSize size;
3108  VkDeviceSize unusedSize;
3121  VkDeviceSize unusedRangeSizeMax;
3124  size_t blockCount;
3126 
3133 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
3134  VmaAllocator VMA_NOT_NULL allocator,
3135  const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
3136  VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
3137 
3140 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
3141  VmaAllocator VMA_NOT_NULL allocator,
3142  VmaPool VMA_NULLABLE pool);
3143 
3150 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3151  VmaAllocator VMA_NOT_NULL allocator,
3152  VmaPool VMA_NOT_NULL pool,
3153  VmaPoolStats* VMA_NOT_NULL pPoolStats);
3154 
3161 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3162  VmaAllocator VMA_NOT_NULL allocator,
3163  VmaPool VMA_NOT_NULL pool,
3164  size_t* VMA_NULLABLE pLostAllocationCount);
3165 
3180 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3181 
3188 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3189  VmaAllocator VMA_NOT_NULL allocator,
3190  VmaPool VMA_NOT_NULL pool,
3191  const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3192 
3198 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3199  VmaAllocator VMA_NOT_NULL allocator,
3200  VmaPool VMA_NOT_NULL pool,
3201  const char* VMA_NULLABLE pName);
3202 
3227 VK_DEFINE_HANDLE(VmaAllocation)
3228 
3229 
3231 typedef struct VmaAllocationInfo {
3236  uint32_t memoryType;
3245  VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3255  VkDeviceSize offset;
3266  VkDeviceSize size;
3275  void* VMA_NULLABLE pMappedData;
3280  void* VMA_NULLABLE pUserData;
3282 
3293 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3294  VmaAllocator VMA_NOT_NULL allocator,
3295  const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3296  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3297  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3298  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3299 
3319 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3320  VmaAllocator VMA_NOT_NULL allocator,
3321  const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3322  const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3323  size_t allocationCount,
3324  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3325  VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3326 
3333 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3334  VmaAllocator VMA_NOT_NULL allocator,
3335  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3336  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3337  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3338  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3339 
3341 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3342  VmaAllocator VMA_NOT_NULL allocator,
3343  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3344  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3345  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3346  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3347 
3352 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3353  VmaAllocator VMA_NOT_NULL allocator,
3354  const VmaAllocation VMA_NULLABLE allocation);
3355 
3366 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3367  VmaAllocator VMA_NOT_NULL allocator,
3368  size_t allocationCount,
3369  const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3370 
3387 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3388  VmaAllocator VMA_NOT_NULL allocator,
3389  VmaAllocation VMA_NOT_NULL allocation,
3390  VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3391 
3406 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3407  VmaAllocator VMA_NOT_NULL allocator,
3408  VmaAllocation VMA_NOT_NULL allocation);
3409 
3423 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3424  VmaAllocator VMA_NOT_NULL allocator,
3425  VmaAllocation VMA_NOT_NULL allocation,
3426  void* VMA_NULLABLE pUserData);
3427 
3438 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3439  VmaAllocator VMA_NOT_NULL allocator,
3440  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3441 
3480 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3481  VmaAllocator VMA_NOT_NULL allocator,
3482  VmaAllocation VMA_NOT_NULL allocation,
3483  void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3484 
3493 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3494  VmaAllocator VMA_NOT_NULL allocator,
3495  VmaAllocation VMA_NOT_NULL allocation);
3496 
3518 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3519  VmaAllocator VMA_NOT_NULL allocator,
3520  VmaAllocation VMA_NOT_NULL allocation,
3521  VkDeviceSize offset,
3522  VkDeviceSize size);
3523 
3545 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3546  VmaAllocator VMA_NOT_NULL allocator,
3547  VmaAllocation VMA_NOT_NULL allocation,
3548  VkDeviceSize offset,
3549  VkDeviceSize size);
3550 
3565 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3566  VmaAllocator VMA_NOT_NULL allocator,
3567  uint32_t allocationCount,
3568  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3569  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3570  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3571 
3586 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3587  VmaAllocator VMA_NOT_NULL allocator,
3588  uint32_t allocationCount,
3589  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3590  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3591  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3592 
3609 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3610 
3617 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3618 
3619 typedef enum VmaDefragmentationFlagBits {
3624 typedef VkFlags VmaDefragmentationFlags;
3625 
3630 typedef struct VmaDefragmentationInfo2 {
3645  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3651  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3654  uint32_t poolCount;
3670  const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3675  VkDeviceSize maxCpuBytesToMove;
3685  VkDeviceSize maxGpuBytesToMove;
3699  VkCommandBuffer VMA_NULLABLE commandBuffer;
3701 
3704  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3705  VkDeviceSize offset;
3707 
3713  uint32_t moveCount;
3714  VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3716 
3721 typedef struct VmaDefragmentationInfo {
3726  VkDeviceSize maxBytesToMove;
3733 
3735 typedef struct VmaDefragmentationStats {
3737  VkDeviceSize bytesMoved;
3739  VkDeviceSize bytesFreed;
3745 
3775 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3776  VmaAllocator VMA_NOT_NULL allocator,
3777  const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3778  VmaDefragmentationStats* VMA_NULLABLE pStats,
3779  VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3780 
3786 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3787  VmaAllocator VMA_NOT_NULL allocator,
3788  VmaDefragmentationContext VMA_NULLABLE context);
3789 
3790 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3791  VmaAllocator VMA_NOT_NULL allocator,
3792  VmaDefragmentationContext VMA_NULLABLE context,
3793  VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3794 );
3795 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3796  VmaAllocator VMA_NOT_NULL allocator,
3797  VmaDefragmentationContext VMA_NULLABLE context
3798 );
3799 
3840 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3841  VmaAllocator VMA_NOT_NULL allocator,
3842  const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3843  size_t allocationCount,
3844  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3845  const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3846  VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3847 
3860 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3861  VmaAllocator VMA_NOT_NULL allocator,
3862  VmaAllocation VMA_NOT_NULL allocation,
3863  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3864 
3875 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3876  VmaAllocator VMA_NOT_NULL allocator,
3877  VmaAllocation VMA_NOT_NULL allocation,
3878  VkDeviceSize allocationLocalOffset,
3879  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3880  const void* VMA_NULLABLE pNext);
3881 
3894 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3895  VmaAllocator VMA_NOT_NULL allocator,
3896  VmaAllocation VMA_NOT_NULL allocation,
3897  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3898 
3909 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3910  VmaAllocator VMA_NOT_NULL allocator,
3911  VmaAllocation VMA_NOT_NULL allocation,
3912  VkDeviceSize allocationLocalOffset,
3913  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3914  const void* VMA_NULLABLE pNext);
3915 
3946 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3947  VmaAllocator VMA_NOT_NULL allocator,
3948  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3949  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3950  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3951  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3952  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3953 
3965 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3966  VmaAllocator VMA_NOT_NULL allocator,
3967  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3968  VmaAllocation VMA_NULLABLE allocation);
3969 
3971 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3972  VmaAllocator VMA_NOT_NULL allocator,
3973  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3974  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3975  VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3976  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3977  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3978 
3990 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3991  VmaAllocator VMA_NOT_NULL allocator,
3992  VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3993  VmaAllocation VMA_NULLABLE allocation);
3994 
3995 #ifdef __cplusplus
3996 }
3997 #endif
3998 
3999 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
4000 
4001 // For Visual Studio IntelliSense.
4002 #if defined(__cplusplus) && defined(__INTELLISENSE__)
4003 #define VMA_IMPLEMENTATION
4004 #endif
4005 
4006 #ifdef VMA_IMPLEMENTATION
4007 #undef VMA_IMPLEMENTATION
4008 
4009 #include <cstdint>
4010 #include <cstdlib>
4011 #include <cstring>
4012 #include <utility>
4013 
4014 #if VMA_RECORDING_ENABLED
4015  #include <chrono>
4016  #if defined(_WIN32)
4017  #include <windows.h>
4018  #else
4019  #include <sstream>
4020  #include <thread>
4021  #endif
4022 #endif
4023 
4024 /*******************************************************************************
4025 CONFIGURATION SECTION
4026 
4027 Define some of these macros before each #include of this header or change them
4028 here if you need other then default behavior depending on your environment.
4029 */
4030 
4031 /*
4032 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4033 internally, like:
4034 
4035  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
4036 */
4037 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
4038  #define VMA_STATIC_VULKAN_FUNCTIONS 1
4039 #endif
4040 
4041 /*
4042 Define this macro to 1 to make the library fetch pointers to Vulkan functions
4043 internally, like:
4044 
4045  vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
4046 */
4047 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
4048  #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
4049  #if defined(VK_NO_PROTOTYPES)
4050  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
4051  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
4052  #endif
4053 #endif
4054 
4055 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
4056 //#define VMA_USE_STL_CONTAINERS 1
4057 
4058 /* Set this macro to 1 to make the library including and using STL containers:
4059 std::pair, std::vector, std::list, std::unordered_map.
4060 
4061 Set it to 0 or undefined to make the library using its own implementation of
4062 the containers.
4063 */
4064 #if VMA_USE_STL_CONTAINERS
4065  #define VMA_USE_STL_VECTOR 1
4066  #define VMA_USE_STL_UNORDERED_MAP 1
4067  #define VMA_USE_STL_LIST 1
4068 #endif
4069 
4070 #ifndef VMA_USE_STL_SHARED_MUTEX
4071  // Compiler conforms to C++17.
4072  #if __cplusplus >= 201703L
4073  #define VMA_USE_STL_SHARED_MUTEX 1
4074  // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
4075  // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
4076  // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
4077  #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
4078  #define VMA_USE_STL_SHARED_MUTEX 1
4079  #else
4080  #define VMA_USE_STL_SHARED_MUTEX 0
4081  #endif
4082 #endif
4083 
4084 /*
4085 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
4086 Library has its own container implementation.
4087 */
4088 #if VMA_USE_STL_VECTOR
4089  #include <vector>
4090 #endif
4091 
4092 #if VMA_USE_STL_UNORDERED_MAP
4093  #include <unordered_map>
4094 #endif
4095 
4096 #if VMA_USE_STL_LIST
4097  #include <list>
4098 #endif
4099 
4100 /*
4101 Following headers are used in this CONFIGURATION section only, so feel free to
4102 remove them if not needed.
4103 */
4104 #include <cassert> // for assert
4105 #include <algorithm> // for min, max
4106 #include <mutex>
4107 
4108 #ifndef VMA_NULL
4109  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
4110  #define VMA_NULL nullptr
4111 #endif
4112 
4113 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
4114 #include <cstdlib>
4115 static void* vma_aligned_alloc(size_t alignment, size_t size)
4116 {
4117  // alignment must be >= sizeof(void*)
4118  if(alignment < sizeof(void*))
4119  {
4120  alignment = sizeof(void*);
4121  }
4122 
4123  return memalign(alignment, size);
4124 }
4125 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
4126 #include <cstdlib>
4127 
4128 #if defined(__APPLE__)
4129 #include <AvailabilityMacros.h>
4130 #endif
4131 
4132 static void* vma_aligned_alloc(size_t alignment, size_t size)
4133 {
4134 #if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
4135 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
4136  // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
4137  // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
4138  // MAC_OS_X_VERSION_10_16), even though the function is marked
4139  // availabe for 10.15. That's why the preprocessor checks for 10.16 but
4140  // the __builtin_available checks for 10.15.
4141  // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
4142  if (__builtin_available(macOS 10.15, iOS 13, *))
4143  return aligned_alloc(alignment, size);
4144 #endif
4145 #endif
4146  // alignment must be >= sizeof(void*)
4147  if(alignment < sizeof(void*))
4148  {
4149  alignment = sizeof(void*);
4150  }
4151 
4152  void *pointer;
4153  if(posix_memalign(&pointer, alignment, size) == 0)
4154  return pointer;
4155  return VMA_NULL;
4156 }
4157 #elif defined(_WIN32)
4158 static void* vma_aligned_alloc(size_t alignment, size_t size)
4159 {
4160  return _aligned_malloc(size, alignment);
4161 }
4162 #else
4163 static void* vma_aligned_alloc(size_t alignment, size_t size)
4164 {
4165  return aligned_alloc(alignment, size);
4166 }
4167 #endif
4168 
4169 #if defined(_WIN32)
4170 static void vma_aligned_free(void* ptr)
4171 {
4172  _aligned_free(ptr);
4173 }
4174 #else
4175 static void vma_aligned_free(void* ptr)
4176 {
4177  free(ptr);
4178 }
4179 #endif
4180 
4181 // If your compiler is not compatible with C++11 and definition of
4182 // aligned_alloc() function is missing, uncommeting following line may help:
4183 
4184 //#include <malloc.h>
4185 
4186 // Normal assert to check for programmer's errors, especially in Debug configuration.
4187 #ifndef VMA_ASSERT
4188  #ifdef NDEBUG
4189  #define VMA_ASSERT(expr)
4190  #else
4191  #define VMA_ASSERT(expr) assert(expr)
4192  #endif
4193 #endif
4194 
4195 // Assert that will be called very often, like inside data structures e.g. operator[].
4196 // Making it non-empty can make program slow.
4197 #ifndef VMA_HEAVY_ASSERT
4198  #ifdef NDEBUG
4199  #define VMA_HEAVY_ASSERT(expr)
4200  #else
4201  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
4202  #endif
4203 #endif
4204 
4205 #ifndef VMA_ALIGN_OF
4206  #define VMA_ALIGN_OF(type) (__alignof(type))
4207 #endif
4208 
4209 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4210  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
4211 #endif
4212 
4213 #ifndef VMA_SYSTEM_ALIGNED_FREE
4214  // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
4215  #if defined(VMA_SYSTEM_FREE)
4216  #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
4217  #else
4218  #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
4219  #endif
4220 #endif
4221 
4222 #ifndef VMA_MIN
4223  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4224 #endif
4225 
4226 #ifndef VMA_MAX
4227  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4228 #endif
4229 
4230 #ifndef VMA_SWAP
4231  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4232 #endif
4233 
4234 #ifndef VMA_SORT
4235  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4236 #endif
4237 
4238 #ifndef VMA_DEBUG_LOG
4239  #define VMA_DEBUG_LOG(format, ...)
4240  /*
4241  #define VMA_DEBUG_LOG(format, ...) do { \
4242  printf(format, __VA_ARGS__); \
4243  printf("\n"); \
4244  } while(false)
4245  */
4246 #endif
4247 
4248 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4249 #if VMA_STATS_STRING_ENABLED
4250  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4251  {
4252  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4253  }
4254  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4255  {
4256  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4257  }
4258  static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4259  {
4260  snprintf(outStr, strLen, "%p", ptr);
4261  }
4262 #endif
4263 
4264 #ifndef VMA_MUTEX
4265  class VmaMutex
4266  {
4267  public:
4268  void Lock() { m_Mutex.lock(); }
4269  void Unlock() { m_Mutex.unlock(); }
4270  bool TryLock() { return m_Mutex.try_lock(); }
4271  private:
4272  std::mutex m_Mutex;
4273  };
4274  #define VMA_MUTEX VmaMutex
4275 #endif
4276 
4277 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4278 #ifndef VMA_RW_MUTEX
4279  #if VMA_USE_STL_SHARED_MUTEX
4280  // Use std::shared_mutex from C++17.
4281  #include <shared_mutex>
4282  class VmaRWMutex
4283  {
4284  public:
4285  void LockRead() { m_Mutex.lock_shared(); }
4286  void UnlockRead() { m_Mutex.unlock_shared(); }
4287  bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4288  void LockWrite() { m_Mutex.lock(); }
4289  void UnlockWrite() { m_Mutex.unlock(); }
4290  bool TryLockWrite() { return m_Mutex.try_lock(); }
4291  private:
4292  std::shared_mutex m_Mutex;
4293  };
4294  #define VMA_RW_MUTEX VmaRWMutex
4295  #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4296  // Use SRWLOCK from WinAPI.
4297  // Minimum supported client = Windows Vista, server = Windows Server 2008.
4298  class VmaRWMutex
4299  {
4300  public:
4301  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4302  void LockRead() { AcquireSRWLockShared(&m_Lock); }
4303  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4304  bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4305  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4306  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4307  bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4308  private:
4309  SRWLOCK m_Lock;
4310  };
4311  #define VMA_RW_MUTEX VmaRWMutex
4312  #else
4313  // Less efficient fallback: Use normal mutex.
4314  class VmaRWMutex
4315  {
4316  public:
4317  void LockRead() { m_Mutex.Lock(); }
4318  void UnlockRead() { m_Mutex.Unlock(); }
4319  bool TryLockRead() { return m_Mutex.TryLock(); }
4320  void LockWrite() { m_Mutex.Lock(); }
4321  void UnlockWrite() { m_Mutex.Unlock(); }
4322  bool TryLockWrite() { return m_Mutex.TryLock(); }
4323  private:
4324  VMA_MUTEX m_Mutex;
4325  };
4326  #define VMA_RW_MUTEX VmaRWMutex
4327  #endif // #if VMA_USE_STL_SHARED_MUTEX
4328 #endif // #ifndef VMA_RW_MUTEX
4329 
4330 /*
4331 If providing your own implementation, you need to implement a subset of std::atomic.
4332 */
4333 #ifndef VMA_ATOMIC_UINT32
4334  #include <atomic>
4335  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4336 #endif
4337 
4338 #ifndef VMA_ATOMIC_UINT64
4339  #include <atomic>
4340  #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4341 #endif
4342 
4343 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4348  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4349 #endif
4350 
4351 #ifndef VMA_DEBUG_ALIGNMENT
4356  #define VMA_DEBUG_ALIGNMENT (1)
4357 #endif
4358 
4359 #ifndef VMA_DEBUG_MARGIN
4364  #define VMA_DEBUG_MARGIN (0)
4365 #endif
4366 
4367 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4372  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4373 #endif
4374 
4375 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4381  #define VMA_DEBUG_DETECT_CORRUPTION (0)
4382 #endif
4383 
4384 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4389  #define VMA_DEBUG_GLOBAL_MUTEX (0)
4390 #endif
4391 
4392 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4397  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4398 #endif
4399 
4400 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
4401  /*
4402  Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
4403  and return error instead of leaving up to Vulkan implementation what to do in such cases.
4404  */
4405  #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
4406 #endif
4407 
4408 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4410  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4411 #endif
4412 
4413 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4415  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4416 #endif
4417 
4418 #ifndef VMA_CLASS_NO_COPY
4419  #define VMA_CLASS_NO_COPY(className) \
4420  private: \
4421  className(const className&) = delete; \
4422  className& operator=(const className&) = delete;
4423 #endif
4424 
4425 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4426 
4427 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4428 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4429 
4430 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4431 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4432 
4433 /*******************************************************************************
4434 END OF CONFIGURATION
4435 */
4436 
4437 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4438 
4439 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4440 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4441 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4442 
4443 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4444 
4445 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4446  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4447 
4448 // Returns number of bits set to 1 in (v).
4449 static inline uint32_t VmaCountBitsSet(uint32_t v)
4450 {
4451  uint32_t c = v - ((v >> 1) & 0x55555555);
4452  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4453  c = ((c >> 4) + c) & 0x0F0F0F0F;
4454  c = ((c >> 8) + c) & 0x00FF00FF;
4455  c = ((c >> 16) + c) & 0x0000FFFF;
4456  return c;
4457 }
4458 
4459 /*
4460 Returns true if given number is a power of two.
4461 T must be unsigned integer number or signed integer but always nonnegative.
4462 For 0 returns true.
4463 */
4464 template <typename T>
4465 inline bool VmaIsPow2(T x)
4466 {
4467  return (x & (x-1)) == 0;
4468 }
4469 
4470 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4471 // Use types like uint32_t, uint64_t as T.
4472 template <typename T>
4473 static inline T VmaAlignUp(T val, T alignment)
4474 {
4475  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4476  return (val + alignment - 1) & ~(alignment - 1);
4477 }
4478 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4479 // Use types like uint32_t, uint64_t as T.
4480 template <typename T>
4481 static inline T VmaAlignDown(T val, T alignment)
4482 {
4483  VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
4484  return val & ~(alignment - 1);
4485 }
4486 
4487 // Division with mathematical rounding to nearest number.
4488 template <typename T>
4489 static inline T VmaRoundDiv(T x, T y)
4490 {
4491  return (x + (y / (T)2)) / y;
4492 }
4493 
4494 // Returns smallest power of 2 greater or equal to v.
4495 static inline uint32_t VmaNextPow2(uint32_t v)
4496 {
4497  v--;
4498  v |= v >> 1;
4499  v |= v >> 2;
4500  v |= v >> 4;
4501  v |= v >> 8;
4502  v |= v >> 16;
4503  v++;
4504  return v;
4505 }
4506 static inline uint64_t VmaNextPow2(uint64_t v)
4507 {
4508  v--;
4509  v |= v >> 1;
4510  v |= v >> 2;
4511  v |= v >> 4;
4512  v |= v >> 8;
4513  v |= v >> 16;
4514  v |= v >> 32;
4515  v++;
4516  return v;
4517 }
4518 
4519 // Returns largest power of 2 less or equal to v.
4520 static inline uint32_t VmaPrevPow2(uint32_t v)
4521 {
4522  v |= v >> 1;
4523  v |= v >> 2;
4524  v |= v >> 4;
4525  v |= v >> 8;
4526  v |= v >> 16;
4527  v = v ^ (v >> 1);
4528  return v;
4529 }
4530 static inline uint64_t VmaPrevPow2(uint64_t v)
4531 {
4532  v |= v >> 1;
4533  v |= v >> 2;
4534  v |= v >> 4;
4535  v |= v >> 8;
4536  v |= v >> 16;
4537  v |= v >> 32;
4538  v = v ^ (v >> 1);
4539  return v;
4540 }
4541 
4542 static inline bool VmaStrIsEmpty(const char* pStr)
4543 {
4544  return pStr == VMA_NULL || *pStr == '\0';
4545 }
4546 
4547 #if VMA_STATS_STRING_ENABLED
4548 
4549 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4550 {
4551  switch(algorithm)
4552  {
4554  return "Linear";
4556  return "Buddy";
4557  case 0:
4558  return "Default";
4559  default:
4560  VMA_ASSERT(0);
4561  return "";
4562  }
4563 }
4564 
4565 #endif // #if VMA_STATS_STRING_ENABLED
4566 
4567 #ifndef VMA_SORT
4568 
4569 template<typename Iterator, typename Compare>
4570 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4571 {
4572  Iterator centerValue = end; --centerValue;
4573  Iterator insertIndex = beg;
4574  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4575  {
4576  if(cmp(*memTypeIndex, *centerValue))
4577  {
4578  if(insertIndex != memTypeIndex)
4579  {
4580  VMA_SWAP(*memTypeIndex, *insertIndex);
4581  }
4582  ++insertIndex;
4583  }
4584  }
4585  if(insertIndex != centerValue)
4586  {
4587  VMA_SWAP(*insertIndex, *centerValue);
4588  }
4589  return insertIndex;
4590 }
4591 
4592 template<typename Iterator, typename Compare>
4593 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4594 {
4595  if(beg < end)
4596  {
4597  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4598  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4599  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4600  }
4601 }
4602 
4603 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4604 
4605 #endif // #ifndef VMA_SORT
4606 
4607 /*
4608 Returns true if two memory blocks occupy overlapping pages.
4609 ResourceA must be in less memory offset than ResourceB.
4610 
4611 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4612 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4613 */
4614 static inline bool VmaBlocksOnSamePage(
4615  VkDeviceSize resourceAOffset,
4616  VkDeviceSize resourceASize,
4617  VkDeviceSize resourceBOffset,
4618  VkDeviceSize pageSize)
4619 {
4620  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4621  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4622  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4623  VkDeviceSize resourceBStart = resourceBOffset;
4624  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4625  return resourceAEndPage == resourceBStartPage;
4626 }
4627 
4628 enum VmaSuballocationType
4629 {
4630  VMA_SUBALLOCATION_TYPE_FREE = 0,
4631  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4632  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4633  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4634  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4635  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4636  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4637 };
4638 
4639 /*
4640 Returns true if given suballocation types could conflict and must respect
4641 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4642 or linear image and another one is optimal image. If type is unknown, behave
4643 conservatively.
4644 */
4645 static inline bool VmaIsBufferImageGranularityConflict(
4646  VmaSuballocationType suballocType1,
4647  VmaSuballocationType suballocType2)
4648 {
4649  if(suballocType1 > suballocType2)
4650  {
4651  VMA_SWAP(suballocType1, suballocType2);
4652  }
4653 
4654  switch(suballocType1)
4655  {
4656  case VMA_SUBALLOCATION_TYPE_FREE:
4657  return false;
4658  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4659  return true;
4660  case VMA_SUBALLOCATION_TYPE_BUFFER:
4661  return
4662  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4663  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4664  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4665  return
4666  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4667  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4668  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4669  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4670  return
4671  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4672  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4673  return false;
4674  default:
4675  VMA_ASSERT(0);
4676  return true;
4677  }
4678 }
4679 
4680 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4681 {
4682 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4683  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4684  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4685  for(size_t i = 0; i < numberCount; ++i, ++pDst)
4686  {
4687  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4688  }
4689 #else
4690  // no-op
4691 #endif
4692 }
4693 
4694 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4695 {
4696 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4697  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4698  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4699  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4700  {
4701  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4702  {
4703  return false;
4704  }
4705  }
4706 #endif
4707  return true;
4708 }
4709 
4710 /*
4711 Fills structure with parameters of an example buffer to be used for transfers
4712 during GPU memory defragmentation.
4713 */
4714 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4715 {
4716  memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4717  outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4718  outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4719  outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4720 }
4721 
4722 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4723 struct VmaMutexLock
4724 {
4725  VMA_CLASS_NO_COPY(VmaMutexLock)
4726 public:
4727  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4728  m_pMutex(useMutex ? &mutex : VMA_NULL)
4729  { if(m_pMutex) { m_pMutex->Lock(); } }
4730  ~VmaMutexLock()
4731  { if(m_pMutex) { m_pMutex->Unlock(); } }
4732 private:
4733  VMA_MUTEX* m_pMutex;
4734 };
4735 
4736 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4737 struct VmaMutexLockRead
4738 {
4739  VMA_CLASS_NO_COPY(VmaMutexLockRead)
4740 public:
4741  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4742  m_pMutex(useMutex ? &mutex : VMA_NULL)
4743  { if(m_pMutex) { m_pMutex->LockRead(); } }
4744  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4745 private:
4746  VMA_RW_MUTEX* m_pMutex;
4747 };
4748 
4749 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4750 struct VmaMutexLockWrite
4751 {
4752  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4753 public:
4754  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4755  m_pMutex(useMutex ? &mutex : VMA_NULL)
4756  { if(m_pMutex) { m_pMutex->LockWrite(); } }
4757  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4758 private:
4759  VMA_RW_MUTEX* m_pMutex;
4760 };
4761 
4762 #if VMA_DEBUG_GLOBAL_MUTEX
4763  static VMA_MUTEX gDebugGlobalMutex;
4764  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4765 #else
4766  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4767 #endif
4768 
4769 // Minimum size of a free suballocation to register it in the free suballocation collection.
4770 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4771 
4772 /*
4773 Performs binary search and returns iterator to first element that is greater or
4774 equal to (key), according to comparison (cmp).
4775 
4776 Cmp should return true if first argument is less than second argument.
4777 
4778 Returned value is the found element, if present in the collection or place where
4779 new element with value (key) should be inserted.
4780 */
4781 template <typename CmpLess, typename IterT, typename KeyT>
4782 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4783 {
4784  size_t down = 0, up = (end - beg);
4785  while(down < up)
4786  {
4787  const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
4788  if(cmp(*(beg+mid), key))
4789  {
4790  down = mid + 1;
4791  }
4792  else
4793  {
4794  up = mid;
4795  }
4796  }
4797  return beg + down;
4798 }
4799 
4800 template<typename CmpLess, typename IterT, typename KeyT>
4801 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4802 {
4803  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4804  beg, end, value, cmp);
4805  if(it == end ||
4806  (!cmp(*it, value) && !cmp(value, *it)))
4807  {
4808  return it;
4809  }
4810  return end;
4811 }
4812 
4813 /*
4814 Returns true if all pointers in the array are not-null and unique.
4815 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4816 T must be pointer type, e.g. VmaAllocation, VmaPool.
4817 */
4818 template<typename T>
4819 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4820 {
4821  for(uint32_t i = 0; i < count; ++i)
4822  {
4823  const T iPtr = arr[i];
4824  if(iPtr == VMA_NULL)
4825  {
4826  return false;
4827  }
4828  for(uint32_t j = i + 1; j < count; ++j)
4829  {
4830  if(iPtr == arr[j])
4831  {
4832  return false;
4833  }
4834  }
4835  }
4836  return true;
4837 }
4838 
4839 template<typename MainT, typename NewT>
4840 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4841 {
4842  newStruct->pNext = mainStruct->pNext;
4843  mainStruct->pNext = newStruct;
4844 }
4845 
4847 // Memory allocation
4848 
4849 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4850 {
4851  void* result = VMA_NULL;
4852  if((pAllocationCallbacks != VMA_NULL) &&
4853  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4854  {
4855  result = (*pAllocationCallbacks->pfnAllocation)(
4856  pAllocationCallbacks->pUserData,
4857  size,
4858  alignment,
4859  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4860  }
4861  else
4862  {
4863  result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4864  }
4865  VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4866  return result;
4867 }
4868 
4869 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4870 {
4871  if((pAllocationCallbacks != VMA_NULL) &&
4872  (pAllocationCallbacks->pfnFree != VMA_NULL))
4873  {
4874  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4875  }
4876  else
4877  {
4878  VMA_SYSTEM_ALIGNED_FREE(ptr);
4879  }
4880 }
4881 
4882 template<typename T>
4883 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4884 {
4885  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4886 }
4887 
4888 template<typename T>
4889 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4890 {
4891  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4892 }
4893 
4894 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4895 
4896 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4897 
4898 template<typename T>
4899 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4900 {
4901  ptr->~T();
4902  VmaFree(pAllocationCallbacks, ptr);
4903 }
4904 
4905 template<typename T>
4906 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4907 {
4908  if(ptr != VMA_NULL)
4909  {
4910  for(size_t i = count; i--; )
4911  {
4912  ptr[i].~T();
4913  }
4914  VmaFree(pAllocationCallbacks, ptr);
4915  }
4916 }
4917 
4918 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4919 {
4920  if(srcStr != VMA_NULL)
4921  {
4922  const size_t len = strlen(srcStr);
4923  char* const result = vma_new_array(allocs, char, len + 1);
4924  memcpy(result, srcStr, len + 1);
4925  return result;
4926  }
4927  else
4928  {
4929  return VMA_NULL;
4930  }
4931 }
4932 
4933 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4934 {
4935  if(str != VMA_NULL)
4936  {
4937  const size_t len = strlen(str);
4938  vma_delete_array(allocs, str, len + 1);
4939  }
4940 }
4941 
4942 // STL-compatible allocator.
4943 template<typename T>
4944 class VmaStlAllocator
4945 {
4946 public:
4947  const VkAllocationCallbacks* const m_pCallbacks;
4948  typedef T value_type;
4949 
4950  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4951  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4952 
4953  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4954  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4955 
4956  template<typename U>
4957  bool operator==(const VmaStlAllocator<U>& rhs) const
4958  {
4959  return m_pCallbacks == rhs.m_pCallbacks;
4960  }
4961  template<typename U>
4962  bool operator!=(const VmaStlAllocator<U>& rhs) const
4963  {
4964  return m_pCallbacks != rhs.m_pCallbacks;
4965  }
4966 
4967  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4968 };
4969 
4970 #if VMA_USE_STL_VECTOR
4971 
4972 #define VmaVector std::vector
4973 
4974 template<typename T, typename allocatorT>
4975 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4976 {
4977  vec.insert(vec.begin() + index, item);
4978 }
4979 
4980 template<typename T, typename allocatorT>
4981 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4982 {
4983  vec.erase(vec.begin() + index);
4984 }
4985 
4986 #else // #if VMA_USE_STL_VECTOR
4987 
4988 /* Class with interface compatible with subset of std::vector.
4989 T must be POD because constructors and destructors are not called and memcpy is
4990 used for these objects. */
4991 template<typename T, typename AllocatorT>
4992 class VmaVector
4993 {
4994 public:
4995  typedef T value_type;
4996 
4997  VmaVector(const AllocatorT& allocator) :
4998  m_Allocator(allocator),
4999  m_pArray(VMA_NULL),
5000  m_Count(0),
5001  m_Capacity(0)
5002  {
5003  }
5004 
5005  VmaVector(size_t count, const AllocatorT& allocator) :
5006  m_Allocator(allocator),
5007  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
5008  m_Count(count),
5009  m_Capacity(count)
5010  {
5011  }
5012 
5013  // This version of the constructor is here for compatibility with pre-C++14 std::vector.
5014  // value is unused.
5015  VmaVector(size_t count, const T& value, const AllocatorT& allocator)
5016  : VmaVector(count, allocator) {}
5017 
5018  VmaVector(const VmaVector<T, AllocatorT>& src) :
5019  m_Allocator(src.m_Allocator),
5020  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
5021  m_Count(src.m_Count),
5022  m_Capacity(src.m_Count)
5023  {
5024  if(m_Count != 0)
5025  {
5026  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
5027  }
5028  }
5029 
5030  ~VmaVector()
5031  {
5032  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5033  }
5034 
5035  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
5036  {
5037  if(&rhs != this)
5038  {
5039  resize(rhs.m_Count);
5040  if(m_Count != 0)
5041  {
5042  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
5043  }
5044  }
5045  return *this;
5046  }
5047 
5048  bool empty() const { return m_Count == 0; }
5049  size_t size() const { return m_Count; }
5050  T* data() { return m_pArray; }
5051  const T* data() const { return m_pArray; }
5052 
5053  T& operator[](size_t index)
5054  {
5055  VMA_HEAVY_ASSERT(index < m_Count);
5056  return m_pArray[index];
5057  }
5058  const T& operator[](size_t index) const
5059  {
5060  VMA_HEAVY_ASSERT(index < m_Count);
5061  return m_pArray[index];
5062  }
5063 
5064  T& front()
5065  {
5066  VMA_HEAVY_ASSERT(m_Count > 0);
5067  return m_pArray[0];
5068  }
5069  const T& front() const
5070  {
5071  VMA_HEAVY_ASSERT(m_Count > 0);
5072  return m_pArray[0];
5073  }
5074  T& back()
5075  {
5076  VMA_HEAVY_ASSERT(m_Count > 0);
5077  return m_pArray[m_Count - 1];
5078  }
5079  const T& back() const
5080  {
5081  VMA_HEAVY_ASSERT(m_Count > 0);
5082  return m_pArray[m_Count - 1];
5083  }
5084 
5085  void reserve(size_t newCapacity, bool freeMemory = false)
5086  {
5087  newCapacity = VMA_MAX(newCapacity, m_Count);
5088 
5089  if((newCapacity < m_Capacity) && !freeMemory)
5090  {
5091  newCapacity = m_Capacity;
5092  }
5093 
5094  if(newCapacity != m_Capacity)
5095  {
5096  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
5097  if(m_Count != 0)
5098  {
5099  memcpy(newArray, m_pArray, m_Count * sizeof(T));
5100  }
5101  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5102  m_Capacity = newCapacity;
5103  m_pArray = newArray;
5104  }
5105  }
5106 
5107  void resize(size_t newCount, bool freeMemory = false)
5108  {
5109  size_t newCapacity = m_Capacity;
5110  if(newCount > m_Capacity)
5111  {
5112  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
5113  }
5114  else if(freeMemory)
5115  {
5116  newCapacity = newCount;
5117  }
5118 
5119  if(newCapacity != m_Capacity)
5120  {
5121  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
5122  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
5123  if(elementsToCopy != 0)
5124  {
5125  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
5126  }
5127  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
5128  m_Capacity = newCapacity;
5129  m_pArray = newArray;
5130  }
5131 
5132  m_Count = newCount;
5133  }
5134 
5135  void clear(bool freeMemory = false)
5136  {
5137  resize(0, freeMemory);
5138  }
5139 
5140  void insert(size_t index, const T& src)
5141  {
5142  VMA_HEAVY_ASSERT(index <= m_Count);
5143  const size_t oldCount = size();
5144  resize(oldCount + 1);
5145  if(index < oldCount)
5146  {
5147  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
5148  }
5149  m_pArray[index] = src;
5150  }
5151 
5152  void remove(size_t index)
5153  {
5154  VMA_HEAVY_ASSERT(index < m_Count);
5155  const size_t oldCount = size();
5156  if(index < oldCount - 1)
5157  {
5158  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
5159  }
5160  resize(oldCount - 1);
5161  }
5162 
5163  void push_back(const T& src)
5164  {
5165  const size_t newIndex = size();
5166  resize(newIndex + 1);
5167  m_pArray[newIndex] = src;
5168  }
5169 
5170  void pop_back()
5171  {
5172  VMA_HEAVY_ASSERT(m_Count > 0);
5173  resize(size() - 1);
5174  }
5175 
5176  void push_front(const T& src)
5177  {
5178  insert(0, src);
5179  }
5180 
5181  void pop_front()
5182  {
5183  VMA_HEAVY_ASSERT(m_Count > 0);
5184  remove(0);
5185  }
5186 
5187  typedef T* iterator;
5188 
5189  iterator begin() { return m_pArray; }
5190  iterator end() { return m_pArray + m_Count; }
5191 
5192 private:
5193  AllocatorT m_Allocator;
5194  T* m_pArray;
5195  size_t m_Count;
5196  size_t m_Capacity;
5197 };
5198 
5199 template<typename T, typename allocatorT>
5200 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5201 {
5202  vec.insert(index, item);
5203 }
5204 
5205 template<typename T, typename allocatorT>
5206 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5207 {
5208  vec.remove(index);
5209 }
5210 
5211 #endif // #if VMA_USE_STL_VECTOR
5212 
5213 template<typename CmpLess, typename VectorT>
5214 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5215 {
5216  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5217  vector.data(),
5218  vector.data() + vector.size(),
5219  value,
5220  CmpLess()) - vector.data();
5221  VmaVectorInsert(vector, indexToInsert, value);
5222  return indexToInsert;
5223 }
5224 
5225 template<typename CmpLess, typename VectorT>
5226 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5227 {
5228  CmpLess comparator;
5229  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5230  vector.begin(),
5231  vector.end(),
5232  value,
5233  comparator);
5234  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5235  {
5236  size_t indexToRemove = it - vector.begin();
5237  VmaVectorRemove(vector, indexToRemove);
5238  return true;
5239  }
5240  return false;
5241 }
5242 
5244 // class VmaSmallVector
5245 
5246 /*
5247 This is a vector (a variable-sized array), optimized for the case when the array is small.
5248 
5249 It contains some number of elements in-place, which allows it to avoid heap allocation
5250 when the actual number of elements is below that threshold. This allows normal "small"
5251 cases to be fast without losing generality for large inputs.
5252 */
5253 
5254 template<typename T, typename AllocatorT, size_t N>
5255 class VmaSmallVector
5256 {
5257 public:
5258  typedef T value_type;
5259 
5260  VmaSmallVector(const AllocatorT& allocator) :
5261  m_Count(0),
5262  m_DynamicArray(allocator)
5263  {
5264  }
5265  VmaSmallVector(size_t count, const AllocatorT& allocator) :
5266  m_Count(count),
5267  m_DynamicArray(count > N ? count : 0, allocator)
5268  {
5269  }
5270  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5271  VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5272  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5273  VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5274 
5275  bool empty() const { return m_Count == 0; }
5276  size_t size() const { return m_Count; }
5277  T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5278  const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5279 
5280  T& operator[](size_t index)
5281  {
5282  VMA_HEAVY_ASSERT(index < m_Count);
5283  return data()[index];
5284  }
5285  const T& operator[](size_t index) const
5286  {
5287  VMA_HEAVY_ASSERT(index < m_Count);
5288  return data()[index];
5289  }
5290 
5291  T& front()
5292  {
5293  VMA_HEAVY_ASSERT(m_Count > 0);
5294  return data()[0];
5295  }
5296  const T& front() const
5297  {
5298  VMA_HEAVY_ASSERT(m_Count > 0);
5299  return data()[0];
5300  }
5301  T& back()
5302  {
5303  VMA_HEAVY_ASSERT(m_Count > 0);
5304  return data()[m_Count - 1];
5305  }
5306  const T& back() const
5307  {
5308  VMA_HEAVY_ASSERT(m_Count > 0);
5309  return data()[m_Count - 1];
5310  }
5311 
5312  void resize(size_t newCount, bool freeMemory = false)
5313  {
5314  if(newCount > N && m_Count > N)
5315  {
5316  // Any direction, staying in m_DynamicArray
5317  m_DynamicArray.resize(newCount, freeMemory);
5318  }
5319  else if(newCount > N && m_Count <= N)
5320  {
5321  // Growing, moving from m_StaticArray to m_DynamicArray
5322  m_DynamicArray.resize(newCount, freeMemory);
5323  if(m_Count > 0)
5324  {
5325  memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5326  }
5327  }
5328  else if(newCount <= N && m_Count > N)
5329  {
5330  // Shrinking, moving from m_DynamicArray to m_StaticArray
5331  if(newCount > 0)
5332  {
5333  memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5334  }
5335  m_DynamicArray.resize(0, freeMemory);
5336  }
5337  else
5338  {
5339  // Any direction, staying in m_StaticArray - nothing to do here
5340  }
5341  m_Count = newCount;
5342  }
5343 
5344  void clear(bool freeMemory = false)
5345  {
5346  m_DynamicArray.clear(freeMemory);
5347  m_Count = 0;
5348  }
5349 
5350  void insert(size_t index, const T& src)
5351  {
5352  VMA_HEAVY_ASSERT(index <= m_Count);
5353  const size_t oldCount = size();
5354  resize(oldCount + 1);
5355  T* const dataPtr = data();
5356  if(index < oldCount)
5357  {
5358  // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5359  memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5360  }
5361  dataPtr[index] = src;
5362  }
5363 
5364  void remove(size_t index)
5365  {
5366  VMA_HEAVY_ASSERT(index < m_Count);
5367  const size_t oldCount = size();
5368  if(index < oldCount - 1)
5369  {
5370  // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5371  T* const dataPtr = data();
5372  memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5373  }
5374  resize(oldCount - 1);
5375  }
5376 
5377  void push_back(const T& src)
5378  {
5379  const size_t newIndex = size();
5380  resize(newIndex + 1);
5381  data()[newIndex] = src;
5382  }
5383 
5384  void pop_back()
5385  {
5386  VMA_HEAVY_ASSERT(m_Count > 0);
5387  resize(size() - 1);
5388  }
5389 
5390  void push_front(const T& src)
5391  {
5392  insert(0, src);
5393  }
5394 
5395  void pop_front()
5396  {
5397  VMA_HEAVY_ASSERT(m_Count > 0);
5398  remove(0);
5399  }
5400 
5401  typedef T* iterator;
5402 
5403  iterator begin() { return data(); }
5404  iterator end() { return data() + m_Count; }
5405 
5406 private:
5407  size_t m_Count;
5408  T m_StaticArray[N]; // Used when m_Size <= N
5409  VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5410 };
5411 
5413 // class VmaPoolAllocator
5414 
5415 /*
5416 Allocator for objects of type T using a list of arrays (pools) to speed up
5417 allocation. Number of elements that can be allocated is not bounded because
5418 allocator can create multiple blocks.
5419 */
5420 template<typename T>
5421 class VmaPoolAllocator
5422 {
5423  VMA_CLASS_NO_COPY(VmaPoolAllocator)
5424 public:
5425  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5426  ~VmaPoolAllocator();
5427  template<typename... Types> T* Alloc(Types... args);
5428  void Free(T* ptr);
5429 
5430 private:
5431  union Item
5432  {
5433  uint32_t NextFreeIndex;
5434  alignas(T) char Value[sizeof(T)];
5435  };
5436 
5437  struct ItemBlock
5438  {
5439  Item* pItems;
5440  uint32_t Capacity;
5441  uint32_t FirstFreeIndex;
5442  };
5443 
5444  const VkAllocationCallbacks* m_pAllocationCallbacks;
5445  const uint32_t m_FirstBlockCapacity;
5446  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5447 
5448  ItemBlock& CreateNewBlock();
5449 };
5450 
5451 template<typename T>
5452 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5453  m_pAllocationCallbacks(pAllocationCallbacks),
5454  m_FirstBlockCapacity(firstBlockCapacity),
5455  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5456 {
5457  VMA_ASSERT(m_FirstBlockCapacity > 1);
5458 }
5459 
5460 template<typename T>
5461 VmaPoolAllocator<T>::~VmaPoolAllocator()
5462 {
5463  for(size_t i = m_ItemBlocks.size(); i--; )
5464  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5465  m_ItemBlocks.clear();
5466 }
5467 
5468 template<typename T>
5469 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5470 {
5471  for(size_t i = m_ItemBlocks.size(); i--; )
5472  {
5473  ItemBlock& block = m_ItemBlocks[i];
5474  // This block has some free items: Use first one.
5475  if(block.FirstFreeIndex != UINT32_MAX)
5476  {
5477  Item* const pItem = &block.pItems[block.FirstFreeIndex];
5478  block.FirstFreeIndex = pItem->NextFreeIndex;
5479  T* result = (T*)&pItem->Value;
5480  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5481  return result;
5482  }
5483  }
5484 
5485  // No block has free item: Create new one and use it.
5486  ItemBlock& newBlock = CreateNewBlock();
5487  Item* const pItem = &newBlock.pItems[0];
5488  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5489  T* result = (T*)&pItem->Value;
5490  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5491  return result;
5492 }
5493 
5494 template<typename T>
5495 void VmaPoolAllocator<T>::Free(T* ptr)
5496 {
5497  // Search all memory blocks to find ptr.
5498  for(size_t i = m_ItemBlocks.size(); i--; )
5499  {
5500  ItemBlock& block = m_ItemBlocks[i];
5501 
5502  // Casting to union.
5503  Item* pItemPtr;
5504  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5505 
5506  // Check if pItemPtr is in address range of this block.
5507  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5508  {
5509  ptr->~T(); // Explicit destructor call.
5510  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5511  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5512  block.FirstFreeIndex = index;
5513  return;
5514  }
5515  }
5516  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5517 }
5518 
5519 template<typename T>
5520 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5521 {
5522  const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5523  m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5524 
5525  const ItemBlock newBlock = {
5526  vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5527  newBlockCapacity,
5528  0 };
5529 
5530  m_ItemBlocks.push_back(newBlock);
5531 
5532  // Setup singly-linked list of all free items in this block.
5533  for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5534  newBlock.pItems[i].NextFreeIndex = i + 1;
5535  newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5536  return m_ItemBlocks.back();
5537 }
5538 
5540 // class VmaRawList, VmaList
5541 
5542 #if VMA_USE_STL_LIST
5543 
5544 #define VmaList std::list
5545 
5546 #else // #if VMA_USE_STL_LIST
5547 
5548 template<typename T>
5549 struct VmaListItem
5550 {
5551  VmaListItem* pPrev;
5552  VmaListItem* pNext;
5553  T Value;
5554 };
5555 
5556 // Doubly linked list.
5557 template<typename T>
5558 class VmaRawList
5559 {
5560  VMA_CLASS_NO_COPY(VmaRawList)
5561 public:
5562  typedef VmaListItem<T> ItemType;
5563 
5564  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5565  ~VmaRawList();
5566  void Clear();
5567 
5568  size_t GetCount() const { return m_Count; }
5569  bool IsEmpty() const { return m_Count == 0; }
5570 
5571  ItemType* Front() { return m_pFront; }
5572  const ItemType* Front() const { return m_pFront; }
5573  ItemType* Back() { return m_pBack; }
5574  const ItemType* Back() const { return m_pBack; }
5575 
5576  ItemType* PushBack();
5577  ItemType* PushFront();
5578  ItemType* PushBack(const T& value);
5579  ItemType* PushFront(const T& value);
5580  void PopBack();
5581  void PopFront();
5582 
5583  // Item can be null - it means PushBack.
5584  ItemType* InsertBefore(ItemType* pItem);
5585  // Item can be null - it means PushFront.
5586  ItemType* InsertAfter(ItemType* pItem);
5587 
5588  ItemType* InsertBefore(ItemType* pItem, const T& value);
5589  ItemType* InsertAfter(ItemType* pItem, const T& value);
5590 
5591  void Remove(ItemType* pItem);
5592 
5593 private:
5594  const VkAllocationCallbacks* const m_pAllocationCallbacks;
5595  VmaPoolAllocator<ItemType> m_ItemAllocator;
5596  ItemType* m_pFront;
5597  ItemType* m_pBack;
5598  size_t m_Count;
5599 };
5600 
5601 template<typename T>
5602 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5603  m_pAllocationCallbacks(pAllocationCallbacks),
5604  m_ItemAllocator(pAllocationCallbacks, 128),
5605  m_pFront(VMA_NULL),
5606  m_pBack(VMA_NULL),
5607  m_Count(0)
5608 {
5609 }
5610 
5611 template<typename T>
5612 VmaRawList<T>::~VmaRawList()
5613 {
5614  // Intentionally not calling Clear, because that would be unnecessary
5615  // computations to return all items to m_ItemAllocator as free.
5616 }
5617 
5618 template<typename T>
5619 void VmaRawList<T>::Clear()
5620 {
5621  if(IsEmpty() == false)
5622  {
5623  ItemType* pItem = m_pBack;
5624  while(pItem != VMA_NULL)
5625  {
5626  ItemType* const pPrevItem = pItem->pPrev;
5627  m_ItemAllocator.Free(pItem);
5628  pItem = pPrevItem;
5629  }
5630  m_pFront = VMA_NULL;
5631  m_pBack = VMA_NULL;
5632  m_Count = 0;
5633  }
5634 }
5635 
5636 template<typename T>
5637 VmaListItem<T>* VmaRawList<T>::PushBack()
5638 {
5639  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5640  pNewItem->pNext = VMA_NULL;
5641  if(IsEmpty())
5642  {
5643  pNewItem->pPrev = VMA_NULL;
5644  m_pFront = pNewItem;
5645  m_pBack = pNewItem;
5646  m_Count = 1;
5647  }
5648  else
5649  {
5650  pNewItem->pPrev = m_pBack;
5651  m_pBack->pNext = pNewItem;
5652  m_pBack = pNewItem;
5653  ++m_Count;
5654  }
5655  return pNewItem;
5656 }
5657 
5658 template<typename T>
5659 VmaListItem<T>* VmaRawList<T>::PushFront()
5660 {
5661  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5662  pNewItem->pPrev = VMA_NULL;
5663  if(IsEmpty())
5664  {
5665  pNewItem->pNext = VMA_NULL;
5666  m_pFront = pNewItem;
5667  m_pBack = pNewItem;
5668  m_Count = 1;
5669  }
5670  else
5671  {
5672  pNewItem->pNext = m_pFront;
5673  m_pFront->pPrev = pNewItem;
5674  m_pFront = pNewItem;
5675  ++m_Count;
5676  }
5677  return pNewItem;
5678 }
5679 
5680 template<typename T>
5681 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5682 {
5683  ItemType* const pNewItem = PushBack();
5684  pNewItem->Value = value;
5685  return pNewItem;
5686 }
5687 
5688 template<typename T>
5689 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5690 {
5691  ItemType* const pNewItem = PushFront();
5692  pNewItem->Value = value;
5693  return pNewItem;
5694 }
5695 
5696 template<typename T>
5697 void VmaRawList<T>::PopBack()
5698 {
5699  VMA_HEAVY_ASSERT(m_Count > 0);
5700  ItemType* const pBackItem = m_pBack;
5701  ItemType* const pPrevItem = pBackItem->pPrev;
5702  if(pPrevItem != VMA_NULL)
5703  {
5704  pPrevItem->pNext = VMA_NULL;
5705  }
5706  m_pBack = pPrevItem;
5707  m_ItemAllocator.Free(pBackItem);
5708  --m_Count;
5709 }
5710 
5711 template<typename T>
5712 void VmaRawList<T>::PopFront()
5713 {
5714  VMA_HEAVY_ASSERT(m_Count > 0);
5715  ItemType* const pFrontItem = m_pFront;
5716  ItemType* const pNextItem = pFrontItem->pNext;
5717  if(pNextItem != VMA_NULL)
5718  {
5719  pNextItem->pPrev = VMA_NULL;
5720  }
5721  m_pFront = pNextItem;
5722  m_ItemAllocator.Free(pFrontItem);
5723  --m_Count;
5724 }
5725 
5726 template<typename T>
5727 void VmaRawList<T>::Remove(ItemType* pItem)
5728 {
5729  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5730  VMA_HEAVY_ASSERT(m_Count > 0);
5731 
5732  if(pItem->pPrev != VMA_NULL)
5733  {
5734  pItem->pPrev->pNext = pItem->pNext;
5735  }
5736  else
5737  {
5738  VMA_HEAVY_ASSERT(m_pFront == pItem);
5739  m_pFront = pItem->pNext;
5740  }
5741 
5742  if(pItem->pNext != VMA_NULL)
5743  {
5744  pItem->pNext->pPrev = pItem->pPrev;
5745  }
5746  else
5747  {
5748  VMA_HEAVY_ASSERT(m_pBack == pItem);
5749  m_pBack = pItem->pPrev;
5750  }
5751 
5752  m_ItemAllocator.Free(pItem);
5753  --m_Count;
5754 }
5755 
5756 template<typename T>
5757 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5758 {
5759  if(pItem != VMA_NULL)
5760  {
5761  ItemType* const prevItem = pItem->pPrev;
5762  ItemType* const newItem = m_ItemAllocator.Alloc();
5763  newItem->pPrev = prevItem;
5764  newItem->pNext = pItem;
5765  pItem->pPrev = newItem;
5766  if(prevItem != VMA_NULL)
5767  {
5768  prevItem->pNext = newItem;
5769  }
5770  else
5771  {
5772  VMA_HEAVY_ASSERT(m_pFront == pItem);
5773  m_pFront = newItem;
5774  }
5775  ++m_Count;
5776  return newItem;
5777  }
5778  else
5779  return PushBack();
5780 }
5781 
5782 template<typename T>
5783 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5784 {
5785  if(pItem != VMA_NULL)
5786  {
5787  ItemType* const nextItem = pItem->pNext;
5788  ItemType* const newItem = m_ItemAllocator.Alloc();
5789  newItem->pNext = nextItem;
5790  newItem->pPrev = pItem;
5791  pItem->pNext = newItem;
5792  if(nextItem != VMA_NULL)
5793  {
5794  nextItem->pPrev = newItem;
5795  }
5796  else
5797  {
5798  VMA_HEAVY_ASSERT(m_pBack == pItem);
5799  m_pBack = newItem;
5800  }
5801  ++m_Count;
5802  return newItem;
5803  }
5804  else
5805  return PushFront();
5806 }
5807 
5808 template<typename T>
5809 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5810 {
5811  ItemType* const newItem = InsertBefore(pItem);
5812  newItem->Value = value;
5813  return newItem;
5814 }
5815 
5816 template<typename T>
5817 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5818 {
5819  ItemType* const newItem = InsertAfter(pItem);
5820  newItem->Value = value;
5821  return newItem;
5822 }
5823 
5824 template<typename T, typename AllocatorT>
5825 class VmaList
5826 {
5827  VMA_CLASS_NO_COPY(VmaList)
5828 public:
5829  class iterator
5830  {
5831  public:
5832  iterator() :
5833  m_pList(VMA_NULL),
5834  m_pItem(VMA_NULL)
5835  {
5836  }
5837 
5838  T& operator*() const
5839  {
5840  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5841  return m_pItem->Value;
5842  }
5843  T* operator->() const
5844  {
5845  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5846  return &m_pItem->Value;
5847  }
5848 
5849  iterator& operator++()
5850  {
5851  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5852  m_pItem = m_pItem->pNext;
5853  return *this;
5854  }
5855  iterator& operator--()
5856  {
5857  if(m_pItem != VMA_NULL)
5858  {
5859  m_pItem = m_pItem->pPrev;
5860  }
5861  else
5862  {
5863  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5864  m_pItem = m_pList->Back();
5865  }
5866  return *this;
5867  }
5868 
5869  iterator operator++(int)
5870  {
5871  iterator result = *this;
5872  ++*this;
5873  return result;
5874  }
5875  iterator operator--(int)
5876  {
5877  iterator result = *this;
5878  --*this;
5879  return result;
5880  }
5881 
5882  bool operator==(const iterator& rhs) const
5883  {
5884  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5885  return m_pItem == rhs.m_pItem;
5886  }
5887  bool operator!=(const iterator& rhs) const
5888  {
5889  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5890  return m_pItem != rhs.m_pItem;
5891  }
5892 
5893  private:
5894  VmaRawList<T>* m_pList;
5895  VmaListItem<T>* m_pItem;
5896 
5897  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5898  m_pList(pList),
5899  m_pItem(pItem)
5900  {
5901  }
5902 
5903  friend class VmaList<T, AllocatorT>;
5904  };
5905 
5906  class const_iterator
5907  {
5908  public:
5909  const_iterator() :
5910  m_pList(VMA_NULL),
5911  m_pItem(VMA_NULL)
5912  {
5913  }
5914 
5915  const_iterator(const iterator& src) :
5916  m_pList(src.m_pList),
5917  m_pItem(src.m_pItem)
5918  {
5919  }
5920 
5921  const T& operator*() const
5922  {
5923  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5924  return m_pItem->Value;
5925  }
5926  const T* operator->() const
5927  {
5928  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5929  return &m_pItem->Value;
5930  }
5931 
5932  const_iterator& operator++()
5933  {
5934  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5935  m_pItem = m_pItem->pNext;
5936  return *this;
5937  }
5938  const_iterator& operator--()
5939  {
5940  if(m_pItem != VMA_NULL)
5941  {
5942  m_pItem = m_pItem->pPrev;
5943  }
5944  else
5945  {
5946  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5947  m_pItem = m_pList->Back();
5948  }
5949  return *this;
5950  }
5951 
5952  const_iterator operator++(int)
5953  {
5954  const_iterator result = *this;
5955  ++*this;
5956  return result;
5957  }
5958  const_iterator operator--(int)
5959  {
5960  const_iterator result = *this;
5961  --*this;
5962  return result;
5963  }
5964 
5965  bool operator==(const const_iterator& rhs) const
5966  {
5967  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5968  return m_pItem == rhs.m_pItem;
5969  }
5970  bool operator!=(const const_iterator& rhs) const
5971  {
5972  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5973  return m_pItem != rhs.m_pItem;
5974  }
5975 
5976  private:
5977  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5978  m_pList(pList),
5979  m_pItem(pItem)
5980  {
5981  }
5982 
5983  const VmaRawList<T>* m_pList;
5984  const VmaListItem<T>* m_pItem;
5985 
5986  friend class VmaList<T, AllocatorT>;
5987  };
5988 
5989  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5990 
5991  bool empty() const { return m_RawList.IsEmpty(); }
5992  size_t size() const { return m_RawList.GetCount(); }
5993 
5994  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
5995  iterator end() { return iterator(&m_RawList, VMA_NULL); }
5996 
5997  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
5998  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5999 
6000  void clear() { m_RawList.Clear(); }
6001  void push_back(const T& value) { m_RawList.PushBack(value); }
6002  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
6003  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
6004 
6005 private:
6006  VmaRawList<T> m_RawList;
6007 };
6008 
6009 #endif // #if VMA_USE_STL_LIST
6010 
6012 // class VmaIntrusiveLinkedList
6013 
6014 /*
6015 Expected interface of ItemTypeTraits:
6016 struct MyItemTypeTraits
6017 {
6018  typedef MyItem ItemType;
6019  static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
6020  static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
6021  static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
6022  static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
6023 };
6024 */
6025 template<typename ItemTypeTraits>
6026 class VmaIntrusiveLinkedList
6027 {
6028 public:
6029  typedef typename ItemTypeTraits::ItemType ItemType;
6030  static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
6031  static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
6032  // Movable, not copyable.
6033  VmaIntrusiveLinkedList() { }
6034  VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6035  VmaIntrusiveLinkedList(VmaIntrusiveLinkedList<ItemTypeTraits>&& src) :
6036  m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
6037  {
6038  src.m_Front = src.m_Back = VMA_NULL;
6039  src.m_Count = 0;
6040  }
6041  ~VmaIntrusiveLinkedList()
6042  {
6043  VMA_HEAVY_ASSERT(IsEmpty());
6044  }
6045  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(const VmaIntrusiveLinkedList<ItemTypeTraits>& src) = delete;
6046  VmaIntrusiveLinkedList<ItemTypeTraits>& operator=(VmaIntrusiveLinkedList<ItemTypeTraits>&& src)
6047  {
6048  if(&src != this)
6049  {
6050  VMA_HEAVY_ASSERT(IsEmpty());
6051  m_Front = src.m_Front;
6052  m_Back = src.m_Back;
6053  m_Count = src.m_Count;
6054  src.m_Front = src.m_Back = VMA_NULL;
6055  src.m_Count = 0;
6056  }
6057  return *this;
6058  }
6059  void RemoveAll()
6060  {
6061  if(!IsEmpty())
6062  {
6063  ItemType* item = m_Back;
6064  while(item != VMA_NULL)
6065  {
6066  ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
6067  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6068  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6069  item = prevItem;
6070  }
6071  m_Front = VMA_NULL;
6072  m_Back = VMA_NULL;
6073  m_Count = 0;
6074  }
6075  }
6076  size_t GetCount() const { return m_Count; }
6077  bool IsEmpty() const { return m_Count == 0; }
6078  ItemType* Front() { return m_Front; }
6079  const ItemType* Front() const { return m_Front; }
6080  ItemType* Back() { return m_Back; }
6081  const ItemType* Back() const { return m_Back; }
6082  void PushBack(ItemType* item)
6083  {
6084  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6085  if(IsEmpty())
6086  {
6087  m_Front = item;
6088  m_Back = item;
6089  m_Count = 1;
6090  }
6091  else
6092  {
6093  ItemTypeTraits::AccessPrev(item) = m_Back;
6094  ItemTypeTraits::AccessNext(m_Back) = item;
6095  m_Back = item;
6096  ++m_Count;
6097  }
6098  }
6099  void PushFront(ItemType* item)
6100  {
6101  VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
6102  if(IsEmpty())
6103  {
6104  m_Front = item;
6105  m_Back = item;
6106  m_Count = 1;
6107  }
6108  else
6109  {
6110  ItemTypeTraits::AccessNext(item) = m_Front;
6111  ItemTypeTraits::AccessPrev(m_Front) = item;
6112  m_Front = item;
6113  ++m_Count;
6114  }
6115  }
6116  ItemType* PopBack()
6117  {
6118  VMA_HEAVY_ASSERT(m_Count > 0);
6119  ItemType* const backItem = m_Back;
6120  ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
6121  if(prevItem != VMA_NULL)
6122  {
6123  ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
6124  }
6125  m_Back = prevItem;
6126  --m_Count;
6127  ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
6128  ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
6129  return backItem;
6130  }
6131  ItemType* PopFront()
6132  {
6133  VMA_HEAVY_ASSERT(m_Count > 0);
6134  ItemType* const frontItem = m_Front;
6135  ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
6136  if(nextItem != VMA_NULL)
6137  {
6138  ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
6139  }
6140  m_Front = nextItem;
6141  --m_Count;
6142  ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
6143  ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
6144  return frontItem;
6145  }
6146 
6147  // MyItem can be null - it means PushBack.
6148  void InsertBefore(ItemType* existingItem, ItemType* newItem)
6149  {
6150  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6151  if(existingItem != VMA_NULL)
6152  {
6153  ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
6154  ItemTypeTraits::AccessPrev(newItem) = prevItem;
6155  ItemTypeTraits::AccessNext(newItem) = existingItem;
6156  ItemTypeTraits::AccessPrev(existingItem) = newItem;
6157  if(prevItem != VMA_NULL)
6158  {
6159  ItemTypeTraits::AccessNext(prevItem) = newItem;
6160  }
6161  else
6162  {
6163  VMA_HEAVY_ASSERT(m_Front == existingItem);
6164  m_Front = newItem;
6165  }
6166  ++m_Count;
6167  }
6168  else
6169  PushBack(newItem);
6170  }
6171  // MyItem can be null - it means PushFront.
6172  void InsertAfter(ItemType* existingItem, ItemType* newItem)
6173  {
6174  VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
6175  if(existingItem != VMA_NULL)
6176  {
6177  ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
6178  ItemTypeTraits::AccessNext(newItem) = nextItem;
6179  ItemTypeTraits::AccessPrev(newItem) = existingItem;
6180  ItemTypeTraits::AccessNext(existingItem) = newItem;
6181  if(nextItem != VMA_NULL)
6182  {
6183  ItemTypeTraits::AccessPrev(nextItem) = newItem;
6184  }
6185  else
6186  {
6187  VMA_HEAVY_ASSERT(m_Back == existingItem);
6188  m_Back = newItem;
6189  }
6190  ++m_Count;
6191  }
6192  else
6193  return PushFront(newItem);
6194  }
6195  void Remove(ItemType* item)
6196  {
6197  VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
6198  if(ItemTypeTraits::GetPrev(item) != VMA_NULL)
6199  {
6200  ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
6201  }
6202  else
6203  {
6204  VMA_HEAVY_ASSERT(m_Front == item);
6205  m_Front = ItemTypeTraits::GetNext(item);
6206  }
6207 
6208  if(ItemTypeTraits::GetNext(item) != VMA_NULL)
6209  {
6210  ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
6211  }
6212  else
6213  {
6214  VMA_HEAVY_ASSERT(m_Back == item);
6215  m_Back = ItemTypeTraits::GetPrev(item);
6216  }
6217  ItemTypeTraits::AccessPrev(item) = VMA_NULL;
6218  ItemTypeTraits::AccessNext(item) = VMA_NULL;
6219  --m_Count;
6220  }
6221 private:
6222  ItemType* m_Front = VMA_NULL;
6223  ItemType* m_Back = VMA_NULL;
6224  size_t m_Count = 0;
6225 };
6226 
6228 // class VmaMap
6229 
6230 // Unused in this version.
6231 #if 0
6232 
6233 #if VMA_USE_STL_UNORDERED_MAP
6234 
6235 #define VmaPair std::pair
6236 
6237 #define VMA_MAP_TYPE(KeyT, ValueT) \
6238  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
6239 
6240 #else // #if VMA_USE_STL_UNORDERED_MAP
6241 
6242 template<typename T1, typename T2>
6243 struct VmaPair
6244 {
6245  T1 first;
6246  T2 second;
6247 
6248  VmaPair() : first(), second() { }
6249  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
6250 };
6251 
6252 /* Class compatible with subset of interface of std::unordered_map.
6253 KeyT, ValueT must be POD because they will be stored in VmaVector.
6254 */
6255 template<typename KeyT, typename ValueT>
6256 class VmaMap
6257 {
6258 public:
6259  typedef VmaPair<KeyT, ValueT> PairType;
6260  typedef PairType* iterator;
6261 
6262  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
6263 
6264  iterator begin() { return m_Vector.begin(); }
6265  iterator end() { return m_Vector.end(); }
6266 
6267  void insert(const PairType& pair);
6268  iterator find(const KeyT& key);
6269  void erase(iterator it);
6270 
6271 private:
6272  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
6273 };
6274 
6275 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
6276 
6277 template<typename FirstT, typename SecondT>
6278 struct VmaPairFirstLess
6279 {
6280  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
6281  {
6282  return lhs.first < rhs.first;
6283  }
6284  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
6285  {
6286  return lhs.first < rhsFirst;
6287  }
6288 };
6289 
6290 template<typename KeyT, typename ValueT>
6291 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
6292 {
6293  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
6294  m_Vector.data(),
6295  m_Vector.data() + m_Vector.size(),
6296  pair,
6297  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
6298  VmaVectorInsert(m_Vector, indexToInsert, pair);
6299 }
6300 
6301 template<typename KeyT, typename ValueT>
6302 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
6303 {
6304  PairType* it = VmaBinaryFindFirstNotLess(
6305  m_Vector.data(),
6306  m_Vector.data() + m_Vector.size(),
6307  key,
6308  VmaPairFirstLess<KeyT, ValueT>());
6309  if((it != m_Vector.end()) && (it->first == key))
6310  {
6311  return it;
6312  }
6313  else
6314  {
6315  return m_Vector.end();
6316  }
6317 }
6318 
6319 template<typename KeyT, typename ValueT>
6320 void VmaMap<KeyT, ValueT>::erase(iterator it)
6321 {
6322  VmaVectorRemove(m_Vector, it - m_Vector.begin());
6323 }
6324 
6325 #endif // #if VMA_USE_STL_UNORDERED_MAP
6326 
6327 #endif // #if 0
6328 
6330 
6331 class VmaDeviceMemoryBlock;
6332 
6333 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
6334 
6335 struct VmaAllocation_T
6336 {
6337 private:
6338  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
6339 
6340  enum FLAGS
6341  {
6342  FLAG_USER_DATA_STRING = 0x01,
6343  };
6344 
6345 public:
6346  enum ALLOCATION_TYPE
6347  {
6348  ALLOCATION_TYPE_NONE,
6349  ALLOCATION_TYPE_BLOCK,
6350  ALLOCATION_TYPE_DEDICATED,
6351  };
6352 
6353  /*
6354  This struct is allocated using VmaPoolAllocator.
6355  */
6356 
6357  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
6358  m_Alignment{1},
6359  m_Size{0},
6360  m_pUserData{VMA_NULL},
6361  m_LastUseFrameIndex{currentFrameIndex},
6362  m_MemoryTypeIndex{0},
6363  m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
6364  m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
6365  m_MapCount{0},
6366  m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
6367  {
6368 #if VMA_STATS_STRING_ENABLED
6369  m_CreationFrameIndex = currentFrameIndex;
6370  m_BufferImageUsage = 0;
6371 #endif
6372  }
6373 
6374  ~VmaAllocation_T()
6375  {
6376  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
6377 
6378  // Check if owned string was freed.
6379  VMA_ASSERT(m_pUserData == VMA_NULL);
6380  }
6381 
6382  void InitBlockAllocation(
6383  VmaDeviceMemoryBlock* block,
6384  VkDeviceSize offset,
6385  VkDeviceSize alignment,
6386  VkDeviceSize size,
6387  uint32_t memoryTypeIndex,
6388  VmaSuballocationType suballocationType,
6389  bool mapped,
6390  bool canBecomeLost)
6391  {
6392  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6393  VMA_ASSERT(block != VMA_NULL);
6394  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6395  m_Alignment = alignment;
6396  m_Size = size;
6397  m_MemoryTypeIndex = memoryTypeIndex;
6398  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6399  m_SuballocationType = (uint8_t)suballocationType;
6400  m_BlockAllocation.m_Block = block;
6401  m_BlockAllocation.m_Offset = offset;
6402  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6403  }
6404 
6405  void InitLost()
6406  {
6407  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6408  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6409  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6410  m_MemoryTypeIndex = 0;
6411  m_BlockAllocation.m_Block = VMA_NULL;
6412  m_BlockAllocation.m_Offset = 0;
6413  m_BlockAllocation.m_CanBecomeLost = true;
6414  }
6415 
6416  void ChangeBlockAllocation(
6417  VmaAllocator hAllocator,
6418  VmaDeviceMemoryBlock* block,
6419  VkDeviceSize offset);
6420 
6421  void ChangeOffset(VkDeviceSize newOffset);
6422 
6423  // pMappedData not null means allocation is created with MAPPED flag.
6424  void InitDedicatedAllocation(
6425  uint32_t memoryTypeIndex,
6426  VkDeviceMemory hMemory,
6427  VmaSuballocationType suballocationType,
6428  void* pMappedData,
6429  VkDeviceSize size)
6430  {
6431  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6432  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6433  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6434  m_Alignment = 0;
6435  m_Size = size;
6436  m_MemoryTypeIndex = memoryTypeIndex;
6437  m_SuballocationType = (uint8_t)suballocationType;
6438  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6439  m_DedicatedAllocation.m_hMemory = hMemory;
6440  m_DedicatedAllocation.m_pMappedData = pMappedData;
6441  m_DedicatedAllocation.m_Prev = VMA_NULL;
6442  m_DedicatedAllocation.m_Next = VMA_NULL;
6443  }
6444 
6445  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6446  VkDeviceSize GetAlignment() const { return m_Alignment; }
6447  VkDeviceSize GetSize() const { return m_Size; }
6448  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6449  void* GetUserData() const { return m_pUserData; }
6450  void SetUserData(VmaAllocator hAllocator, void* pUserData);
6451  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6452 
6453  VmaDeviceMemoryBlock* GetBlock() const
6454  {
6455  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6456  return m_BlockAllocation.m_Block;
6457  }
6458  VkDeviceSize GetOffset() const;
6459  VkDeviceMemory GetMemory() const;
6460  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6461  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6462  void* GetMappedData() const;
6463  bool CanBecomeLost() const;
6464 
6465  uint32_t GetLastUseFrameIndex() const
6466  {
6467  return m_LastUseFrameIndex.load();
6468  }
6469  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6470  {
6471  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6472  }
6473  /*
6474  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6475  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6476  - Else, returns false.
6477 
6478  If hAllocation is already lost, assert - you should not call it then.
6479  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6480  */
6481  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6482 
6483  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6484  {
6485  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6486  outInfo.blockCount = 1;
6487  outInfo.allocationCount = 1;
6488  outInfo.unusedRangeCount = 0;
6489  outInfo.usedBytes = m_Size;
6490  outInfo.unusedBytes = 0;
6491  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6492  outInfo.unusedRangeSizeMin = UINT64_MAX;
6493  outInfo.unusedRangeSizeMax = 0;
6494  }
6495 
6496  void BlockAllocMap();
6497  void BlockAllocUnmap();
6498  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6499  void DedicatedAllocUnmap(VmaAllocator hAllocator);
6500 
6501 #if VMA_STATS_STRING_ENABLED
6502  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6503  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6504 
6505  void InitBufferImageUsage(uint32_t bufferImageUsage)
6506  {
6507  VMA_ASSERT(m_BufferImageUsage == 0);
6508  m_BufferImageUsage = bufferImageUsage;
6509  }
6510 
6511  void PrintParameters(class VmaJsonWriter& json) const;
6512 #endif
6513 
6514 private:
6515  VkDeviceSize m_Alignment;
6516  VkDeviceSize m_Size;
6517  void* m_pUserData;
6518  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6519  uint32_t m_MemoryTypeIndex;
6520  uint8_t m_Type; // ALLOCATION_TYPE
6521  uint8_t m_SuballocationType; // VmaSuballocationType
6522  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6523  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6524  uint8_t m_MapCount;
6525  uint8_t m_Flags; // enum FLAGS
6526 
6527  // Allocation out of VmaDeviceMemoryBlock.
6528  struct BlockAllocation
6529  {
6530  VmaDeviceMemoryBlock* m_Block;
6531  VkDeviceSize m_Offset;
6532  bool m_CanBecomeLost;
6533  };
6534 
6535  // Allocation for an object that has its own private VkDeviceMemory.
6536  struct DedicatedAllocation
6537  {
6538  VkDeviceMemory m_hMemory;
6539  void* m_pMappedData; // Not null means memory is mapped.
6540  VmaAllocation_T* m_Prev;
6541  VmaAllocation_T* m_Next;
6542  };
6543 
6544  union
6545  {
6546  // Allocation out of VmaDeviceMemoryBlock.
6547  BlockAllocation m_BlockAllocation;
6548  // Allocation for an object that has its own private VkDeviceMemory.
6549  DedicatedAllocation m_DedicatedAllocation;
6550  };
6551 
6552 #if VMA_STATS_STRING_ENABLED
6553  uint32_t m_CreationFrameIndex;
6554  uint32_t m_BufferImageUsage; // 0 if unknown.
6555 #endif
6556 
6557  void FreeUserDataString(VmaAllocator hAllocator);
6558 
6559  friend struct VmaDedicatedAllocationListItemTraits;
6560 };
6561 
6562 struct VmaDedicatedAllocationListItemTraits
6563 {
6564  typedef VmaAllocation_T ItemType;
6565  static ItemType* GetPrev(const ItemType* item)
6566  {
6567  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6568  return item->m_DedicatedAllocation.m_Prev;
6569  }
6570  static ItemType* GetNext(const ItemType* item)
6571  {
6572  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6573  return item->m_DedicatedAllocation.m_Next;
6574  }
6575  static ItemType*& AccessPrev(ItemType* item)
6576  {
6577  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6578  return item->m_DedicatedAllocation.m_Prev;
6579  }
6580  static ItemType*& AccessNext(ItemType* item){
6581  VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6582  return item->m_DedicatedAllocation.m_Next;
6583  }
6584 };
6585 
6586 /*
6587 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6588 allocated memory block or free.
6589 */
6590 struct VmaSuballocation
6591 {
6592  VkDeviceSize offset;
6593  VkDeviceSize size;
6594  VmaAllocation hAllocation;
6595  VmaSuballocationType type;
6596 };
6597 
6598 // Comparator for offsets.
6599 struct VmaSuballocationOffsetLess
6600 {
6601  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6602  {
6603  return lhs.offset < rhs.offset;
6604  }
6605 };
6606 struct VmaSuballocationOffsetGreater
6607 {
6608  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6609  {
6610  return lhs.offset > rhs.offset;
6611  }
6612 };
6613 
6614 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6615 
6616 // Cost of one additional allocation lost, as equivalent in bytes.
6617 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6618 
6619 enum class VmaAllocationRequestType
6620 {
6621  Normal,
6622  // Used by "Linear" algorithm.
6623  UpperAddress,
6624  EndOf1st,
6625  EndOf2nd,
6626 };
6627 
6628 /*
6629 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6630 
6631 If canMakeOtherLost was false:
6632 - item points to a FREE suballocation.
6633 - itemsToMakeLostCount is 0.
6634 
6635 If canMakeOtherLost was true:
6636 - item points to first of sequence of suballocations, which are either FREE,
6637  or point to VmaAllocations that can become lost.
6638 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6639  the requested allocation to succeed.
6640 */
6641 struct VmaAllocationRequest
6642 {
6643  VkDeviceSize offset;
6644  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6645  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6646  VmaSuballocationList::iterator item;
6647  size_t itemsToMakeLostCount;
6648  void* customData;
6649  VmaAllocationRequestType type;
6650 
6651  VkDeviceSize CalcCost() const
6652  {
6653  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6654  }
6655 };
6656 
6657 /*
6658 Data structure used for bookkeeping of allocations and unused ranges of memory
6659 in a single VkDeviceMemory block.
6660 */
6661 class VmaBlockMetadata
6662 {
6663 public:
6664  VmaBlockMetadata(VmaAllocator hAllocator);
6665  virtual ~VmaBlockMetadata() { }
6666  virtual void Init(VkDeviceSize size) { m_Size = size; }
6667 
6668  // Validates all data structures inside this object. If not valid, returns false.
6669  virtual bool Validate() const = 0;
6670  VkDeviceSize GetSize() const { return m_Size; }
6671  virtual size_t GetAllocationCount() const = 0;
6672  virtual VkDeviceSize GetSumFreeSize() const = 0;
6673  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6674  // Returns true if this block is empty - contains only single free suballocation.
6675  virtual bool IsEmpty() const = 0;
6676 
6677  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6678  // Shouldn't modify blockCount.
6679  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6680 
6681 #if VMA_STATS_STRING_ENABLED
6682  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6683 #endif
6684 
6685  // Tries to find a place for suballocation with given parameters inside this block.
6686  // If succeeded, fills pAllocationRequest and returns true.
6687  // If failed, returns false.
6688  virtual bool CreateAllocationRequest(
6689  uint32_t currentFrameIndex,
6690  uint32_t frameInUseCount,
6691  VkDeviceSize bufferImageGranularity,
6692  VkDeviceSize allocSize,
6693  VkDeviceSize allocAlignment,
6694  bool upperAddress,
6695  VmaSuballocationType allocType,
6696  bool canMakeOtherLost,
6697  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6698  uint32_t strategy,
6699  VmaAllocationRequest* pAllocationRequest) = 0;
6700 
6701  virtual bool MakeRequestedAllocationsLost(
6702  uint32_t currentFrameIndex,
6703  uint32_t frameInUseCount,
6704  VmaAllocationRequest* pAllocationRequest) = 0;
6705 
6706  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6707 
6708  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6709 
6710  // Makes actual allocation based on request. Request must already be checked and valid.
6711  virtual void Alloc(
6712  const VmaAllocationRequest& request,
6713  VmaSuballocationType type,
6714  VkDeviceSize allocSize,
6715  VmaAllocation hAllocation) = 0;
6716 
6717  // Frees suballocation assigned to given memory region.
6718  virtual void Free(const VmaAllocation allocation) = 0;
6719  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6720 
6721 protected:
6722  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6723 
6724 #if VMA_STATS_STRING_ENABLED
6725  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6726  VkDeviceSize unusedBytes,
6727  size_t allocationCount,
6728  size_t unusedRangeCount) const;
6729  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6730  VkDeviceSize offset,
6731  VmaAllocation hAllocation) const;
6732  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6733  VkDeviceSize offset,
6734  VkDeviceSize size) const;
6735  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6736 #endif
6737 
6738 private:
6739  VkDeviceSize m_Size;
6740  const VkAllocationCallbacks* m_pAllocationCallbacks;
6741 };
6742 
6743 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6744  VMA_ASSERT(0 && "Validation failed: " #cond); \
6745  return false; \
6746  } } while(false)
6747 
6748 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6749 {
6750  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6751 public:
6752  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6753  virtual ~VmaBlockMetadata_Generic();
6754  virtual void Init(VkDeviceSize size);
6755 
6756  virtual bool Validate() const;
6757  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6758  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6759  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6760  virtual bool IsEmpty() const;
6761 
6762  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6763  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6764 
6765 #if VMA_STATS_STRING_ENABLED
6766  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6767 #endif
6768 
6769  virtual bool CreateAllocationRequest(
6770  uint32_t currentFrameIndex,
6771  uint32_t frameInUseCount,
6772  VkDeviceSize bufferImageGranularity,
6773  VkDeviceSize allocSize,
6774  VkDeviceSize allocAlignment,
6775  bool upperAddress,
6776  VmaSuballocationType allocType,
6777  bool canMakeOtherLost,
6778  uint32_t strategy,
6779  VmaAllocationRequest* pAllocationRequest);
6780 
6781  virtual bool MakeRequestedAllocationsLost(
6782  uint32_t currentFrameIndex,
6783  uint32_t frameInUseCount,
6784  VmaAllocationRequest* pAllocationRequest);
6785 
6786  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6787 
6788  virtual VkResult CheckCorruption(const void* pBlockData);
6789 
6790  virtual void Alloc(
6791  const VmaAllocationRequest& request,
6792  VmaSuballocationType type,
6793  VkDeviceSize allocSize,
6794  VmaAllocation hAllocation);
6795 
6796  virtual void Free(const VmaAllocation allocation);
6797  virtual void FreeAtOffset(VkDeviceSize offset);
6798 
6800  // For defragmentation
6801 
6802  bool IsBufferImageGranularityConflictPossible(
6803  VkDeviceSize bufferImageGranularity,
6804  VmaSuballocationType& inOutPrevSuballocType) const;
6805 
6806 private:
6807  friend class VmaDefragmentationAlgorithm_Generic;
6808  friend class VmaDefragmentationAlgorithm_Fast;
6809 
6810  uint32_t m_FreeCount;
6811  VkDeviceSize m_SumFreeSize;
6812  VmaSuballocationList m_Suballocations;
6813  // Suballocations that are free and have size greater than certain threshold.
6814  // Sorted by size, ascending.
6815  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6816 
6817  bool ValidateFreeSuballocationList() const;
6818 
6819  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6820  // If yes, fills pOffset and returns true. If no, returns false.
6821  bool CheckAllocation(
6822  uint32_t currentFrameIndex,
6823  uint32_t frameInUseCount,
6824  VkDeviceSize bufferImageGranularity,
6825  VkDeviceSize allocSize,
6826  VkDeviceSize allocAlignment,
6827  VmaSuballocationType allocType,
6828  VmaSuballocationList::const_iterator suballocItem,
6829  bool canMakeOtherLost,
6830  VkDeviceSize* pOffset,
6831  size_t* itemsToMakeLostCount,
6832  VkDeviceSize* pSumFreeSize,
6833  VkDeviceSize* pSumItemSize) const;
6834  // Given free suballocation, it merges it with following one, which must also be free.
6835  void MergeFreeWithNext(VmaSuballocationList::iterator item);
6836  // Releases given suballocation, making it free.
6837  // Merges it with adjacent free suballocations if applicable.
6838  // Returns iterator to new free suballocation at this place.
6839  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6840  // Given free suballocation, it inserts it into sorted list of
6841  // m_FreeSuballocationsBySize if it's suitable.
6842  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6843  // Given free suballocation, it removes it from sorted list of
6844  // m_FreeSuballocationsBySize if it's suitable.
6845  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6846 };
6847 
6848 /*
6849 Allocations and their references in internal data structure look like this:
6850 
6851 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6852 
6853  0 +-------+
6854  | |
6855  | |
6856  | |
6857  +-------+
6858  | Alloc | 1st[m_1stNullItemsBeginCount]
6859  +-------+
6860  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6861  +-------+
6862  | ... |
6863  +-------+
6864  | Alloc | 1st[1st.size() - 1]
6865  +-------+
6866  | |
6867  | |
6868  | |
6869 GetSize() +-------+
6870 
6871 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6872 
6873  0 +-------+
6874  | Alloc | 2nd[0]
6875  +-------+
6876  | Alloc | 2nd[1]
6877  +-------+
6878  | ... |
6879  +-------+
6880  | Alloc | 2nd[2nd.size() - 1]
6881  +-------+
6882  | |
6883  | |
6884  | |
6885  +-------+
6886  | Alloc | 1st[m_1stNullItemsBeginCount]
6887  +-------+
6888  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6889  +-------+
6890  | ... |
6891  +-------+
6892  | Alloc | 1st[1st.size() - 1]
6893  +-------+
6894  | |
6895 GetSize() +-------+
6896 
6897 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6898 
6899  0 +-------+
6900  | |
6901  | |
6902  | |
6903  +-------+
6904  | Alloc | 1st[m_1stNullItemsBeginCount]
6905  +-------+
6906  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6907  +-------+
6908  | ... |
6909  +-------+
6910  | Alloc | 1st[1st.size() - 1]
6911  +-------+
6912  | |
6913  | |
6914  | |
6915  +-------+
6916  | Alloc | 2nd[2nd.size() - 1]
6917  +-------+
6918  | ... |
6919  +-------+
6920  | Alloc | 2nd[1]
6921  +-------+
6922  | Alloc | 2nd[0]
6923 GetSize() +-------+
6924 
6925 */
6926 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6927 {
6928  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6929 public:
6930  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6931  virtual ~VmaBlockMetadata_Linear();
6932  virtual void Init(VkDeviceSize size);
6933 
6934  virtual bool Validate() const;
6935  virtual size_t GetAllocationCount() const;
6936  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6937  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6938  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6939 
6940  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6941  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6942 
6943 #if VMA_STATS_STRING_ENABLED
6944  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6945 #endif
6946 
6947  virtual bool CreateAllocationRequest(
6948  uint32_t currentFrameIndex,
6949  uint32_t frameInUseCount,
6950  VkDeviceSize bufferImageGranularity,
6951  VkDeviceSize allocSize,
6952  VkDeviceSize allocAlignment,
6953  bool upperAddress,
6954  VmaSuballocationType allocType,
6955  bool canMakeOtherLost,
6956  uint32_t strategy,
6957  VmaAllocationRequest* pAllocationRequest);
6958 
6959  virtual bool MakeRequestedAllocationsLost(
6960  uint32_t currentFrameIndex,
6961  uint32_t frameInUseCount,
6962  VmaAllocationRequest* pAllocationRequest);
6963 
6964  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6965 
6966  virtual VkResult CheckCorruption(const void* pBlockData);
6967 
6968  virtual void Alloc(
6969  const VmaAllocationRequest& request,
6970  VmaSuballocationType type,
6971  VkDeviceSize allocSize,
6972  VmaAllocation hAllocation);
6973 
6974  virtual void Free(const VmaAllocation allocation);
6975  virtual void FreeAtOffset(VkDeviceSize offset);
6976 
6977 private:
6978  /*
6979  There are two suballocation vectors, used in ping-pong way.
6980  The one with index m_1stVectorIndex is called 1st.
6981  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6982  2nd can be non-empty only when 1st is not empty.
6983  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6984  */
6985  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6986 
6987  enum SECOND_VECTOR_MODE
6988  {
6989  SECOND_VECTOR_EMPTY,
6990  /*
6991  Suballocations in 2nd vector are created later than the ones in 1st, but they
6992  all have smaller offset.
6993  */
6994  SECOND_VECTOR_RING_BUFFER,
6995  /*
6996  Suballocations in 2nd vector are upper side of double stack.
6997  They all have offsets higher than those in 1st vector.
6998  Top of this stack means smaller offsets, but higher indices in this vector.
6999  */
7000  SECOND_VECTOR_DOUBLE_STACK,
7001  };
7002 
7003  VkDeviceSize m_SumFreeSize;
7004  SuballocationVectorType m_Suballocations0, m_Suballocations1;
7005  uint32_t m_1stVectorIndex;
7006  SECOND_VECTOR_MODE m_2ndVectorMode;
7007 
7008  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7009  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7010  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
7011  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7012 
7013  // Number of items in 1st vector with hAllocation = null at the beginning.
7014  size_t m_1stNullItemsBeginCount;
7015  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7016  size_t m_1stNullItemsMiddleCount;
7017  // Number of items in 2nd vector with hAllocation = null.
7018  size_t m_2ndNullItemsCount;
7019 
7020  bool ShouldCompact1st() const;
7021  void CleanupAfterFree();
7022 
7023  bool CreateAllocationRequest_LowerAddress(
7024  uint32_t currentFrameIndex,
7025  uint32_t frameInUseCount,
7026  VkDeviceSize bufferImageGranularity,
7027  VkDeviceSize allocSize,
7028  VkDeviceSize allocAlignment,
7029  VmaSuballocationType allocType,
7030  bool canMakeOtherLost,
7031  uint32_t strategy,
7032  VmaAllocationRequest* pAllocationRequest);
7033  bool CreateAllocationRequest_UpperAddress(
7034  uint32_t currentFrameIndex,
7035  uint32_t frameInUseCount,
7036  VkDeviceSize bufferImageGranularity,
7037  VkDeviceSize allocSize,
7038  VkDeviceSize allocAlignment,
7039  VmaSuballocationType allocType,
7040  bool canMakeOtherLost,
7041  uint32_t strategy,
7042  VmaAllocationRequest* pAllocationRequest);
7043 };
7044 
7045 /*
7046 - GetSize() is the original size of allocated memory block.
7047 - m_UsableSize is this size aligned down to a power of two.
7048  All allocations and calculations happen relative to m_UsableSize.
7049 - GetUnusableSize() is the difference between them.
7050  It is repoted as separate, unused range, not available for allocations.
7051 
7052 Node at level 0 has size = m_UsableSize.
7053 Each next level contains nodes with size 2 times smaller than current level.
7054 m_LevelCount is the maximum number of levels to use in the current object.
7055 */
7056 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
7057 {
7058  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
7059 public:
7060  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
7061  virtual ~VmaBlockMetadata_Buddy();
7062  virtual void Init(VkDeviceSize size);
7063 
7064  virtual bool Validate() const;
7065  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
7066  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
7067  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
7068  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
7069 
7070  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
7071  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
7072 
7073 #if VMA_STATS_STRING_ENABLED
7074  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
7075 #endif
7076 
7077  virtual bool CreateAllocationRequest(
7078  uint32_t currentFrameIndex,
7079  uint32_t frameInUseCount,
7080  VkDeviceSize bufferImageGranularity,
7081  VkDeviceSize allocSize,
7082  VkDeviceSize allocAlignment,
7083  bool upperAddress,
7084  VmaSuballocationType allocType,
7085  bool canMakeOtherLost,
7086  uint32_t strategy,
7087  VmaAllocationRequest* pAllocationRequest);
7088 
7089  virtual bool MakeRequestedAllocationsLost(
7090  uint32_t currentFrameIndex,
7091  uint32_t frameInUseCount,
7092  VmaAllocationRequest* pAllocationRequest);
7093 
7094  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
7095 
7096  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
7097 
7098  virtual void Alloc(
7099  const VmaAllocationRequest& request,
7100  VmaSuballocationType type,
7101  VkDeviceSize allocSize,
7102  VmaAllocation hAllocation);
7103 
7104  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
7105  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
7106 
7107 private:
7108  static const VkDeviceSize MIN_NODE_SIZE = 32;
7109  static const size_t MAX_LEVELS = 30;
7110 
7111  struct ValidationContext
7112  {
7113  size_t calculatedAllocationCount;
7114  size_t calculatedFreeCount;
7115  VkDeviceSize calculatedSumFreeSize;
7116 
7117  ValidationContext() :
7118  calculatedAllocationCount(0),
7119  calculatedFreeCount(0),
7120  calculatedSumFreeSize(0) { }
7121  };
7122 
7123  struct Node
7124  {
7125  VkDeviceSize offset;
7126  enum TYPE
7127  {
7128  TYPE_FREE,
7129  TYPE_ALLOCATION,
7130  TYPE_SPLIT,
7131  TYPE_COUNT
7132  } type;
7133  Node* parent;
7134  Node* buddy;
7135 
7136  union
7137  {
7138  struct
7139  {
7140  Node* prev;
7141  Node* next;
7142  } free;
7143  struct
7144  {
7145  VmaAllocation alloc;
7146  } allocation;
7147  struct
7148  {
7149  Node* leftChild;
7150  } split;
7151  };
7152  };
7153 
7154  // Size of the memory block aligned down to a power of two.
7155  VkDeviceSize m_UsableSize;
7156  uint32_t m_LevelCount;
7157 
7158  Node* m_Root;
7159  struct {
7160  Node* front;
7161  Node* back;
7162  } m_FreeList[MAX_LEVELS];
7163  // Number of nodes in the tree with type == TYPE_ALLOCATION.
7164  size_t m_AllocationCount;
7165  // Number of nodes in the tree with type == TYPE_FREE.
7166  size_t m_FreeCount;
7167  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
7168  VkDeviceSize m_SumFreeSize;
7169 
7170  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
7171  void DeleteNode(Node* node);
7172  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
7173  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
7174  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
7175  // Alloc passed just for validation. Can be null.
7176  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
7177  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
7178  // Adds node to the front of FreeList at given level.
7179  // node->type must be FREE.
7180  // node->free.prev, next can be undefined.
7181  void AddToFreeListFront(uint32_t level, Node* node);
7182  // Removes node from FreeList at given level.
7183  // node->type must be FREE.
7184  // node->free.prev, next stay untouched.
7185  void RemoveFromFreeList(uint32_t level, Node* node);
7186 
7187 #if VMA_STATS_STRING_ENABLED
7188  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
7189 #endif
7190 };
7191 
7192 /*
7193 Represents a single block of device memory (`VkDeviceMemory`) with all the
7194 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
7195 
7196 Thread-safety: This class must be externally synchronized.
7197 */
7198 class VmaDeviceMemoryBlock
7199 {
7200  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
7201 public:
7202  VmaBlockMetadata* m_pMetadata;
7203 
7204  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
7205 
7206  ~VmaDeviceMemoryBlock()
7207  {
7208  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
7209  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
7210  }
7211 
7212  // Always call after construction.
7213  void Init(
7214  VmaAllocator hAllocator,
7215  VmaPool hParentPool,
7216  uint32_t newMemoryTypeIndex,
7217  VkDeviceMemory newMemory,
7218  VkDeviceSize newSize,
7219  uint32_t id,
7220  uint32_t algorithm);
7221  // Always call before destruction.
7222  void Destroy(VmaAllocator allocator);
7223 
7224  VmaPool GetParentPool() const { return m_hParentPool; }
7225  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
7226  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7227  uint32_t GetId() const { return m_Id; }
7228  void* GetMappedData() const { return m_pMappedData; }
7229 
7230  // Validates all data structures inside this object. If not valid, returns false.
7231  bool Validate() const;
7232 
7233  VkResult CheckCorruption(VmaAllocator hAllocator);
7234 
7235  // ppData can be null.
7236  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
7237  void Unmap(VmaAllocator hAllocator, uint32_t count);
7238 
7239  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7240  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
7241 
7242  VkResult BindBufferMemory(
7243  const VmaAllocator hAllocator,
7244  const VmaAllocation hAllocation,
7245  VkDeviceSize allocationLocalOffset,
7246  VkBuffer hBuffer,
7247  const void* pNext);
7248  VkResult BindImageMemory(
7249  const VmaAllocator hAllocator,
7250  const VmaAllocation hAllocation,
7251  VkDeviceSize allocationLocalOffset,
7252  VkImage hImage,
7253  const void* pNext);
7254 
7255 private:
7256  VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
7257  uint32_t m_MemoryTypeIndex;
7258  uint32_t m_Id;
7259  VkDeviceMemory m_hMemory;
7260 
7261  /*
7262  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
7263  Also protects m_MapCount, m_pMappedData.
7264  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
7265  */
7266  VMA_MUTEX m_Mutex;
7267  uint32_t m_MapCount;
7268  void* m_pMappedData;
7269 };
7270 
7271 struct VmaDefragmentationMove
7272 {
7273  size_t srcBlockIndex;
7274  size_t dstBlockIndex;
7275  VkDeviceSize srcOffset;
7276  VkDeviceSize dstOffset;
7277  VkDeviceSize size;
7278  VmaAllocation hAllocation;
7279  VmaDeviceMemoryBlock* pSrcBlock;
7280  VmaDeviceMemoryBlock* pDstBlock;
7281 };
7282 
7283 class VmaDefragmentationAlgorithm;
7284 
7285 /*
7286 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
7287 Vulkan memory type.
7288 
7289 Synchronized internally with a mutex.
7290 */
7291 struct VmaBlockVector
7292 {
7293  VMA_CLASS_NO_COPY(VmaBlockVector)
7294 public:
7295  VmaBlockVector(
7296  VmaAllocator hAllocator,
7297  VmaPool hParentPool,
7298  uint32_t memoryTypeIndex,
7299  VkDeviceSize preferredBlockSize,
7300  size_t minBlockCount,
7301  size_t maxBlockCount,
7302  VkDeviceSize bufferImageGranularity,
7303  uint32_t frameInUseCount,
7304  bool explicitBlockSize,
7305  uint32_t algorithm,
7306  float priority);
7307  ~VmaBlockVector();
7308 
7309  VkResult CreateMinBlocks();
7310 
7311  VmaAllocator GetAllocator() const { return m_hAllocator; }
7312  VmaPool GetParentPool() const { return m_hParentPool; }
7313  bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
7314  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
7315  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
7316  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
7317  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
7318  uint32_t GetAlgorithm() const { return m_Algorithm; }
7319 
7320  void GetPoolStats(VmaPoolStats* pStats);
7321 
7322  bool IsEmpty();
7323  bool IsCorruptionDetectionEnabled() const;
7324 
7325  VkResult Allocate(
7326  uint32_t currentFrameIndex,
7327  VkDeviceSize size,
7328  VkDeviceSize alignment,
7329  const VmaAllocationCreateInfo& createInfo,
7330  VmaSuballocationType suballocType,
7331  size_t allocationCount,
7332  VmaAllocation* pAllocations);
7333 
7334  void Free(const VmaAllocation hAllocation);
7335 
7336  // Adds statistics of this BlockVector to pStats.
7337  void AddStats(VmaStats* pStats);
7338 
7339 #if VMA_STATS_STRING_ENABLED
7340  void PrintDetailedMap(class VmaJsonWriter& json);
7341 #endif
7342 
7343  void MakePoolAllocationsLost(
7344  uint32_t currentFrameIndex,
7345  size_t* pLostAllocationCount);
7346  VkResult CheckCorruption();
7347 
7348  // Saves results in pCtx->res.
7349  void Defragment(
7350  class VmaBlockVectorDefragmentationContext* pCtx,
7352  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
7353  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
7354  VkCommandBuffer commandBuffer);
7355  void DefragmentationEnd(
7356  class VmaBlockVectorDefragmentationContext* pCtx,
7357  uint32_t flags,
7358  VmaDefragmentationStats* pStats);
7359 
7360  uint32_t ProcessDefragmentations(
7361  class VmaBlockVectorDefragmentationContext *pCtx,
7362  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
7363 
7364  void CommitDefragmentations(
7365  class VmaBlockVectorDefragmentationContext *pCtx,
7366  VmaDefragmentationStats* pStats);
7367 
7369  // To be used only while the m_Mutex is locked. Used during defragmentation.
7370 
7371  size_t GetBlockCount() const { return m_Blocks.size(); }
7372  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
7373  size_t CalcAllocationCount() const;
7374  bool IsBufferImageGranularityConflictPossible() const;
7375 
7376 private:
7377  friend class VmaDefragmentationAlgorithm_Generic;
7378 
7379  const VmaAllocator m_hAllocator;
7380  const VmaPool m_hParentPool;
7381  const uint32_t m_MemoryTypeIndex;
7382  const VkDeviceSize m_PreferredBlockSize;
7383  const size_t m_MinBlockCount;
7384  const size_t m_MaxBlockCount;
7385  const VkDeviceSize m_BufferImageGranularity;
7386  const uint32_t m_FrameInUseCount;
7387  const bool m_ExplicitBlockSize;
7388  const uint32_t m_Algorithm;
7389  const float m_Priority;
7390  VMA_RW_MUTEX m_Mutex;
7391 
7392  /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7393  a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7394  bool m_HasEmptyBlock;
7395  // Incrementally sorted by sumFreeSize, ascending.
7396  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7397  uint32_t m_NextBlockId;
7398 
7399  VkDeviceSize CalcMaxBlockSize() const;
7400 
7401  // Finds and removes given block from vector.
7402  void Remove(VmaDeviceMemoryBlock* pBlock);
7403 
7404  // Performs single step in sorting m_Blocks. They may not be fully sorted
7405  // after this call.
7406  void IncrementallySortBlocks();
7407 
7408  VkResult AllocatePage(
7409  uint32_t currentFrameIndex,
7410  VkDeviceSize size,
7411  VkDeviceSize alignment,
7412  const VmaAllocationCreateInfo& createInfo,
7413  VmaSuballocationType suballocType,
7414  VmaAllocation* pAllocation);
7415 
7416  // To be used only without CAN_MAKE_OTHER_LOST flag.
7417  VkResult AllocateFromBlock(
7418  VmaDeviceMemoryBlock* pBlock,
7419  uint32_t currentFrameIndex,
7420  VkDeviceSize size,
7421  VkDeviceSize alignment,
7422  VmaAllocationCreateFlags allocFlags,
7423  void* pUserData,
7424  VmaSuballocationType suballocType,
7425  uint32_t strategy,
7426  VmaAllocation* pAllocation);
7427 
7428  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7429 
7430  // Saves result to pCtx->res.
7431  void ApplyDefragmentationMovesCpu(
7432  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7433  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7434  // Saves result to pCtx->res.
7435  void ApplyDefragmentationMovesGpu(
7436  class VmaBlockVectorDefragmentationContext* pDefragCtx,
7437  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7438  VkCommandBuffer commandBuffer);
7439 
7440  /*
7441  Used during defragmentation. pDefragmentationStats is optional. It's in/out
7442  - updated with new data.
7443  */
7444  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7445 
7446  void UpdateHasEmptyBlock();
7447 };
7448 
7449 struct VmaPool_T
7450 {
7451  VMA_CLASS_NO_COPY(VmaPool_T)
7452 public:
7453  VmaBlockVector m_BlockVector;
7454 
7455  VmaPool_T(
7456  VmaAllocator hAllocator,
7457  const VmaPoolCreateInfo& createInfo,
7458  VkDeviceSize preferredBlockSize);
7459  ~VmaPool_T();
7460 
7461  uint32_t GetId() const { return m_Id; }
7462  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7463 
7464  const char* GetName() const { return m_Name; }
7465  void SetName(const char* pName);
7466 
7467 #if VMA_STATS_STRING_ENABLED
7468  //void PrintDetailedMap(class VmaStringBuilder& sb);
7469 #endif
7470 
7471 private:
7472  uint32_t m_Id;
7473  char* m_Name;
7474  VmaPool_T* m_PrevPool = VMA_NULL;
7475  VmaPool_T* m_NextPool = VMA_NULL;
7476  friend struct VmaPoolListItemTraits;
7477 };
7478 
7479 struct VmaPoolListItemTraits
7480 {
7481  typedef VmaPool_T ItemType;
7482  static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
7483  static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
7484  static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
7485  static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
7486 };
7487 
7488 /*
7489 Performs defragmentation:
7490 
7491 - Updates `pBlockVector->m_pMetadata`.
7492 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7493 - Does not move actual data, only returns requested moves as `moves`.
7494 */
7495 class VmaDefragmentationAlgorithm
7496 {
7497  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7498 public:
7499  VmaDefragmentationAlgorithm(
7500  VmaAllocator hAllocator,
7501  VmaBlockVector* pBlockVector,
7502  uint32_t currentFrameIndex) :
7503  m_hAllocator(hAllocator),
7504  m_pBlockVector(pBlockVector),
7505  m_CurrentFrameIndex(currentFrameIndex)
7506  {
7507  }
7508  virtual ~VmaDefragmentationAlgorithm()
7509  {
7510  }
7511 
7512  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7513  virtual void AddAll() = 0;
7514 
7515  virtual VkResult Defragment(
7516  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7517  VkDeviceSize maxBytesToMove,
7518  uint32_t maxAllocationsToMove,
7519  VmaDefragmentationFlags flags) = 0;
7520 
7521  virtual VkDeviceSize GetBytesMoved() const = 0;
7522  virtual uint32_t GetAllocationsMoved() const = 0;
7523 
7524 protected:
7525  VmaAllocator const m_hAllocator;
7526  VmaBlockVector* const m_pBlockVector;
7527  const uint32_t m_CurrentFrameIndex;
7528 
7529  struct AllocationInfo
7530  {
7531  VmaAllocation m_hAllocation;
7532  VkBool32* m_pChanged;
7533 
7534  AllocationInfo() :
7535  m_hAllocation(VK_NULL_HANDLE),
7536  m_pChanged(VMA_NULL)
7537  {
7538  }
7539  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7540  m_hAllocation(hAlloc),
7541  m_pChanged(pChanged)
7542  {
7543  }
7544  };
7545 };
7546 
7547 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7548 {
7549  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7550 public:
7551  VmaDefragmentationAlgorithm_Generic(
7552  VmaAllocator hAllocator,
7553  VmaBlockVector* pBlockVector,
7554  uint32_t currentFrameIndex,
7555  bool overlappingMoveSupported);
7556  virtual ~VmaDefragmentationAlgorithm_Generic();
7557 
7558  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7559  virtual void AddAll() { m_AllAllocations = true; }
7560 
7561  virtual VkResult Defragment(
7562  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7563  VkDeviceSize maxBytesToMove,
7564  uint32_t maxAllocationsToMove,
7565  VmaDefragmentationFlags flags);
7566 
7567  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7568  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7569 
7570 private:
7571  uint32_t m_AllocationCount;
7572  bool m_AllAllocations;
7573 
7574  VkDeviceSize m_BytesMoved;
7575  uint32_t m_AllocationsMoved;
7576 
7577  struct AllocationInfoSizeGreater
7578  {
7579  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7580  {
7581  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7582  }
7583  };
7584 
7585  struct AllocationInfoOffsetGreater
7586  {
7587  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7588  {
7589  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7590  }
7591  };
7592 
7593  struct BlockInfo
7594  {
7595  size_t m_OriginalBlockIndex;
7596  VmaDeviceMemoryBlock* m_pBlock;
7597  bool m_HasNonMovableAllocations;
7598  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7599 
7600  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7601  m_OriginalBlockIndex(SIZE_MAX),
7602  m_pBlock(VMA_NULL),
7603  m_HasNonMovableAllocations(true),
7604  m_Allocations(pAllocationCallbacks)
7605  {
7606  }
7607 
7608  void CalcHasNonMovableAllocations()
7609  {
7610  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7611  const size_t defragmentAllocCount = m_Allocations.size();
7612  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7613  }
7614 
7615  void SortAllocationsBySizeDescending()
7616  {
7617  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7618  }
7619 
7620  void SortAllocationsByOffsetDescending()
7621  {
7622  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7623  }
7624  };
7625 
7626  struct BlockPointerLess
7627  {
7628  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7629  {
7630  return pLhsBlockInfo->m_pBlock < pRhsBlock;
7631  }
7632  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7633  {
7634  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7635  }
7636  };
7637 
7638  // 1. Blocks with some non-movable allocations go first.
7639  // 2. Blocks with smaller sumFreeSize go first.
7640  struct BlockInfoCompareMoveDestination
7641  {
7642  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7643  {
7644  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7645  {
7646  return true;
7647  }
7648  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7649  {
7650  return false;
7651  }
7652  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7653  {
7654  return true;
7655  }
7656  return false;
7657  }
7658  };
7659 
7660  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7661  BlockInfoVector m_Blocks;
7662 
7663  VkResult DefragmentRound(
7664  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7665  VkDeviceSize maxBytesToMove,
7666  uint32_t maxAllocationsToMove,
7667  bool freeOldAllocations);
7668 
7669  size_t CalcBlocksWithNonMovableCount() const;
7670 
7671  static bool MoveMakesSense(
7672  size_t dstBlockIndex, VkDeviceSize dstOffset,
7673  size_t srcBlockIndex, VkDeviceSize srcOffset);
7674 };
7675 
7676 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7677 {
7678  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7679 public:
7680  VmaDefragmentationAlgorithm_Fast(
7681  VmaAllocator hAllocator,
7682  VmaBlockVector* pBlockVector,
7683  uint32_t currentFrameIndex,
7684  bool overlappingMoveSupported);
7685  virtual ~VmaDefragmentationAlgorithm_Fast();
7686 
7687  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7688  virtual void AddAll() { m_AllAllocations = true; }
7689 
7690  virtual VkResult Defragment(
7691  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7692  VkDeviceSize maxBytesToMove,
7693  uint32_t maxAllocationsToMove,
7694  VmaDefragmentationFlags flags);
7695 
7696  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7697  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7698 
7699 private:
7700  struct BlockInfo
7701  {
7702  size_t origBlockIndex;
7703  };
7704 
7705  class FreeSpaceDatabase
7706  {
7707  public:
7708  FreeSpaceDatabase()
7709  {
7710  FreeSpace s = {};
7711  s.blockInfoIndex = SIZE_MAX;
7712  for(size_t i = 0; i < MAX_COUNT; ++i)
7713  {
7714  m_FreeSpaces[i] = s;
7715  }
7716  }
7717 
7718  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7719  {
7720  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7721  {
7722  return;
7723  }
7724 
7725  // Find first invalid or the smallest structure.
7726  size_t bestIndex = SIZE_MAX;
7727  for(size_t i = 0; i < MAX_COUNT; ++i)
7728  {
7729  // Empty structure.
7730  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7731  {
7732  bestIndex = i;
7733  break;
7734  }
7735  if(m_FreeSpaces[i].size < size &&
7736  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7737  {
7738  bestIndex = i;
7739  }
7740  }
7741 
7742  if(bestIndex != SIZE_MAX)
7743  {
7744  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7745  m_FreeSpaces[bestIndex].offset = offset;
7746  m_FreeSpaces[bestIndex].size = size;
7747  }
7748  }
7749 
7750  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7751  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7752  {
7753  size_t bestIndex = SIZE_MAX;
7754  VkDeviceSize bestFreeSpaceAfter = 0;
7755  for(size_t i = 0; i < MAX_COUNT; ++i)
7756  {
7757  // Structure is valid.
7758  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7759  {
7760  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7761  // Allocation fits into this structure.
7762  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7763  {
7764  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7765  (dstOffset + size);
7766  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7767  {
7768  bestIndex = i;
7769  bestFreeSpaceAfter = freeSpaceAfter;
7770  }
7771  }
7772  }
7773  }
7774 
7775  if(bestIndex != SIZE_MAX)
7776  {
7777  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7778  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7779 
7780  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7781  {
7782  // Leave this structure for remaining empty space.
7783  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7784  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7785  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7786  }
7787  else
7788  {
7789  // This structure becomes invalid.
7790  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7791  }
7792 
7793  return true;
7794  }
7795 
7796  return false;
7797  }
7798 
7799  private:
7800  static const size_t MAX_COUNT = 4;
7801 
7802  struct FreeSpace
7803  {
7804  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7805  VkDeviceSize offset;
7806  VkDeviceSize size;
7807  } m_FreeSpaces[MAX_COUNT];
7808  };
7809 
7810  const bool m_OverlappingMoveSupported;
7811 
7812  uint32_t m_AllocationCount;
7813  bool m_AllAllocations;
7814 
7815  VkDeviceSize m_BytesMoved;
7816  uint32_t m_AllocationsMoved;
7817 
7818  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7819 
7820  void PreprocessMetadata();
7821  void PostprocessMetadata();
7822  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7823 };
7824 
7825 struct VmaBlockDefragmentationContext
7826 {
7827  enum BLOCK_FLAG
7828  {
7829  BLOCK_FLAG_USED = 0x00000001,
7830  };
7831  uint32_t flags;
7832  VkBuffer hBuffer;
7833 };
7834 
7835 class VmaBlockVectorDefragmentationContext
7836 {
7837  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7838 public:
7839  VkResult res;
7840  bool mutexLocked;
7841  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7842  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7843  uint32_t defragmentationMovesProcessed;
7844  uint32_t defragmentationMovesCommitted;
7845  bool hasDefragmentationPlan;
7846 
7847  VmaBlockVectorDefragmentationContext(
7848  VmaAllocator hAllocator,
7849  VmaPool hCustomPool, // Optional.
7850  VmaBlockVector* pBlockVector,
7851  uint32_t currFrameIndex);
7852  ~VmaBlockVectorDefragmentationContext();
7853 
7854  VmaPool GetCustomPool() const { return m_hCustomPool; }
7855  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7856  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7857 
7858  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7859  void AddAll() { m_AllAllocations = true; }
7860 
7861  void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7862 
7863 private:
7864  const VmaAllocator m_hAllocator;
7865  // Null if not from custom pool.
7866  const VmaPool m_hCustomPool;
7867  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7868  VmaBlockVector* const m_pBlockVector;
7869  const uint32_t m_CurrFrameIndex;
7870  // Owner of this object.
7871  VmaDefragmentationAlgorithm* m_pAlgorithm;
7872 
7873  struct AllocInfo
7874  {
7875  VmaAllocation hAlloc;
7876  VkBool32* pChanged;
7877  };
7878  // Used between constructor and Begin.
7879  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7880  bool m_AllAllocations;
7881 };
7882 
7883 struct VmaDefragmentationContext_T
7884 {
7885 private:
7886  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7887 public:
7888  VmaDefragmentationContext_T(
7889  VmaAllocator hAllocator,
7890  uint32_t currFrameIndex,
7891  uint32_t flags,
7892  VmaDefragmentationStats* pStats);
7893  ~VmaDefragmentationContext_T();
7894 
7895  void AddPools(uint32_t poolCount, const VmaPool* pPools);
7896  void AddAllocations(
7897  uint32_t allocationCount,
7898  const VmaAllocation* pAllocations,
7899  VkBool32* pAllocationsChanged);
7900 
7901  /*
7902  Returns:
7903  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7904  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7905  - Negative value if error occured and object can be destroyed immediately.
7906  */
7907  VkResult Defragment(
7908  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7909  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7910  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7911 
7912  VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7913  VkResult DefragmentPassEnd();
7914 
7915 private:
7916  const VmaAllocator m_hAllocator;
7917  const uint32_t m_CurrFrameIndex;
7918  const uint32_t m_Flags;
7919  VmaDefragmentationStats* const m_pStats;
7920 
7921  VkDeviceSize m_MaxCpuBytesToMove;
7922  uint32_t m_MaxCpuAllocationsToMove;
7923  VkDeviceSize m_MaxGpuBytesToMove;
7924  uint32_t m_MaxGpuAllocationsToMove;
7925 
7926  // Owner of these objects.
7927  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7928  // Owner of these objects.
7929  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7930 };
7931 
7932 #if VMA_RECORDING_ENABLED
7933 
7934 class VmaRecorder
7935 {
7936 public:
7937  VmaRecorder();
7938  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7939  void WriteConfiguration(
7940  const VkPhysicalDeviceProperties& devProps,
7941  const VkPhysicalDeviceMemoryProperties& memProps,
7942  uint32_t vulkanApiVersion,
7943  bool dedicatedAllocationExtensionEnabled,
7944  bool bindMemory2ExtensionEnabled,
7945  bool memoryBudgetExtensionEnabled,
7946  bool deviceCoherentMemoryExtensionEnabled);
7947  ~VmaRecorder();
7948 
7949  void RecordCreateAllocator(uint32_t frameIndex);
7950  void RecordDestroyAllocator(uint32_t frameIndex);
7951  void RecordCreatePool(uint32_t frameIndex,
7952  const VmaPoolCreateInfo& createInfo,
7953  VmaPool pool);
7954  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7955  void RecordAllocateMemory(uint32_t frameIndex,
7956  const VkMemoryRequirements& vkMemReq,
7957  const VmaAllocationCreateInfo& createInfo,
7958  VmaAllocation allocation);
7959  void RecordAllocateMemoryPages(uint32_t frameIndex,
7960  const VkMemoryRequirements& vkMemReq,
7961  const VmaAllocationCreateInfo& createInfo,
7962  uint64_t allocationCount,
7963  const VmaAllocation* pAllocations);
7964  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7965  const VkMemoryRequirements& vkMemReq,
7966  bool requiresDedicatedAllocation,
7967  bool prefersDedicatedAllocation,
7968  const VmaAllocationCreateInfo& createInfo,
7969  VmaAllocation allocation);
7970  void RecordAllocateMemoryForImage(uint32_t frameIndex,
7971  const VkMemoryRequirements& vkMemReq,
7972  bool requiresDedicatedAllocation,
7973  bool prefersDedicatedAllocation,
7974  const VmaAllocationCreateInfo& createInfo,
7975  VmaAllocation allocation);
7976  void RecordFreeMemory(uint32_t frameIndex,
7977  VmaAllocation allocation);
7978  void RecordFreeMemoryPages(uint32_t frameIndex,
7979  uint64_t allocationCount,
7980  const VmaAllocation* pAllocations);
7981  void RecordSetAllocationUserData(uint32_t frameIndex,
7982  VmaAllocation allocation,
7983  const void* pUserData);
7984  void RecordCreateLostAllocation(uint32_t frameIndex,
7985  VmaAllocation allocation);
7986  void RecordMapMemory(uint32_t frameIndex,
7987  VmaAllocation allocation);
7988  void RecordUnmapMemory(uint32_t frameIndex,
7989  VmaAllocation allocation);
7990  void RecordFlushAllocation(uint32_t frameIndex,
7991  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7992  void RecordInvalidateAllocation(uint32_t frameIndex,
7993  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7994  void RecordCreateBuffer(uint32_t frameIndex,
7995  const VkBufferCreateInfo& bufCreateInfo,
7996  const VmaAllocationCreateInfo& allocCreateInfo,
7997  VmaAllocation allocation);
7998  void RecordCreateImage(uint32_t frameIndex,
7999  const VkImageCreateInfo& imageCreateInfo,
8000  const VmaAllocationCreateInfo& allocCreateInfo,
8001  VmaAllocation allocation);
8002  void RecordDestroyBuffer(uint32_t frameIndex,
8003  VmaAllocation allocation);
8004  void RecordDestroyImage(uint32_t frameIndex,
8005  VmaAllocation allocation);
8006  void RecordTouchAllocation(uint32_t frameIndex,
8007  VmaAllocation allocation);
8008  void RecordGetAllocationInfo(uint32_t frameIndex,
8009  VmaAllocation allocation);
8010  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
8011  VmaPool pool);
8012  void RecordDefragmentationBegin(uint32_t frameIndex,
8013  const VmaDefragmentationInfo2& info,
8015  void RecordDefragmentationEnd(uint32_t frameIndex,
8017  void RecordSetPoolName(uint32_t frameIndex,
8018  VmaPool pool,
8019  const char* name);
8020 
8021 private:
8022  struct CallParams
8023  {
8024  uint32_t threadId;
8025  double time;
8026  };
8027 
8028  class UserDataString
8029  {
8030  public:
8031  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
8032  const char* GetString() const { return m_Str; }
8033 
8034  private:
8035  char m_PtrStr[17];
8036  const char* m_Str;
8037  };
8038 
8039  bool m_UseMutex;
8040  VmaRecordFlags m_Flags;
8041  FILE* m_File;
8042  VMA_MUTEX m_FileMutex;
8043  std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
8044 
8045  void GetBasicParams(CallParams& outParams);
8046 
8047  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
8048  template<typename T>
8049  void PrintPointerList(uint64_t count, const T* pItems)
8050  {
8051  if(count)
8052  {
8053  fprintf(m_File, "%p", pItems[0]);
8054  for(uint64_t i = 1; i < count; ++i)
8055  {
8056  fprintf(m_File, " %p", pItems[i]);
8057  }
8058  }
8059  }
8060 
8061  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
8062  void Flush();
8063 };
8064 
8065 #endif // #if VMA_RECORDING_ENABLED
8066 
8067 /*
8068 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
8069 */
8070 class VmaAllocationObjectAllocator
8071 {
8072  VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
8073 public:
8074  VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
8075 
8076  template<typename... Types> VmaAllocation Allocate(Types... args);
8077  void Free(VmaAllocation hAlloc);
8078 
8079 private:
8080  VMA_MUTEX m_Mutex;
8081  VmaPoolAllocator<VmaAllocation_T> m_Allocator;
8082 };
8083 
8084 struct VmaCurrentBudgetData
8085 {
8086  VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
8087  VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
8088 
8089 #if VMA_MEMORY_BUDGET
8090  VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
8091  VMA_RW_MUTEX m_BudgetMutex;
8092  uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
8093  uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
8094  uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
8095 #endif // #if VMA_MEMORY_BUDGET
8096 
8097  VmaCurrentBudgetData()
8098  {
8099  for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
8100  {
8101  m_BlockBytes[heapIndex] = 0;
8102  m_AllocationBytes[heapIndex] = 0;
8103 #if VMA_MEMORY_BUDGET
8104  m_VulkanUsage[heapIndex] = 0;
8105  m_VulkanBudget[heapIndex] = 0;
8106  m_BlockBytesAtBudgetFetch[heapIndex] = 0;
8107 #endif
8108  }
8109 
8110 #if VMA_MEMORY_BUDGET
8111  m_OperationsSinceBudgetFetch = 0;
8112 #endif
8113  }
8114 
8115  void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8116  {
8117  m_AllocationBytes[heapIndex] += allocationSize;
8118 #if VMA_MEMORY_BUDGET
8119  ++m_OperationsSinceBudgetFetch;
8120 #endif
8121  }
8122 
8123  void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
8124  {
8125  VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
8126  m_AllocationBytes[heapIndex] -= allocationSize;
8127 #if VMA_MEMORY_BUDGET
8128  ++m_OperationsSinceBudgetFetch;
8129 #endif
8130  }
8131 };
8132 
8133 // Main allocator object.
8134 struct VmaAllocator_T
8135 {
8136  VMA_CLASS_NO_COPY(VmaAllocator_T)
8137 public:
8138  bool m_UseMutex;
8139  uint32_t m_VulkanApiVersion;
8140  bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8141  bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
8142  bool m_UseExtMemoryBudget;
8143  bool m_UseAmdDeviceCoherentMemory;
8144  bool m_UseKhrBufferDeviceAddress;
8145  bool m_UseExtMemoryPriority;
8146  VkDevice m_hDevice;
8147  VkInstance m_hInstance;
8148  bool m_AllocationCallbacksSpecified;
8149  VkAllocationCallbacks m_AllocationCallbacks;
8150  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
8151  VmaAllocationObjectAllocator m_AllocationObjectAllocator;
8152 
8153  // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
8154  uint32_t m_HeapSizeLimitMask;
8155 
8156  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
8157  VkPhysicalDeviceMemoryProperties m_MemProps;
8158 
8159  // Default pools.
8160  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
8161 
8162  typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
8163  DedicatedAllocationLinkedList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
8164  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
8165 
8166  VmaCurrentBudgetData m_Budget;
8167  VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
8168 
8169  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
8170  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
8171  ~VmaAllocator_T();
8172 
8173  const VkAllocationCallbacks* GetAllocationCallbacks() const
8174  {
8175  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
8176  }
8177  const VmaVulkanFunctions& GetVulkanFunctions() const
8178  {
8179  return m_VulkanFunctions;
8180  }
8181 
8182  VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
8183 
8184  VkDeviceSize GetBufferImageGranularity() const
8185  {
8186  return VMA_MAX(
8187  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
8188  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
8189  }
8190 
8191  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
8192  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
8193 
8194  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
8195  {
8196  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
8197  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
8198  }
8199  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
8200  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
8201  {
8202  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
8203  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8204  }
8205  // Minimum alignment for all allocations in specific memory type.
8206  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
8207  {
8208  return IsMemoryTypeNonCoherent(memTypeIndex) ?
8209  VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
8210  (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
8211  }
8212 
8213  bool IsIntegratedGpu() const
8214  {
8215  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
8216  }
8217 
8218  uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
8219 
8220 #if VMA_RECORDING_ENABLED
8221  VmaRecorder* GetRecorder() const { return m_pRecorder; }
8222 #endif
8223 
8224  void GetBufferMemoryRequirements(
8225  VkBuffer hBuffer,
8226  VkMemoryRequirements& memReq,
8227  bool& requiresDedicatedAllocation,
8228  bool& prefersDedicatedAllocation) const;
8229  void GetImageMemoryRequirements(
8230  VkImage hImage,
8231  VkMemoryRequirements& memReq,
8232  bool& requiresDedicatedAllocation,
8233  bool& prefersDedicatedAllocation) const;
8234 
8235  // Main allocation function.
8236  VkResult AllocateMemory(
8237  const VkMemoryRequirements& vkMemReq,
8238  bool requiresDedicatedAllocation,
8239  bool prefersDedicatedAllocation,
8240  VkBuffer dedicatedBuffer,
8241  VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
8242  VkImage dedicatedImage,
8243  const VmaAllocationCreateInfo& createInfo,
8244  VmaSuballocationType suballocType,
8245  size_t allocationCount,
8246  VmaAllocation* pAllocations);
8247 
8248  // Main deallocation function.
8249  void FreeMemory(
8250  size_t allocationCount,
8251  const VmaAllocation* pAllocations);
8252 
8253  void CalculateStats(VmaStats* pStats);
8254 
8255  void GetBudget(
8256  VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
8257 
8258 #if VMA_STATS_STRING_ENABLED
8259  void PrintDetailedMap(class VmaJsonWriter& json);
8260 #endif
8261 
8262  VkResult DefragmentationBegin(
8263  const VmaDefragmentationInfo2& info,
8264  VmaDefragmentationStats* pStats,
8265  VmaDefragmentationContext* pContext);
8266  VkResult DefragmentationEnd(
8267  VmaDefragmentationContext context);
8268 
8269  VkResult DefragmentationPassBegin(
8271  VmaDefragmentationContext context);
8272  VkResult DefragmentationPassEnd(
8273  VmaDefragmentationContext context);
8274 
8275  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
8276  bool TouchAllocation(VmaAllocation hAllocation);
8277 
8278  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
8279  void DestroyPool(VmaPool pool);
8280  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
8281 
8282  void SetCurrentFrameIndex(uint32_t frameIndex);
8283  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
8284 
8285  void MakePoolAllocationsLost(
8286  VmaPool hPool,
8287  size_t* pLostAllocationCount);
8288  VkResult CheckPoolCorruption(VmaPool hPool);
8289  VkResult CheckCorruption(uint32_t memoryTypeBits);
8290 
8291  void CreateLostAllocation(VmaAllocation* pAllocation);
8292 
8293  // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
8294  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
8295  // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
8296  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
8297  // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
8298  VkResult BindVulkanBuffer(
8299  VkDeviceMemory memory,
8300  VkDeviceSize memoryOffset,
8301  VkBuffer buffer,
8302  const void* pNext);
8303  // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
8304  VkResult BindVulkanImage(
8305  VkDeviceMemory memory,
8306  VkDeviceSize memoryOffset,
8307  VkImage image,
8308  const void* pNext);
8309 
8310  VkResult Map(VmaAllocation hAllocation, void** ppData);
8311  void Unmap(VmaAllocation hAllocation);
8312 
8313  VkResult BindBufferMemory(
8314  VmaAllocation hAllocation,
8315  VkDeviceSize allocationLocalOffset,
8316  VkBuffer hBuffer,
8317  const void* pNext);
8318  VkResult BindImageMemory(
8319  VmaAllocation hAllocation,
8320  VkDeviceSize allocationLocalOffset,
8321  VkImage hImage,
8322  const void* pNext);
8323 
8324  VkResult FlushOrInvalidateAllocation(
8325  VmaAllocation hAllocation,
8326  VkDeviceSize offset, VkDeviceSize size,
8327  VMA_CACHE_OPERATION op);
8328  VkResult FlushOrInvalidateAllocations(
8329  uint32_t allocationCount,
8330  const VmaAllocation* allocations,
8331  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
8332  VMA_CACHE_OPERATION op);
8333 
8334  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
8335 
8336  /*
8337  Returns bit mask of memory types that can support defragmentation on GPU as
8338  they support creation of required buffer for copy operations.
8339  */
8340  uint32_t GetGpuDefragmentationMemoryTypeBits();
8341 
8342 private:
8343  VkDeviceSize m_PreferredLargeHeapBlockSize;
8344 
8345  VkPhysicalDevice m_PhysicalDevice;
8346  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
8347  VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
8348 
8349  VMA_RW_MUTEX m_PoolsMutex;
8350  typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
8351  // Protected by m_PoolsMutex.
8352  PoolList m_Pools;
8353  uint32_t m_NextPoolId;
8354 
8355  VmaVulkanFunctions m_VulkanFunctions;
8356 
8357  // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
8358  uint32_t m_GlobalMemoryTypeBits;
8359 
8360 #if VMA_RECORDING_ENABLED
8361  VmaRecorder* m_pRecorder;
8362 #endif
8363 
8364  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
8365 
8366 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8367  void ImportVulkanFunctions_Static();
8368 #endif
8369 
8370  void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8371 
8372 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8373  void ImportVulkanFunctions_Dynamic();
8374 #endif
8375 
8376  void ValidateVulkanFunctions();
8377 
8378  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8379 
8380  VkResult AllocateMemoryOfType(
8381  VkDeviceSize size,
8382  VkDeviceSize alignment,
8383  bool dedicatedAllocation,
8384  VkBuffer dedicatedBuffer,
8385  VkBufferUsageFlags dedicatedBufferUsage,
8386  VkImage dedicatedImage,
8387  const VmaAllocationCreateInfo& createInfo,
8388  uint32_t memTypeIndex,
8389  VmaSuballocationType suballocType,
8390  size_t allocationCount,
8391  VmaAllocation* pAllocations);
8392 
8393  // Helper function only to be used inside AllocateDedicatedMemory.
8394  VkResult AllocateDedicatedMemoryPage(
8395  VkDeviceSize size,
8396  VmaSuballocationType suballocType,
8397  uint32_t memTypeIndex,
8398  const VkMemoryAllocateInfo& allocInfo,
8399  bool map,
8400  bool isUserDataString,
8401  void* pUserData,
8402  VmaAllocation* pAllocation);
8403 
8404  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8405  VkResult AllocateDedicatedMemory(
8406  VkDeviceSize size,
8407  VmaSuballocationType suballocType,
8408  uint32_t memTypeIndex,
8409  bool withinBudget,
8410  bool map,
8411  bool isUserDataString,
8412  void* pUserData,
8413  float priority,
8414  VkBuffer dedicatedBuffer,
8415  VkBufferUsageFlags dedicatedBufferUsage,
8416  VkImage dedicatedImage,
8417  size_t allocationCount,
8418  VmaAllocation* pAllocations);
8419 
8420  void FreeDedicatedMemory(const VmaAllocation allocation);
8421 
8422  /*
8423  Calculates and returns bit mask of memory types that can support defragmentation
8424  on GPU as they support creation of required buffer for copy operations.
8425  */
8426  uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8427 
8428  uint32_t CalculateGlobalMemoryTypeBits() const;
8429 
8430  bool GetFlushOrInvalidateRange(
8431  VmaAllocation allocation,
8432  VkDeviceSize offset, VkDeviceSize size,
8433  VkMappedMemoryRange& outRange) const;
8434 
8435 #if VMA_MEMORY_BUDGET
8436  void UpdateVulkanBudget();
8437 #endif // #if VMA_MEMORY_BUDGET
8438 };
8439 
8441 // Memory allocation #2 after VmaAllocator_T definition
8442 
8443 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8444 {
8445  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8446 }
8447 
8448 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8449 {
8450  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8451 }
8452 
8453 template<typename T>
8454 static T* VmaAllocate(VmaAllocator hAllocator)
8455 {
8456  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8457 }
8458 
8459 template<typename T>
8460 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8461 {
8462  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8463 }
8464 
8465 template<typename T>
8466 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8467 {
8468  if(ptr != VMA_NULL)
8469  {
8470  ptr->~T();
8471  VmaFree(hAllocator, ptr);
8472  }
8473 }
8474 
8475 template<typename T>
8476 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8477 {
8478  if(ptr != VMA_NULL)
8479  {
8480  for(size_t i = count; i--; )
8481  ptr[i].~T();
8482  VmaFree(hAllocator, ptr);
8483  }
8484 }
8485 
8487 // VmaStringBuilder
8488 
8489 #if VMA_STATS_STRING_ENABLED
8490 
8491 class VmaStringBuilder
8492 {
8493 public:
8494  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8495  size_t GetLength() const { return m_Data.size(); }
8496  const char* GetData() const { return m_Data.data(); }
8497 
8498  void Add(char ch) { m_Data.push_back(ch); }
8499  void Add(const char* pStr);
8500  void AddNewLine() { Add('\n'); }
8501  void AddNumber(uint32_t num);
8502  void AddNumber(uint64_t num);
8503  void AddPointer(const void* ptr);
8504 
8505 private:
8506  VmaVector< char, VmaStlAllocator<char> > m_Data;
8507 };
8508 
8509 void VmaStringBuilder::Add(const char* pStr)
8510 {
8511  const size_t strLen = strlen(pStr);
8512  if(strLen > 0)
8513  {
8514  const size_t oldCount = m_Data.size();
8515  m_Data.resize(oldCount + strLen);
8516  memcpy(m_Data.data() + oldCount, pStr, strLen);
8517  }
8518 }
8519 
8520 void VmaStringBuilder::AddNumber(uint32_t num)
8521 {
8522  char buf[11];
8523  buf[10] = '\0';
8524  char *p = &buf[10];
8525  do
8526  {
8527  *--p = '0' + (num % 10);
8528  num /= 10;
8529  }
8530  while(num);
8531  Add(p);
8532 }
8533 
8534 void VmaStringBuilder::AddNumber(uint64_t num)
8535 {
8536  char buf[21];
8537  buf[20] = '\0';
8538  char *p = &buf[20];
8539  do
8540  {
8541  *--p = '0' + (num % 10);
8542  num /= 10;
8543  }
8544  while(num);
8545  Add(p);
8546 }
8547 
8548 void VmaStringBuilder::AddPointer(const void* ptr)
8549 {
8550  char buf[21];
8551  VmaPtrToStr(buf, sizeof(buf), ptr);
8552  Add(buf);
8553 }
8554 
8555 #endif // #if VMA_STATS_STRING_ENABLED
8556 
8558 // VmaJsonWriter
8559 
8560 #if VMA_STATS_STRING_ENABLED
8561 
8562 class VmaJsonWriter
8563 {
8564  VMA_CLASS_NO_COPY(VmaJsonWriter)
8565 public:
8566  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8567  ~VmaJsonWriter();
8568 
8569  void BeginObject(bool singleLine = false);
8570  void EndObject();
8571 
8572  void BeginArray(bool singleLine = false);
8573  void EndArray();
8574 
8575  void WriteString(const char* pStr);
8576  void BeginString(const char* pStr = VMA_NULL);
8577  void ContinueString(const char* pStr);
8578  void ContinueString(uint32_t n);
8579  void ContinueString(uint64_t n);
8580  void ContinueString_Pointer(const void* ptr);
8581  void EndString(const char* pStr = VMA_NULL);
8582 
8583  void WriteNumber(uint32_t n);
8584  void WriteNumber(uint64_t n);
8585  void WriteBool(bool b);
8586  void WriteNull();
8587 
8588 private:
8589  static const char* const INDENT;
8590 
8591  enum COLLECTION_TYPE
8592  {
8593  COLLECTION_TYPE_OBJECT,
8594  COLLECTION_TYPE_ARRAY,
8595  };
8596  struct StackItem
8597  {
8598  COLLECTION_TYPE type;
8599  uint32_t valueCount;
8600  bool singleLineMode;
8601  };
8602 
8603  VmaStringBuilder& m_SB;
8604  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8605  bool m_InsideString;
8606 
8607  void BeginValue(bool isString);
8608  void WriteIndent(bool oneLess = false);
8609 };
8610 
8611 const char* const VmaJsonWriter::INDENT = " ";
8612 
8613 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8614  m_SB(sb),
8615  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8616  m_InsideString(false)
8617 {
8618 }
8619 
8620 VmaJsonWriter::~VmaJsonWriter()
8621 {
8622  VMA_ASSERT(!m_InsideString);
8623  VMA_ASSERT(m_Stack.empty());
8624 }
8625 
8626 void VmaJsonWriter::BeginObject(bool singleLine)
8627 {
8628  VMA_ASSERT(!m_InsideString);
8629 
8630  BeginValue(false);
8631  m_SB.Add('{');
8632 
8633  StackItem item;
8634  item.type = COLLECTION_TYPE_OBJECT;
8635  item.valueCount = 0;
8636  item.singleLineMode = singleLine;
8637  m_Stack.push_back(item);
8638 }
8639 
8640 void VmaJsonWriter::EndObject()
8641 {
8642  VMA_ASSERT(!m_InsideString);
8643 
8644  WriteIndent(true);
8645  m_SB.Add('}');
8646 
8647  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8648  m_Stack.pop_back();
8649 }
8650 
8651 void VmaJsonWriter::BeginArray(bool singleLine)
8652 {
8653  VMA_ASSERT(!m_InsideString);
8654 
8655  BeginValue(false);
8656  m_SB.Add('[');
8657 
8658  StackItem item;
8659  item.type = COLLECTION_TYPE_ARRAY;
8660  item.valueCount = 0;
8661  item.singleLineMode = singleLine;
8662  m_Stack.push_back(item);
8663 }
8664 
8665 void VmaJsonWriter::EndArray()
8666 {
8667  VMA_ASSERT(!m_InsideString);
8668 
8669  WriteIndent(true);
8670  m_SB.Add(']');
8671 
8672  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8673  m_Stack.pop_back();
8674 }
8675 
8676 void VmaJsonWriter::WriteString(const char* pStr)
8677 {
8678  BeginString(pStr);
8679  EndString();
8680 }
8681 
8682 void VmaJsonWriter::BeginString(const char* pStr)
8683 {
8684  VMA_ASSERT(!m_InsideString);
8685 
8686  BeginValue(true);
8687  m_SB.Add('"');
8688  m_InsideString = true;
8689  if(pStr != VMA_NULL && pStr[0] != '\0')
8690  {
8691  ContinueString(pStr);
8692  }
8693 }
8694 
8695 void VmaJsonWriter::ContinueString(const char* pStr)
8696 {
8697  VMA_ASSERT(m_InsideString);
8698 
8699  const size_t strLen = strlen(pStr);
8700  for(size_t i = 0; i < strLen; ++i)
8701  {
8702  char ch = pStr[i];
8703  if(ch == '\\')
8704  {
8705  m_SB.Add("\\\\");
8706  }
8707  else if(ch == '"')
8708  {
8709  m_SB.Add("\\\"");
8710  }
8711  else if(ch >= 32)
8712  {
8713  m_SB.Add(ch);
8714  }
8715  else switch(ch)
8716  {
8717  case '\b':
8718  m_SB.Add("\\b");
8719  break;
8720  case '\f':
8721  m_SB.Add("\\f");
8722  break;
8723  case '\n':
8724  m_SB.Add("\\n");
8725  break;
8726  case '\r':
8727  m_SB.Add("\\r");
8728  break;
8729  case '\t':
8730  m_SB.Add("\\t");
8731  break;
8732  default:
8733  VMA_ASSERT(0 && "Character not currently supported.");
8734  break;
8735  }
8736  }
8737 }
8738 
8739 void VmaJsonWriter::ContinueString(uint32_t n)
8740 {
8741  VMA_ASSERT(m_InsideString);
8742  m_SB.AddNumber(n);
8743 }
8744 
8745 void VmaJsonWriter::ContinueString(uint64_t n)
8746 {
8747  VMA_ASSERT(m_InsideString);
8748  m_SB.AddNumber(n);
8749 }
8750 
8751 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8752 {
8753  VMA_ASSERT(m_InsideString);
8754  m_SB.AddPointer(ptr);
8755 }
8756 
8757 void VmaJsonWriter::EndString(const char* pStr)
8758 {
8759  VMA_ASSERT(m_InsideString);
8760  if(pStr != VMA_NULL && pStr[0] != '\0')
8761  {
8762  ContinueString(pStr);
8763  }
8764  m_SB.Add('"');
8765  m_InsideString = false;
8766 }
8767 
8768 void VmaJsonWriter::WriteNumber(uint32_t n)
8769 {
8770  VMA_ASSERT(!m_InsideString);
8771  BeginValue(false);
8772  m_SB.AddNumber(n);
8773 }
8774 
8775 void VmaJsonWriter::WriteNumber(uint64_t n)
8776 {
8777  VMA_ASSERT(!m_InsideString);
8778  BeginValue(false);
8779  m_SB.AddNumber(n);
8780 }
8781 
8782 void VmaJsonWriter::WriteBool(bool b)
8783 {
8784  VMA_ASSERT(!m_InsideString);
8785  BeginValue(false);
8786  m_SB.Add(b ? "true" : "false");
8787 }
8788 
8789 void VmaJsonWriter::WriteNull()
8790 {
8791  VMA_ASSERT(!m_InsideString);
8792  BeginValue(false);
8793  m_SB.Add("null");
8794 }
8795 
8796 void VmaJsonWriter::BeginValue(bool isString)
8797 {
8798  if(!m_Stack.empty())
8799  {
8800  StackItem& currItem = m_Stack.back();
8801  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8802  currItem.valueCount % 2 == 0)
8803  {
8804  VMA_ASSERT(isString);
8805  }
8806 
8807  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8808  currItem.valueCount % 2 != 0)
8809  {
8810  m_SB.Add(": ");
8811  }
8812  else if(currItem.valueCount > 0)
8813  {
8814  m_SB.Add(", ");
8815  WriteIndent();
8816  }
8817  else
8818  {
8819  WriteIndent();
8820  }
8821  ++currItem.valueCount;
8822  }
8823 }
8824 
8825 void VmaJsonWriter::WriteIndent(bool oneLess)
8826 {
8827  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8828  {
8829  m_SB.AddNewLine();
8830 
8831  size_t count = m_Stack.size();
8832  if(count > 0 && oneLess)
8833  {
8834  --count;
8835  }
8836  for(size_t i = 0; i < count; ++i)
8837  {
8838  m_SB.Add(INDENT);
8839  }
8840  }
8841 }
8842 
8843 #endif // #if VMA_STATS_STRING_ENABLED
8844 
8846 
8847 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8848 {
8849  if(IsUserDataString())
8850  {
8851  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8852 
8853  FreeUserDataString(hAllocator);
8854 
8855  if(pUserData != VMA_NULL)
8856  {
8857  m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8858  }
8859  }
8860  else
8861  {
8862  m_pUserData = pUserData;
8863  }
8864 }
8865 
8866 void VmaAllocation_T::ChangeBlockAllocation(
8867  VmaAllocator hAllocator,
8868  VmaDeviceMemoryBlock* block,
8869  VkDeviceSize offset)
8870 {
8871  VMA_ASSERT(block != VMA_NULL);
8872  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8873 
8874  // Move mapping reference counter from old block to new block.
8875  if(block != m_BlockAllocation.m_Block)
8876  {
8877  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8878  if(IsPersistentMap())
8879  ++mapRefCount;
8880  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8881  block->Map(hAllocator, mapRefCount, VMA_NULL);
8882  }
8883 
8884  m_BlockAllocation.m_Block = block;
8885  m_BlockAllocation.m_Offset = offset;
8886 }
8887 
8888 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8889 {
8890  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8891  m_BlockAllocation.m_Offset = newOffset;
8892 }
8893 
8894 VkDeviceSize VmaAllocation_T::GetOffset() const
8895 {
8896  switch(m_Type)
8897  {
8898  case ALLOCATION_TYPE_BLOCK:
8899  return m_BlockAllocation.m_Offset;
8900  case ALLOCATION_TYPE_DEDICATED:
8901  return 0;
8902  default:
8903  VMA_ASSERT(0);
8904  return 0;
8905  }
8906 }
8907 
8908 VkDeviceMemory VmaAllocation_T::GetMemory() const
8909 {
8910  switch(m_Type)
8911  {
8912  case ALLOCATION_TYPE_BLOCK:
8913  return m_BlockAllocation.m_Block->GetDeviceMemory();
8914  case ALLOCATION_TYPE_DEDICATED:
8915  return m_DedicatedAllocation.m_hMemory;
8916  default:
8917  VMA_ASSERT(0);
8918  return VK_NULL_HANDLE;
8919  }
8920 }
8921 
8922 void* VmaAllocation_T::GetMappedData() const
8923 {
8924  switch(m_Type)
8925  {
8926  case ALLOCATION_TYPE_BLOCK:
8927  if(m_MapCount != 0)
8928  {
8929  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8930  VMA_ASSERT(pBlockData != VMA_NULL);
8931  return (char*)pBlockData + m_BlockAllocation.m_Offset;
8932  }
8933  else
8934  {
8935  return VMA_NULL;
8936  }
8937  break;
8938  case ALLOCATION_TYPE_DEDICATED:
8939  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8940  return m_DedicatedAllocation.m_pMappedData;
8941  default:
8942  VMA_ASSERT(0);
8943  return VMA_NULL;
8944  }
8945 }
8946 
8947 bool VmaAllocation_T::CanBecomeLost() const
8948 {
8949  switch(m_Type)
8950  {
8951  case ALLOCATION_TYPE_BLOCK:
8952  return m_BlockAllocation.m_CanBecomeLost;
8953  case ALLOCATION_TYPE_DEDICATED:
8954  return false;
8955  default:
8956  VMA_ASSERT(0);
8957  return false;
8958  }
8959 }
8960 
8961 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8962 {
8963  VMA_ASSERT(CanBecomeLost());
8964 
8965  /*
8966  Warning: This is a carefully designed algorithm.
8967  Do not modify unless you really know what you're doing :)
8968  */
8969  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8970  for(;;)
8971  {
8972  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8973  {
8974  VMA_ASSERT(0);
8975  return false;
8976  }
8977  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8978  {
8979  return false;
8980  }
8981  else // Last use time earlier than current time.
8982  {
8983  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8984  {
8985  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8986  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8987  return true;
8988  }
8989  }
8990  }
8991 }
8992 
8993 #if VMA_STATS_STRING_ENABLED
8994 
8995 // Correspond to values of enum VmaSuballocationType.
8996 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8997  "FREE",
8998  "UNKNOWN",
8999  "BUFFER",
9000  "IMAGE_UNKNOWN",
9001  "IMAGE_LINEAR",
9002  "IMAGE_OPTIMAL",
9003 };
9004 
9005 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
9006 {
9007  json.WriteString("Type");
9008  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
9009 
9010  json.WriteString("Size");
9011  json.WriteNumber(m_Size);
9012 
9013  if(m_pUserData != VMA_NULL)
9014  {
9015  json.WriteString("UserData");
9016  if(IsUserDataString())
9017  {
9018  json.WriteString((const char*)m_pUserData);
9019  }
9020  else
9021  {
9022  json.BeginString();
9023  json.ContinueString_Pointer(m_pUserData);
9024  json.EndString();
9025  }
9026  }
9027 
9028  json.WriteString("CreationFrameIndex");
9029  json.WriteNumber(m_CreationFrameIndex);
9030 
9031  json.WriteString("LastUseFrameIndex");
9032  json.WriteNumber(GetLastUseFrameIndex());
9033 
9034  if(m_BufferImageUsage != 0)
9035  {
9036  json.WriteString("Usage");
9037  json.WriteNumber(m_BufferImageUsage);
9038  }
9039 }
9040 
9041 #endif
9042 
9043 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
9044 {
9045  VMA_ASSERT(IsUserDataString());
9046  VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
9047  m_pUserData = VMA_NULL;
9048 }
9049 
9050 void VmaAllocation_T::BlockAllocMap()
9051 {
9052  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9053 
9054  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9055  {
9056  ++m_MapCount;
9057  }
9058  else
9059  {
9060  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
9061  }
9062 }
9063 
9064 void VmaAllocation_T::BlockAllocUnmap()
9065 {
9066  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
9067 
9068  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9069  {
9070  --m_MapCount;
9071  }
9072  else
9073  {
9074  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
9075  }
9076 }
9077 
9078 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
9079 {
9080  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9081 
9082  if(m_MapCount != 0)
9083  {
9084  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
9085  {
9086  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
9087  *ppData = m_DedicatedAllocation.m_pMappedData;
9088  ++m_MapCount;
9089  return VK_SUCCESS;
9090  }
9091  else
9092  {
9093  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
9094  return VK_ERROR_MEMORY_MAP_FAILED;
9095  }
9096  }
9097  else
9098  {
9099  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
9100  hAllocator->m_hDevice,
9101  m_DedicatedAllocation.m_hMemory,
9102  0, // offset
9103  VK_WHOLE_SIZE,
9104  0, // flags
9105  ppData);
9106  if(result == VK_SUCCESS)
9107  {
9108  m_DedicatedAllocation.m_pMappedData = *ppData;
9109  m_MapCount = 1;
9110  }
9111  return result;
9112  }
9113 }
9114 
9115 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
9116 {
9117  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
9118 
9119  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
9120  {
9121  --m_MapCount;
9122  if(m_MapCount == 0)
9123  {
9124  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
9125  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
9126  hAllocator->m_hDevice,
9127  m_DedicatedAllocation.m_hMemory);
9128  }
9129  }
9130  else
9131  {
9132  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
9133  }
9134 }
9135 
9136 #if VMA_STATS_STRING_ENABLED
9137 
9138 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
9139 {
9140  json.BeginObject();
9141 
9142  json.WriteString("Blocks");
9143  json.WriteNumber(stat.blockCount);
9144 
9145  json.WriteString("Allocations");
9146  json.WriteNumber(stat.allocationCount);
9147 
9148  json.WriteString("UnusedRanges");
9149  json.WriteNumber(stat.unusedRangeCount);
9150 
9151  json.WriteString("UsedBytes");
9152  json.WriteNumber(stat.usedBytes);
9153 
9154  json.WriteString("UnusedBytes");
9155  json.WriteNumber(stat.unusedBytes);
9156 
9157  if(stat.allocationCount > 1)
9158  {
9159  json.WriteString("AllocationSize");
9160  json.BeginObject(true);
9161  json.WriteString("Min");
9162  json.WriteNumber(stat.allocationSizeMin);
9163  json.WriteString("Avg");
9164  json.WriteNumber(stat.allocationSizeAvg);
9165  json.WriteString("Max");
9166  json.WriteNumber(stat.allocationSizeMax);
9167  json.EndObject();
9168  }
9169 
9170  if(stat.unusedRangeCount > 1)
9171  {
9172  json.WriteString("UnusedRangeSize");
9173  json.BeginObject(true);
9174  json.WriteString("Min");
9175  json.WriteNumber(stat.unusedRangeSizeMin);
9176  json.WriteString("Avg");
9177  json.WriteNumber(stat.unusedRangeSizeAvg);
9178  json.WriteString("Max");
9179  json.WriteNumber(stat.unusedRangeSizeMax);
9180  json.EndObject();
9181  }
9182 
9183  json.EndObject();
9184 }
9185 
9186 #endif // #if VMA_STATS_STRING_ENABLED
9187 
9188 struct VmaSuballocationItemSizeLess
9189 {
9190  bool operator()(
9191  const VmaSuballocationList::iterator lhs,
9192  const VmaSuballocationList::iterator rhs) const
9193  {
9194  return lhs->size < rhs->size;
9195  }
9196  bool operator()(
9197  const VmaSuballocationList::iterator lhs,
9198  VkDeviceSize rhsSize) const
9199  {
9200  return lhs->size < rhsSize;
9201  }
9202 };
9203 
9204 
9206 // class VmaBlockMetadata
9207 
9208 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
9209  m_Size(0),
9210  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
9211 {
9212 }
9213 
9214 #if VMA_STATS_STRING_ENABLED
9215 
9216 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
9217  VkDeviceSize unusedBytes,
9218  size_t allocationCount,
9219  size_t unusedRangeCount) const
9220 {
9221  json.BeginObject();
9222 
9223  json.WriteString("TotalBytes");
9224  json.WriteNumber(GetSize());
9225 
9226  json.WriteString("UnusedBytes");
9227  json.WriteNumber(unusedBytes);
9228 
9229  json.WriteString("Allocations");
9230  json.WriteNumber((uint64_t)allocationCount);
9231 
9232  json.WriteString("UnusedRanges");
9233  json.WriteNumber((uint64_t)unusedRangeCount);
9234 
9235  json.WriteString("Suballocations");
9236  json.BeginArray();
9237 }
9238 
9239 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
9240  VkDeviceSize offset,
9241  VmaAllocation hAllocation) const
9242 {
9243  json.BeginObject(true);
9244 
9245  json.WriteString("Offset");
9246  json.WriteNumber(offset);
9247 
9248  hAllocation->PrintParameters(json);
9249 
9250  json.EndObject();
9251 }
9252 
9253 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
9254  VkDeviceSize offset,
9255  VkDeviceSize size) const
9256 {
9257  json.BeginObject(true);
9258 
9259  json.WriteString("Offset");
9260  json.WriteNumber(offset);
9261 
9262  json.WriteString("Type");
9263  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
9264 
9265  json.WriteString("Size");
9266  json.WriteNumber(size);
9267 
9268  json.EndObject();
9269 }
9270 
9271 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
9272 {
9273  json.EndArray();
9274  json.EndObject();
9275 }
9276 
9277 #endif // #if VMA_STATS_STRING_ENABLED
9278 
9280 // class VmaBlockMetadata_Generic
9281 
9282 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
9283  VmaBlockMetadata(hAllocator),
9284  m_FreeCount(0),
9285  m_SumFreeSize(0),
9286  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9287  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
9288 {
9289 }
9290 
9291 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
9292 {
9293 }
9294 
9295 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
9296 {
9297  VmaBlockMetadata::Init(size);
9298 
9299  m_FreeCount = 1;
9300  m_SumFreeSize = size;
9301 
9302  VmaSuballocation suballoc = {};
9303  suballoc.offset = 0;
9304  suballoc.size = size;
9305  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9306  suballoc.hAllocation = VK_NULL_HANDLE;
9307 
9308  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9309  m_Suballocations.push_back(suballoc);
9310  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
9311  --suballocItem;
9312  m_FreeSuballocationsBySize.push_back(suballocItem);
9313 }
9314 
9315 bool VmaBlockMetadata_Generic::Validate() const
9316 {
9317  VMA_VALIDATE(!m_Suballocations.empty());
9318 
9319  // Expected offset of new suballocation as calculated from previous ones.
9320  VkDeviceSize calculatedOffset = 0;
9321  // Expected number of free suballocations as calculated from traversing their list.
9322  uint32_t calculatedFreeCount = 0;
9323  // Expected sum size of free suballocations as calculated from traversing their list.
9324  VkDeviceSize calculatedSumFreeSize = 0;
9325  // Expected number of free suballocations that should be registered in
9326  // m_FreeSuballocationsBySize calculated from traversing their list.
9327  size_t freeSuballocationsToRegister = 0;
9328  // True if previous visited suballocation was free.
9329  bool prevFree = false;
9330 
9331  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9332  suballocItem != m_Suballocations.cend();
9333  ++suballocItem)
9334  {
9335  const VmaSuballocation& subAlloc = *suballocItem;
9336 
9337  // Actual offset of this suballocation doesn't match expected one.
9338  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
9339 
9340  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
9341  // Two adjacent free suballocations are invalid. They should be merged.
9342  VMA_VALIDATE(!prevFree || !currFree);
9343 
9344  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
9345 
9346  if(currFree)
9347  {
9348  calculatedSumFreeSize += subAlloc.size;
9349  ++calculatedFreeCount;
9350  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9351  {
9352  ++freeSuballocationsToRegister;
9353  }
9354 
9355  // Margin required between allocations - every free space must be at least that large.
9356  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
9357  }
9358  else
9359  {
9360  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
9361  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
9362 
9363  // Margin required between allocations - previous allocation must be free.
9364  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
9365  }
9366 
9367  calculatedOffset += subAlloc.size;
9368  prevFree = currFree;
9369  }
9370 
9371  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9372  // match expected one.
9373  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9374 
9375  VkDeviceSize lastSize = 0;
9376  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9377  {
9378  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9379 
9380  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9381  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9382  // They must be sorted by size ascending.
9383  VMA_VALIDATE(suballocItem->size >= lastSize);
9384 
9385  lastSize = suballocItem->size;
9386  }
9387 
9388  // Check if totals match calculacted values.
9389  VMA_VALIDATE(ValidateFreeSuballocationList());
9390  VMA_VALIDATE(calculatedOffset == GetSize());
9391  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9392  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9393 
9394  return true;
9395 }
9396 
9397 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9398 {
9399  if(!m_FreeSuballocationsBySize.empty())
9400  {
9401  return m_FreeSuballocationsBySize.back()->size;
9402  }
9403  else
9404  {
9405  return 0;
9406  }
9407 }
9408 
9409 bool VmaBlockMetadata_Generic::IsEmpty() const
9410 {
9411  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9412 }
9413 
9414 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9415 {
9416  outInfo.blockCount = 1;
9417 
9418  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9419  outInfo.allocationCount = rangeCount - m_FreeCount;
9420  outInfo.unusedRangeCount = m_FreeCount;
9421 
9422  outInfo.unusedBytes = m_SumFreeSize;
9423  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9424 
9425  outInfo.allocationSizeMin = UINT64_MAX;
9426  outInfo.allocationSizeMax = 0;
9427  outInfo.unusedRangeSizeMin = UINT64_MAX;
9428  outInfo.unusedRangeSizeMax = 0;
9429 
9430  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9431  suballocItem != m_Suballocations.cend();
9432  ++suballocItem)
9433  {
9434  const VmaSuballocation& suballoc = *suballocItem;
9435  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9436  {
9437  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9438  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9439  }
9440  else
9441  {
9442  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9443  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9444  }
9445  }
9446 }
9447 
9448 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9449 {
9450  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9451 
9452  inoutStats.size += GetSize();
9453  inoutStats.unusedSize += m_SumFreeSize;
9454  inoutStats.allocationCount += rangeCount - m_FreeCount;
9455  inoutStats.unusedRangeCount += m_FreeCount;
9456  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9457 }
9458 
9459 #if VMA_STATS_STRING_ENABLED
9460 
9461 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9462 {
9463  PrintDetailedMap_Begin(json,
9464  m_SumFreeSize, // unusedBytes
9465  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9466  m_FreeCount); // unusedRangeCount
9467 
9468  size_t i = 0;
9469  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9470  suballocItem != m_Suballocations.cend();
9471  ++suballocItem, ++i)
9472  {
9473  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9474  {
9475  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9476  }
9477  else
9478  {
9479  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9480  }
9481  }
9482 
9483  PrintDetailedMap_End(json);
9484 }
9485 
9486 #endif // #if VMA_STATS_STRING_ENABLED
9487 
9488 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9489  uint32_t currentFrameIndex,
9490  uint32_t frameInUseCount,
9491  VkDeviceSize bufferImageGranularity,
9492  VkDeviceSize allocSize,
9493  VkDeviceSize allocAlignment,
9494  bool upperAddress,
9495  VmaSuballocationType allocType,
9496  bool canMakeOtherLost,
9497  uint32_t strategy,
9498  VmaAllocationRequest* pAllocationRequest)
9499 {
9500  VMA_ASSERT(allocSize > 0);
9501  VMA_ASSERT(!upperAddress);
9502  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9503  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9504  VMA_HEAVY_ASSERT(Validate());
9505 
9506  pAllocationRequest->type = VmaAllocationRequestType::Normal;
9507 
9508  // There is not enough total free space in this block to fullfill the request: Early return.
9509  if(canMakeOtherLost == false &&
9510  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9511  {
9512  return false;
9513  }
9514 
9515  // New algorithm, efficiently searching freeSuballocationsBySize.
9516  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9517  if(freeSuballocCount > 0)
9518  {
9520  {
9521  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9522  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9523  m_FreeSuballocationsBySize.data(),
9524  m_FreeSuballocationsBySize.data() + freeSuballocCount,
9525  allocSize + 2 * VMA_DEBUG_MARGIN,
9526  VmaSuballocationItemSizeLess());
9527  size_t index = it - m_FreeSuballocationsBySize.data();
9528  for(; index < freeSuballocCount; ++index)
9529  {
9530  if(CheckAllocation(
9531  currentFrameIndex,
9532  frameInUseCount,
9533  bufferImageGranularity,
9534  allocSize,
9535  allocAlignment,
9536  allocType,
9537  m_FreeSuballocationsBySize[index],
9538  false, // canMakeOtherLost
9539  &pAllocationRequest->offset,
9540  &pAllocationRequest->itemsToMakeLostCount,
9541  &pAllocationRequest->sumFreeSize,
9542  &pAllocationRequest->sumItemSize))
9543  {
9544  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9545  return true;
9546  }
9547  }
9548  }
9549  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9550  {
9551  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9552  it != m_Suballocations.end();
9553  ++it)
9554  {
9555  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9556  currentFrameIndex,
9557  frameInUseCount,
9558  bufferImageGranularity,
9559  allocSize,
9560  allocAlignment,
9561  allocType,
9562  it,
9563  false, // canMakeOtherLost
9564  &pAllocationRequest->offset,
9565  &pAllocationRequest->itemsToMakeLostCount,
9566  &pAllocationRequest->sumFreeSize,
9567  &pAllocationRequest->sumItemSize))
9568  {
9569  pAllocationRequest->item = it;
9570  return true;
9571  }
9572  }
9573  }
9574  else // WORST_FIT, FIRST_FIT
9575  {
9576  // Search staring from biggest suballocations.
9577  for(size_t index = freeSuballocCount; index--; )
9578  {
9579  if(CheckAllocation(
9580  currentFrameIndex,
9581  frameInUseCount,
9582  bufferImageGranularity,
9583  allocSize,
9584  allocAlignment,
9585  allocType,
9586  m_FreeSuballocationsBySize[index],
9587  false, // canMakeOtherLost
9588  &pAllocationRequest->offset,
9589  &pAllocationRequest->itemsToMakeLostCount,
9590  &pAllocationRequest->sumFreeSize,
9591  &pAllocationRequest->sumItemSize))
9592  {
9593  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9594  return true;
9595  }
9596  }
9597  }
9598  }
9599 
9600  if(canMakeOtherLost)
9601  {
9602  // Brute-force algorithm. TODO: Come up with something better.
9603 
9604  bool found = false;
9605  VmaAllocationRequest tmpAllocRequest = {};
9606  tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9607  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9608  suballocIt != m_Suballocations.end();
9609  ++suballocIt)
9610  {
9611  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9612  suballocIt->hAllocation->CanBecomeLost())
9613  {
9614  if(CheckAllocation(
9615  currentFrameIndex,
9616  frameInUseCount,
9617  bufferImageGranularity,
9618  allocSize,
9619  allocAlignment,
9620  allocType,
9621  suballocIt,
9622  canMakeOtherLost,
9623  &tmpAllocRequest.offset,
9624  &tmpAllocRequest.itemsToMakeLostCount,
9625  &tmpAllocRequest.sumFreeSize,
9626  &tmpAllocRequest.sumItemSize))
9627  {
9629  {
9630  *pAllocationRequest = tmpAllocRequest;
9631  pAllocationRequest->item = suballocIt;
9632  break;
9633  }
9634  if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9635  {
9636  *pAllocationRequest = tmpAllocRequest;
9637  pAllocationRequest->item = suballocIt;
9638  found = true;
9639  }
9640  }
9641  }
9642  }
9643 
9644  return found;
9645  }
9646 
9647  return false;
9648 }
9649 
9650 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9651  uint32_t currentFrameIndex,
9652  uint32_t frameInUseCount,
9653  VmaAllocationRequest* pAllocationRequest)
9654 {
9655  VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9656 
9657  while(pAllocationRequest->itemsToMakeLostCount > 0)
9658  {
9659  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9660  {
9661  ++pAllocationRequest->item;
9662  }
9663  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9664  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9665  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9666  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9667  {
9668  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9669  --pAllocationRequest->itemsToMakeLostCount;
9670  }
9671  else
9672  {
9673  return false;
9674  }
9675  }
9676 
9677  VMA_HEAVY_ASSERT(Validate());
9678  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9679  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9680 
9681  return true;
9682 }
9683 
9684 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9685 {
9686  uint32_t lostAllocationCount = 0;
9687  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9688  it != m_Suballocations.end();
9689  ++it)
9690  {
9691  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9692  it->hAllocation->CanBecomeLost() &&
9693  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9694  {
9695  it = FreeSuballocation(it);
9696  ++lostAllocationCount;
9697  }
9698  }
9699  return lostAllocationCount;
9700 }
9701 
9702 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9703 {
9704  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9705  it != m_Suballocations.end();
9706  ++it)
9707  {
9708  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9709  {
9710  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9711  {
9712  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9713  return VK_ERROR_VALIDATION_FAILED_EXT;
9714  }
9715  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9716  {
9717  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9718  return VK_ERROR_VALIDATION_FAILED_EXT;
9719  }
9720  }
9721  }
9722 
9723  return VK_SUCCESS;
9724 }
9725 
9726 void VmaBlockMetadata_Generic::Alloc(
9727  const VmaAllocationRequest& request,
9728  VmaSuballocationType type,
9729  VkDeviceSize allocSize,
9730  VmaAllocation hAllocation)
9731 {
9732  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9733  VMA_ASSERT(request.item != m_Suballocations.end());
9734  VmaSuballocation& suballoc = *request.item;
9735  // Given suballocation is a free block.
9736  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9737  // Given offset is inside this suballocation.
9738  VMA_ASSERT(request.offset >= suballoc.offset);
9739  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9740  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9741  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9742 
9743  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9744  // it to become used.
9745  UnregisterFreeSuballocation(request.item);
9746 
9747  suballoc.offset = request.offset;
9748  suballoc.size = allocSize;
9749  suballoc.type = type;
9750  suballoc.hAllocation = hAllocation;
9751 
9752  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9753  if(paddingEnd)
9754  {
9755  VmaSuballocation paddingSuballoc = {};
9756  paddingSuballoc.offset = request.offset + allocSize;
9757  paddingSuballoc.size = paddingEnd;
9758  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9759  VmaSuballocationList::iterator next = request.item;
9760  ++next;
9761  const VmaSuballocationList::iterator paddingEndItem =
9762  m_Suballocations.insert(next, paddingSuballoc);
9763  RegisterFreeSuballocation(paddingEndItem);
9764  }
9765 
9766  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9767  if(paddingBegin)
9768  {
9769  VmaSuballocation paddingSuballoc = {};
9770  paddingSuballoc.offset = request.offset - paddingBegin;
9771  paddingSuballoc.size = paddingBegin;
9772  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9773  const VmaSuballocationList::iterator paddingBeginItem =
9774  m_Suballocations.insert(request.item, paddingSuballoc);
9775  RegisterFreeSuballocation(paddingBeginItem);
9776  }
9777 
9778  // Update totals.
9779  m_FreeCount = m_FreeCount - 1;
9780  if(paddingBegin > 0)
9781  {
9782  ++m_FreeCount;
9783  }
9784  if(paddingEnd > 0)
9785  {
9786  ++m_FreeCount;
9787  }
9788  m_SumFreeSize -= allocSize;
9789 }
9790 
9791 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9792 {
9793  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9794  suballocItem != m_Suballocations.end();
9795  ++suballocItem)
9796  {
9797  VmaSuballocation& suballoc = *suballocItem;
9798  if(suballoc.hAllocation == allocation)
9799  {
9800  FreeSuballocation(suballocItem);
9801  VMA_HEAVY_ASSERT(Validate());
9802  return;
9803  }
9804  }
9805  VMA_ASSERT(0 && "Not found!");
9806 }
9807 
9808 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9809 {
9810  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9811  suballocItem != m_Suballocations.end();
9812  ++suballocItem)
9813  {
9814  VmaSuballocation& suballoc = *suballocItem;
9815  if(suballoc.offset == offset)
9816  {
9817  FreeSuballocation(suballocItem);
9818  return;
9819  }
9820  }
9821  VMA_ASSERT(0 && "Not found!");
9822 }
9823 
9824 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9825 {
9826  VkDeviceSize lastSize = 0;
9827  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9828  {
9829  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9830 
9831  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9832  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9833  VMA_VALIDATE(it->size >= lastSize);
9834  lastSize = it->size;
9835  }
9836  return true;
9837 }
9838 
9839 bool VmaBlockMetadata_Generic::CheckAllocation(
9840  uint32_t currentFrameIndex,
9841  uint32_t frameInUseCount,
9842  VkDeviceSize bufferImageGranularity,
9843  VkDeviceSize allocSize,
9844  VkDeviceSize allocAlignment,
9845  VmaSuballocationType allocType,
9846  VmaSuballocationList::const_iterator suballocItem,
9847  bool canMakeOtherLost,
9848  VkDeviceSize* pOffset,
9849  size_t* itemsToMakeLostCount,
9850  VkDeviceSize* pSumFreeSize,
9851  VkDeviceSize* pSumItemSize) const
9852 {
9853  VMA_ASSERT(allocSize > 0);
9854  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9855  VMA_ASSERT(suballocItem != m_Suballocations.cend());
9856  VMA_ASSERT(pOffset != VMA_NULL);
9857 
9858  *itemsToMakeLostCount = 0;
9859  *pSumFreeSize = 0;
9860  *pSumItemSize = 0;
9861 
9862  if(canMakeOtherLost)
9863  {
9864  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9865  {
9866  *pSumFreeSize = suballocItem->size;
9867  }
9868  else
9869  {
9870  if(suballocItem->hAllocation->CanBecomeLost() &&
9871  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9872  {
9873  ++*itemsToMakeLostCount;
9874  *pSumItemSize = suballocItem->size;
9875  }
9876  else
9877  {
9878  return false;
9879  }
9880  }
9881 
9882  // Remaining size is too small for this request: Early return.
9883  if(GetSize() - suballocItem->offset < allocSize)
9884  {
9885  return false;
9886  }
9887 
9888  // Start from offset equal to beginning of this suballocation.
9889  *pOffset = suballocItem->offset;
9890 
9891  // Apply VMA_DEBUG_MARGIN at the beginning.
9892  if(VMA_DEBUG_MARGIN > 0)
9893  {
9894  *pOffset += VMA_DEBUG_MARGIN;
9895  }
9896 
9897  // Apply alignment.
9898  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9899 
9900  // Check previous suballocations for BufferImageGranularity conflicts.
9901  // Make bigger alignment if necessary.
9902  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
9903  {
9904  bool bufferImageGranularityConflict = false;
9905  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9906  while(prevSuballocItem != m_Suballocations.cbegin())
9907  {
9908  --prevSuballocItem;
9909  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9910  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9911  {
9912  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9913  {
9914  bufferImageGranularityConflict = true;
9915  break;
9916  }
9917  }
9918  else
9919  // Already on previous page.
9920  break;
9921  }
9922  if(bufferImageGranularityConflict)
9923  {
9924  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9925  }
9926  }
9927 
9928  // Now that we have final *pOffset, check if we are past suballocItem.
9929  // If yes, return false - this function should be called for another suballocItem as starting point.
9930  if(*pOffset >= suballocItem->offset + suballocItem->size)
9931  {
9932  return false;
9933  }
9934 
9935  // Calculate padding at the beginning based on current offset.
9936  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9937 
9938  // Calculate required margin at the end.
9939  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9940 
9941  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9942  // Another early return check.
9943  if(suballocItem->offset + totalSize > GetSize())
9944  {
9945  return false;
9946  }
9947 
9948  // Advance lastSuballocItem until desired size is reached.
9949  // Update itemsToMakeLostCount.
9950  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9951  if(totalSize > suballocItem->size)
9952  {
9953  VkDeviceSize remainingSize = totalSize - suballocItem->size;
9954  while(remainingSize > 0)
9955  {
9956  ++lastSuballocItem;
9957  if(lastSuballocItem == m_Suballocations.cend())
9958  {
9959  return false;
9960  }
9961  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9962  {
9963  *pSumFreeSize += lastSuballocItem->size;
9964  }
9965  else
9966  {
9967  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9968  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9969  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9970  {
9971  ++*itemsToMakeLostCount;
9972  *pSumItemSize += lastSuballocItem->size;
9973  }
9974  else
9975  {
9976  return false;
9977  }
9978  }
9979  remainingSize = (lastSuballocItem->size < remainingSize) ?
9980  remainingSize - lastSuballocItem->size : 0;
9981  }
9982  }
9983 
9984  // Check next suballocations for BufferImageGranularity conflicts.
9985  // If conflict exists, we must mark more allocations lost or fail.
9986  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
9987  {
9988  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9989  ++nextSuballocItem;
9990  while(nextSuballocItem != m_Suballocations.cend())
9991  {
9992  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9993  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9994  {
9995  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9996  {
9997  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9998  if(nextSuballoc.hAllocation->CanBecomeLost() &&
9999  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
10000  {
10001  ++*itemsToMakeLostCount;
10002  }
10003  else
10004  {
10005  return false;
10006  }
10007  }
10008  }
10009  else
10010  {
10011  // Already on next page.
10012  break;
10013  }
10014  ++nextSuballocItem;
10015  }
10016  }
10017  }
10018  else
10019  {
10020  const VmaSuballocation& suballoc = *suballocItem;
10021  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10022 
10023  *pSumFreeSize = suballoc.size;
10024 
10025  // Size of this suballocation is too small for this request: Early return.
10026  if(suballoc.size < allocSize)
10027  {
10028  return false;
10029  }
10030 
10031  // Start from offset equal to beginning of this suballocation.
10032  *pOffset = suballoc.offset;
10033 
10034  // Apply VMA_DEBUG_MARGIN at the beginning.
10035  if(VMA_DEBUG_MARGIN > 0)
10036  {
10037  *pOffset += VMA_DEBUG_MARGIN;
10038  }
10039 
10040  // Apply alignment.
10041  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
10042 
10043  // Check previous suballocations for BufferImageGranularity conflicts.
10044  // Make bigger alignment if necessary.
10045  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
10046  {
10047  bool bufferImageGranularityConflict = false;
10048  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
10049  while(prevSuballocItem != m_Suballocations.cbegin())
10050  {
10051  --prevSuballocItem;
10052  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
10053  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
10054  {
10055  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10056  {
10057  bufferImageGranularityConflict = true;
10058  break;
10059  }
10060  }
10061  else
10062  // Already on previous page.
10063  break;
10064  }
10065  if(bufferImageGranularityConflict)
10066  {
10067  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
10068  }
10069  }
10070 
10071  // Calculate padding at the beginning based on current offset.
10072  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
10073 
10074  // Calculate required margin at the end.
10075  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
10076 
10077  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
10078  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
10079  {
10080  return false;
10081  }
10082 
10083  // Check next suballocations for BufferImageGranularity conflicts.
10084  // If conflict exists, allocation cannot be made here.
10085  if(allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity)
10086  {
10087  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
10088  ++nextSuballocItem;
10089  while(nextSuballocItem != m_Suballocations.cend())
10090  {
10091  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
10092  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10093  {
10094  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10095  {
10096  return false;
10097  }
10098  }
10099  else
10100  {
10101  // Already on next page.
10102  break;
10103  }
10104  ++nextSuballocItem;
10105  }
10106  }
10107  }
10108 
10109  // All tests passed: Success. pOffset is already filled.
10110  return true;
10111 }
10112 
10113 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
10114 {
10115  VMA_ASSERT(item != m_Suballocations.end());
10116  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10117 
10118  VmaSuballocationList::iterator nextItem = item;
10119  ++nextItem;
10120  VMA_ASSERT(nextItem != m_Suballocations.end());
10121  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
10122 
10123  item->size += nextItem->size;
10124  --m_FreeCount;
10125  m_Suballocations.erase(nextItem);
10126 }
10127 
10128 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
10129 {
10130  // Change this suballocation to be marked as free.
10131  VmaSuballocation& suballoc = *suballocItem;
10132  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10133  suballoc.hAllocation = VK_NULL_HANDLE;
10134 
10135  // Update totals.
10136  ++m_FreeCount;
10137  m_SumFreeSize += suballoc.size;
10138 
10139  // Merge with previous and/or next suballocation if it's also free.
10140  bool mergeWithNext = false;
10141  bool mergeWithPrev = false;
10142 
10143  VmaSuballocationList::iterator nextItem = suballocItem;
10144  ++nextItem;
10145  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
10146  {
10147  mergeWithNext = true;
10148  }
10149 
10150  VmaSuballocationList::iterator prevItem = suballocItem;
10151  if(suballocItem != m_Suballocations.begin())
10152  {
10153  --prevItem;
10154  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
10155  {
10156  mergeWithPrev = true;
10157  }
10158  }
10159 
10160  if(mergeWithNext)
10161  {
10162  UnregisterFreeSuballocation(nextItem);
10163  MergeFreeWithNext(suballocItem);
10164  }
10165 
10166  if(mergeWithPrev)
10167  {
10168  UnregisterFreeSuballocation(prevItem);
10169  MergeFreeWithNext(prevItem);
10170  RegisterFreeSuballocation(prevItem);
10171  return prevItem;
10172  }
10173  else
10174  {
10175  RegisterFreeSuballocation(suballocItem);
10176  return suballocItem;
10177  }
10178 }
10179 
10180 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
10181 {
10182  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10183  VMA_ASSERT(item->size > 0);
10184 
10185  // You may want to enable this validation at the beginning or at the end of
10186  // this function, depending on what do you want to check.
10187  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10188 
10189  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10190  {
10191  if(m_FreeSuballocationsBySize.empty())
10192  {
10193  m_FreeSuballocationsBySize.push_back(item);
10194  }
10195  else
10196  {
10197  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
10198  }
10199  }
10200 
10201  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10202 }
10203 
10204 
10205 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
10206 {
10207  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
10208  VMA_ASSERT(item->size > 0);
10209 
10210  // You may want to enable this validation at the beginning or at the end of
10211  // this function, depending on what do you want to check.
10212  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10213 
10214  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
10215  {
10216  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
10217  m_FreeSuballocationsBySize.data(),
10218  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
10219  item,
10220  VmaSuballocationItemSizeLess());
10221  for(size_t index = it - m_FreeSuballocationsBySize.data();
10222  index < m_FreeSuballocationsBySize.size();
10223  ++index)
10224  {
10225  if(m_FreeSuballocationsBySize[index] == item)
10226  {
10227  VmaVectorRemove(m_FreeSuballocationsBySize, index);
10228  return;
10229  }
10230  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
10231  }
10232  VMA_ASSERT(0 && "Not found.");
10233  }
10234 
10235  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
10236 }
10237 
10238 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
10239  VkDeviceSize bufferImageGranularity,
10240  VmaSuballocationType& inOutPrevSuballocType) const
10241 {
10242  if(bufferImageGranularity == 1 || IsEmpty())
10243  {
10244  return false;
10245  }
10246 
10247  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
10248  bool typeConflictFound = false;
10249  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
10250  it != m_Suballocations.cend();
10251  ++it)
10252  {
10253  const VmaSuballocationType suballocType = it->type;
10254  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
10255  {
10256  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
10257  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
10258  {
10259  typeConflictFound = true;
10260  }
10261  inOutPrevSuballocType = suballocType;
10262  }
10263  }
10264 
10265  return typeConflictFound || minAlignment >= bufferImageGranularity;
10266 }
10267 
10269 // class VmaBlockMetadata_Linear
10270 
10271 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
10272  VmaBlockMetadata(hAllocator),
10273  m_SumFreeSize(0),
10274  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10275  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
10276  m_1stVectorIndex(0),
10277  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
10278  m_1stNullItemsBeginCount(0),
10279  m_1stNullItemsMiddleCount(0),
10280  m_2ndNullItemsCount(0)
10281 {
10282 }
10283 
10284 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
10285 {
10286 }
10287 
10288 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
10289 {
10290  VmaBlockMetadata::Init(size);
10291  m_SumFreeSize = size;
10292 }
10293 
10294 bool VmaBlockMetadata_Linear::Validate() const
10295 {
10296  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10297  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10298 
10299  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
10300  VMA_VALIDATE(!suballocations1st.empty() ||
10301  suballocations2nd.empty() ||
10302  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
10303 
10304  if(!suballocations1st.empty())
10305  {
10306  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
10307  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
10308  // Null item at the end should be just pop_back().
10309  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
10310  }
10311  if(!suballocations2nd.empty())
10312  {
10313  // Null item at the end should be just pop_back().
10314  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
10315  }
10316 
10317  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
10318  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
10319 
10320  VkDeviceSize sumUsedSize = 0;
10321  const size_t suballoc1stCount = suballocations1st.size();
10322  VkDeviceSize offset = VMA_DEBUG_MARGIN;
10323 
10324  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10325  {
10326  const size_t suballoc2ndCount = suballocations2nd.size();
10327  size_t nullItem2ndCount = 0;
10328  for(size_t i = 0; i < suballoc2ndCount; ++i)
10329  {
10330  const VmaSuballocation& suballoc = suballocations2nd[i];
10331  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10332 
10333  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10334  VMA_VALIDATE(suballoc.offset >= offset);
10335 
10336  if(!currFree)
10337  {
10338  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10339  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10340  sumUsedSize += suballoc.size;
10341  }
10342  else
10343  {
10344  ++nullItem2ndCount;
10345  }
10346 
10347  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10348  }
10349 
10350  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10351  }
10352 
10353  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
10354  {
10355  const VmaSuballocation& suballoc = suballocations1st[i];
10356  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
10357  suballoc.hAllocation == VK_NULL_HANDLE);
10358  }
10359 
10360  size_t nullItem1stCount = m_1stNullItemsBeginCount;
10361 
10362  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
10363  {
10364  const VmaSuballocation& suballoc = suballocations1st[i];
10365  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10366 
10367  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10368  VMA_VALIDATE(suballoc.offset >= offset);
10369  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10370 
10371  if(!currFree)
10372  {
10373  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10374  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10375  sumUsedSize += suballoc.size;
10376  }
10377  else
10378  {
10379  ++nullItem1stCount;
10380  }
10381 
10382  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10383  }
10384  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10385 
10386  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10387  {
10388  const size_t suballoc2ndCount = suballocations2nd.size();
10389  size_t nullItem2ndCount = 0;
10390  for(size_t i = suballoc2ndCount; i--; )
10391  {
10392  const VmaSuballocation& suballoc = suballocations2nd[i];
10393  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10394 
10395  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10396  VMA_VALIDATE(suballoc.offset >= offset);
10397 
10398  if(!currFree)
10399  {
10400  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10401  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10402  sumUsedSize += suballoc.size;
10403  }
10404  else
10405  {
10406  ++nullItem2ndCount;
10407  }
10408 
10409  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10410  }
10411 
10412  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10413  }
10414 
10415  VMA_VALIDATE(offset <= GetSize());
10416  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10417 
10418  return true;
10419 }
10420 
10421 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10422 {
10423  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10424  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10425 }
10426 
10427 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10428 {
10429  const VkDeviceSize size = GetSize();
10430 
10431  /*
10432  We don't consider gaps inside allocation vectors with freed allocations because
10433  they are not suitable for reuse in linear allocator. We consider only space that
10434  is available for new allocations.
10435  */
10436  if(IsEmpty())
10437  {
10438  return size;
10439  }
10440 
10441  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10442 
10443  switch(m_2ndVectorMode)
10444  {
10445  case SECOND_VECTOR_EMPTY:
10446  /*
10447  Available space is after end of 1st, as well as before beginning of 1st (which
10448  whould make it a ring buffer).
10449  */
10450  {
10451  const size_t suballocations1stCount = suballocations1st.size();
10452  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10453  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10454  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10455  return VMA_MAX(
10456  firstSuballoc.offset,
10457  size - (lastSuballoc.offset + lastSuballoc.size));
10458  }
10459  break;
10460 
10461  case SECOND_VECTOR_RING_BUFFER:
10462  /*
10463  Available space is only between end of 2nd and beginning of 1st.
10464  */
10465  {
10466  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10467  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10468  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10469  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10470  }
10471  break;
10472 
10473  case SECOND_VECTOR_DOUBLE_STACK:
10474  /*
10475  Available space is only between end of 1st and top of 2nd.
10476  */
10477  {
10478  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10479  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10480  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10481  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10482  }
10483  break;
10484 
10485  default:
10486  VMA_ASSERT(0);
10487  return 0;
10488  }
10489 }
10490 
10491 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10492 {
10493  const VkDeviceSize size = GetSize();
10494  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10495  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10496  const size_t suballoc1stCount = suballocations1st.size();
10497  const size_t suballoc2ndCount = suballocations2nd.size();
10498 
10499  outInfo.blockCount = 1;
10500  outInfo.allocationCount = (uint32_t)GetAllocationCount();
10501  outInfo.unusedRangeCount = 0;
10502  outInfo.usedBytes = 0;
10503  outInfo.allocationSizeMin = UINT64_MAX;
10504  outInfo.allocationSizeMax = 0;
10505  outInfo.unusedRangeSizeMin = UINT64_MAX;
10506  outInfo.unusedRangeSizeMax = 0;
10507 
10508  VkDeviceSize lastOffset = 0;
10509 
10510  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10511  {
10512  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10513  size_t nextAlloc2ndIndex = 0;
10514  while(lastOffset < freeSpace2ndTo1stEnd)
10515  {
10516  // Find next non-null allocation or move nextAllocIndex to the end.
10517  while(nextAlloc2ndIndex < suballoc2ndCount &&
10518  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10519  {
10520  ++nextAlloc2ndIndex;
10521  }
10522 
10523  // Found non-null allocation.
10524  if(nextAlloc2ndIndex < suballoc2ndCount)
10525  {
10526  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10527 
10528  // 1. Process free space before this allocation.
10529  if(lastOffset < suballoc.offset)
10530  {
10531  // There is free space from lastOffset to suballoc.offset.
10532  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10533  ++outInfo.unusedRangeCount;
10534  outInfo.unusedBytes += unusedRangeSize;
10535  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10536  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10537  }
10538 
10539  // 2. Process this allocation.
10540  // There is allocation with suballoc.offset, suballoc.size.
10541  outInfo.usedBytes += suballoc.size;
10542  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10543  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10544 
10545  // 3. Prepare for next iteration.
10546  lastOffset = suballoc.offset + suballoc.size;
10547  ++nextAlloc2ndIndex;
10548  }
10549  // We are at the end.
10550  else
10551  {
10552  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10553  if(lastOffset < freeSpace2ndTo1stEnd)
10554  {
10555  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10556  ++outInfo.unusedRangeCount;
10557  outInfo.unusedBytes += unusedRangeSize;
10558  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10559  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10560  }
10561 
10562  // End of loop.
10563  lastOffset = freeSpace2ndTo1stEnd;
10564  }
10565  }
10566  }
10567 
10568  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10569  const VkDeviceSize freeSpace1stTo2ndEnd =
10570  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10571  while(lastOffset < freeSpace1stTo2ndEnd)
10572  {
10573  // Find next non-null allocation or move nextAllocIndex to the end.
10574  while(nextAlloc1stIndex < suballoc1stCount &&
10575  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10576  {
10577  ++nextAlloc1stIndex;
10578  }
10579 
10580  // Found non-null allocation.
10581  if(nextAlloc1stIndex < suballoc1stCount)
10582  {
10583  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10584 
10585  // 1. Process free space before this allocation.
10586  if(lastOffset < suballoc.offset)
10587  {
10588  // There is free space from lastOffset to suballoc.offset.
10589  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10590  ++outInfo.unusedRangeCount;
10591  outInfo.unusedBytes += unusedRangeSize;
10592  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10593  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10594  }
10595 
10596  // 2. Process this allocation.
10597  // There is allocation with suballoc.offset, suballoc.size.
10598  outInfo.usedBytes += suballoc.size;
10599  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10600  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10601 
10602  // 3. Prepare for next iteration.
10603  lastOffset = suballoc.offset + suballoc.size;
10604  ++nextAlloc1stIndex;
10605  }
10606  // We are at the end.
10607  else
10608  {
10609  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10610  if(lastOffset < freeSpace1stTo2ndEnd)
10611  {
10612  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10613  ++outInfo.unusedRangeCount;
10614  outInfo.unusedBytes += unusedRangeSize;
10615  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10616  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10617  }
10618 
10619  // End of loop.
10620  lastOffset = freeSpace1stTo2ndEnd;
10621  }
10622  }
10623 
10624  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10625  {
10626  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10627  while(lastOffset < size)
10628  {
10629  // Find next non-null allocation or move nextAllocIndex to the end.
10630  while(nextAlloc2ndIndex != SIZE_MAX &&
10631  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10632  {
10633  --nextAlloc2ndIndex;
10634  }
10635 
10636  // Found non-null allocation.
10637  if(nextAlloc2ndIndex != SIZE_MAX)
10638  {
10639  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10640 
10641  // 1. Process free space before this allocation.
10642  if(lastOffset < suballoc.offset)
10643  {
10644  // There is free space from lastOffset to suballoc.offset.
10645  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10646  ++outInfo.unusedRangeCount;
10647  outInfo.unusedBytes += unusedRangeSize;
10648  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10649  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10650  }
10651 
10652  // 2. Process this allocation.
10653  // There is allocation with suballoc.offset, suballoc.size.
10654  outInfo.usedBytes += suballoc.size;
10655  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10656  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10657 
10658  // 3. Prepare for next iteration.
10659  lastOffset = suballoc.offset + suballoc.size;
10660  --nextAlloc2ndIndex;
10661  }
10662  // We are at the end.
10663  else
10664  {
10665  // There is free space from lastOffset to size.
10666  if(lastOffset < size)
10667  {
10668  const VkDeviceSize unusedRangeSize = size - lastOffset;
10669  ++outInfo.unusedRangeCount;
10670  outInfo.unusedBytes += unusedRangeSize;
10671  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10672  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10673  }
10674 
10675  // End of loop.
10676  lastOffset = size;
10677  }
10678  }
10679  }
10680 
10681  outInfo.unusedBytes = size - outInfo.usedBytes;
10682 }
10683 
10684 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10685 {
10686  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10687  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10688  const VkDeviceSize size = GetSize();
10689  const size_t suballoc1stCount = suballocations1st.size();
10690  const size_t suballoc2ndCount = suballocations2nd.size();
10691 
10692  inoutStats.size += size;
10693 
10694  VkDeviceSize lastOffset = 0;
10695 
10696  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10697  {
10698  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10699  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10700  while(lastOffset < freeSpace2ndTo1stEnd)
10701  {
10702  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10703  while(nextAlloc2ndIndex < suballoc2ndCount &&
10704  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10705  {
10706  ++nextAlloc2ndIndex;
10707  }
10708 
10709  // Found non-null allocation.
10710  if(nextAlloc2ndIndex < suballoc2ndCount)
10711  {
10712  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10713 
10714  // 1. Process free space before this allocation.
10715  if(lastOffset < suballoc.offset)
10716  {
10717  // There is free space from lastOffset to suballoc.offset.
10718  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10719  inoutStats.unusedSize += unusedRangeSize;
10720  ++inoutStats.unusedRangeCount;
10721  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10722  }
10723 
10724  // 2. Process this allocation.
10725  // There is allocation with suballoc.offset, suballoc.size.
10726  ++inoutStats.allocationCount;
10727 
10728  // 3. Prepare for next iteration.
10729  lastOffset = suballoc.offset + suballoc.size;
10730  ++nextAlloc2ndIndex;
10731  }
10732  // We are at the end.
10733  else
10734  {
10735  if(lastOffset < freeSpace2ndTo1stEnd)
10736  {
10737  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10738  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10739  inoutStats.unusedSize += unusedRangeSize;
10740  ++inoutStats.unusedRangeCount;
10741  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10742  }
10743 
10744  // End of loop.
10745  lastOffset = freeSpace2ndTo1stEnd;
10746  }
10747  }
10748  }
10749 
10750  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10751  const VkDeviceSize freeSpace1stTo2ndEnd =
10752  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10753  while(lastOffset < freeSpace1stTo2ndEnd)
10754  {
10755  // Find next non-null allocation or move nextAllocIndex to the end.
10756  while(nextAlloc1stIndex < suballoc1stCount &&
10757  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10758  {
10759  ++nextAlloc1stIndex;
10760  }
10761 
10762  // Found non-null allocation.
10763  if(nextAlloc1stIndex < suballoc1stCount)
10764  {
10765  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10766 
10767  // 1. Process free space before this allocation.
10768  if(lastOffset < suballoc.offset)
10769  {
10770  // There is free space from lastOffset to suballoc.offset.
10771  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10772  inoutStats.unusedSize += unusedRangeSize;
10773  ++inoutStats.unusedRangeCount;
10774  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10775  }
10776 
10777  // 2. Process this allocation.
10778  // There is allocation with suballoc.offset, suballoc.size.
10779  ++inoutStats.allocationCount;
10780 
10781  // 3. Prepare for next iteration.
10782  lastOffset = suballoc.offset + suballoc.size;
10783  ++nextAlloc1stIndex;
10784  }
10785  // We are at the end.
10786  else
10787  {
10788  if(lastOffset < freeSpace1stTo2ndEnd)
10789  {
10790  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10791  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10792  inoutStats.unusedSize += unusedRangeSize;
10793  ++inoutStats.unusedRangeCount;
10794  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10795  }
10796 
10797  // End of loop.
10798  lastOffset = freeSpace1stTo2ndEnd;
10799  }
10800  }
10801 
10802  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10803  {
10804  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10805  while(lastOffset < size)
10806  {
10807  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10808  while(nextAlloc2ndIndex != SIZE_MAX &&
10809  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10810  {
10811  --nextAlloc2ndIndex;
10812  }
10813 
10814  // Found non-null allocation.
10815  if(nextAlloc2ndIndex != SIZE_MAX)
10816  {
10817  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10818 
10819  // 1. Process free space before this allocation.
10820  if(lastOffset < suballoc.offset)
10821  {
10822  // There is free space from lastOffset to suballoc.offset.
10823  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10824  inoutStats.unusedSize += unusedRangeSize;
10825  ++inoutStats.unusedRangeCount;
10826  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10827  }
10828 
10829  // 2. Process this allocation.
10830  // There is allocation with suballoc.offset, suballoc.size.
10831  ++inoutStats.allocationCount;
10832 
10833  // 3. Prepare for next iteration.
10834  lastOffset = suballoc.offset + suballoc.size;
10835  --nextAlloc2ndIndex;
10836  }
10837  // We are at the end.
10838  else
10839  {
10840  if(lastOffset < size)
10841  {
10842  // There is free space from lastOffset to size.
10843  const VkDeviceSize unusedRangeSize = size - lastOffset;
10844  inoutStats.unusedSize += unusedRangeSize;
10845  ++inoutStats.unusedRangeCount;
10846  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10847  }
10848 
10849  // End of loop.
10850  lastOffset = size;
10851  }
10852  }
10853  }
10854 }
10855 
10856 #if VMA_STATS_STRING_ENABLED
10857 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10858 {
10859  const VkDeviceSize size = GetSize();
10860  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10861  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10862  const size_t suballoc1stCount = suballocations1st.size();
10863  const size_t suballoc2ndCount = suballocations2nd.size();
10864 
10865  // FIRST PASS
10866 
10867  size_t unusedRangeCount = 0;
10868  VkDeviceSize usedBytes = 0;
10869 
10870  VkDeviceSize lastOffset = 0;
10871 
10872  size_t alloc2ndCount = 0;
10873  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10874  {
10875  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10876  size_t nextAlloc2ndIndex = 0;
10877  while(lastOffset < freeSpace2ndTo1stEnd)
10878  {
10879  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10880  while(nextAlloc2ndIndex < suballoc2ndCount &&
10881  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10882  {
10883  ++nextAlloc2ndIndex;
10884  }
10885 
10886  // Found non-null allocation.
10887  if(nextAlloc2ndIndex < suballoc2ndCount)
10888  {
10889  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10890 
10891  // 1. Process free space before this allocation.
10892  if(lastOffset < suballoc.offset)
10893  {
10894  // There is free space from lastOffset to suballoc.offset.
10895  ++unusedRangeCount;
10896  }
10897 
10898  // 2. Process this allocation.
10899  // There is allocation with suballoc.offset, suballoc.size.
10900  ++alloc2ndCount;
10901  usedBytes += suballoc.size;
10902 
10903  // 3. Prepare for next iteration.
10904  lastOffset = suballoc.offset + suballoc.size;
10905  ++nextAlloc2ndIndex;
10906  }
10907  // We are at the end.
10908  else
10909  {
10910  if(lastOffset < freeSpace2ndTo1stEnd)
10911  {
10912  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10913  ++unusedRangeCount;
10914  }
10915 
10916  // End of loop.
10917  lastOffset = freeSpace2ndTo1stEnd;
10918  }
10919  }
10920  }
10921 
10922  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10923  size_t alloc1stCount = 0;
10924  const VkDeviceSize freeSpace1stTo2ndEnd =
10925  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10926  while(lastOffset < freeSpace1stTo2ndEnd)
10927  {
10928  // Find next non-null allocation or move nextAllocIndex to the end.
10929  while(nextAlloc1stIndex < suballoc1stCount &&
10930  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10931  {
10932  ++nextAlloc1stIndex;
10933  }
10934 
10935  // Found non-null allocation.
10936  if(nextAlloc1stIndex < suballoc1stCount)
10937  {
10938  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10939 
10940  // 1. Process free space before this allocation.
10941  if(lastOffset < suballoc.offset)
10942  {
10943  // There is free space from lastOffset to suballoc.offset.
10944  ++unusedRangeCount;
10945  }
10946 
10947  // 2. Process this allocation.
10948  // There is allocation with suballoc.offset, suballoc.size.
10949  ++alloc1stCount;
10950  usedBytes += suballoc.size;
10951 
10952  // 3. Prepare for next iteration.
10953  lastOffset = suballoc.offset + suballoc.size;
10954  ++nextAlloc1stIndex;
10955  }
10956  // We are at the end.
10957  else
10958  {
10959  if(lastOffset < size)
10960  {
10961  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10962  ++unusedRangeCount;
10963  }
10964 
10965  // End of loop.
10966  lastOffset = freeSpace1stTo2ndEnd;
10967  }
10968  }
10969 
10970  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10971  {
10972  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10973  while(lastOffset < size)
10974  {
10975  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10976  while(nextAlloc2ndIndex != SIZE_MAX &&
10977  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10978  {
10979  --nextAlloc2ndIndex;
10980  }
10981 
10982  // Found non-null allocation.
10983  if(nextAlloc2ndIndex != SIZE_MAX)
10984  {
10985  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10986 
10987  // 1. Process free space before this allocation.
10988  if(lastOffset < suballoc.offset)
10989  {
10990  // There is free space from lastOffset to suballoc.offset.
10991  ++unusedRangeCount;
10992  }
10993 
10994  // 2. Process this allocation.
10995  // There is allocation with suballoc.offset, suballoc.size.
10996  ++alloc2ndCount;
10997  usedBytes += suballoc.size;
10998 
10999  // 3. Prepare for next iteration.
11000  lastOffset = suballoc.offset + suballoc.size;
11001  --nextAlloc2ndIndex;
11002  }
11003  // We are at the end.
11004  else
11005  {
11006  if(lastOffset < size)
11007  {
11008  // There is free space from lastOffset to size.
11009  ++unusedRangeCount;
11010  }
11011 
11012  // End of loop.
11013  lastOffset = size;
11014  }
11015  }
11016  }
11017 
11018  const VkDeviceSize unusedBytes = size - usedBytes;
11019  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
11020 
11021  // SECOND PASS
11022  lastOffset = 0;
11023 
11024  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11025  {
11026  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
11027  size_t nextAlloc2ndIndex = 0;
11028  while(lastOffset < freeSpace2ndTo1stEnd)
11029  {
11030  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11031  while(nextAlloc2ndIndex < suballoc2ndCount &&
11032  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11033  {
11034  ++nextAlloc2ndIndex;
11035  }
11036 
11037  // Found non-null allocation.
11038  if(nextAlloc2ndIndex < suballoc2ndCount)
11039  {
11040  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11041 
11042  // 1. Process free space before this allocation.
11043  if(lastOffset < suballoc.offset)
11044  {
11045  // There is free space from lastOffset to suballoc.offset.
11046  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11047  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11048  }
11049 
11050  // 2. Process this allocation.
11051  // There is allocation with suballoc.offset, suballoc.size.
11052  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11053 
11054  // 3. Prepare for next iteration.
11055  lastOffset = suballoc.offset + suballoc.size;
11056  ++nextAlloc2ndIndex;
11057  }
11058  // We are at the end.
11059  else
11060  {
11061  if(lastOffset < freeSpace2ndTo1stEnd)
11062  {
11063  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
11064  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
11065  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11066  }
11067 
11068  // End of loop.
11069  lastOffset = freeSpace2ndTo1stEnd;
11070  }
11071  }
11072  }
11073 
11074  nextAlloc1stIndex = m_1stNullItemsBeginCount;
11075  while(lastOffset < freeSpace1stTo2ndEnd)
11076  {
11077  // Find next non-null allocation or move nextAllocIndex to the end.
11078  while(nextAlloc1stIndex < suballoc1stCount &&
11079  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
11080  {
11081  ++nextAlloc1stIndex;
11082  }
11083 
11084  // Found non-null allocation.
11085  if(nextAlloc1stIndex < suballoc1stCount)
11086  {
11087  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
11088 
11089  // 1. Process free space before this allocation.
11090  if(lastOffset < suballoc.offset)
11091  {
11092  // There is free space from lastOffset to suballoc.offset.
11093  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11094  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11095  }
11096 
11097  // 2. Process this allocation.
11098  // There is allocation with suballoc.offset, suballoc.size.
11099  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11100 
11101  // 3. Prepare for next iteration.
11102  lastOffset = suballoc.offset + suballoc.size;
11103  ++nextAlloc1stIndex;
11104  }
11105  // We are at the end.
11106  else
11107  {
11108  if(lastOffset < freeSpace1stTo2ndEnd)
11109  {
11110  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
11111  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
11112  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11113  }
11114 
11115  // End of loop.
11116  lastOffset = freeSpace1stTo2ndEnd;
11117  }
11118  }
11119 
11120  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11121  {
11122  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
11123  while(lastOffset < size)
11124  {
11125  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
11126  while(nextAlloc2ndIndex != SIZE_MAX &&
11127  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
11128  {
11129  --nextAlloc2ndIndex;
11130  }
11131 
11132  // Found non-null allocation.
11133  if(nextAlloc2ndIndex != SIZE_MAX)
11134  {
11135  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
11136 
11137  // 1. Process free space before this allocation.
11138  if(lastOffset < suballoc.offset)
11139  {
11140  // There is free space from lastOffset to suballoc.offset.
11141  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
11142  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11143  }
11144 
11145  // 2. Process this allocation.
11146  // There is allocation with suballoc.offset, suballoc.size.
11147  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
11148 
11149  // 3. Prepare for next iteration.
11150  lastOffset = suballoc.offset + suballoc.size;
11151  --nextAlloc2ndIndex;
11152  }
11153  // We are at the end.
11154  else
11155  {
11156  if(lastOffset < size)
11157  {
11158  // There is free space from lastOffset to size.
11159  const VkDeviceSize unusedRangeSize = size - lastOffset;
11160  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
11161  }
11162 
11163  // End of loop.
11164  lastOffset = size;
11165  }
11166  }
11167  }
11168 
11169  PrintDetailedMap_End(json);
11170 }
11171 #endif // #if VMA_STATS_STRING_ENABLED
11172 
11173 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
11174  uint32_t currentFrameIndex,
11175  uint32_t frameInUseCount,
11176  VkDeviceSize bufferImageGranularity,
11177  VkDeviceSize allocSize,
11178  VkDeviceSize allocAlignment,
11179  bool upperAddress,
11180  VmaSuballocationType allocType,
11181  bool canMakeOtherLost,
11182  uint32_t strategy,
11183  VmaAllocationRequest* pAllocationRequest)
11184 {
11185  VMA_ASSERT(allocSize > 0);
11186  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
11187  VMA_ASSERT(pAllocationRequest != VMA_NULL);
11188  VMA_HEAVY_ASSERT(Validate());
11189  return upperAddress ?
11190  CreateAllocationRequest_UpperAddress(
11191  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11192  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
11193  CreateAllocationRequest_LowerAddress(
11194  currentFrameIndex, frameInUseCount, bufferImageGranularity,
11195  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
11196 }
11197 
11198 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
11199  uint32_t currentFrameIndex,
11200  uint32_t frameInUseCount,
11201  VkDeviceSize bufferImageGranularity,
11202  VkDeviceSize allocSize,
11203  VkDeviceSize allocAlignment,
11204  VmaSuballocationType allocType,
11205  bool canMakeOtherLost,
11206  uint32_t strategy,
11207  VmaAllocationRequest* pAllocationRequest)
11208 {
11209  const VkDeviceSize size = GetSize();
11210  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11211  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11212 
11213  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11214  {
11215  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
11216  return false;
11217  }
11218 
11219  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
11220  if(allocSize > size)
11221  {
11222  return false;
11223  }
11224  VkDeviceSize resultBaseOffset = size - allocSize;
11225  if(!suballocations2nd.empty())
11226  {
11227  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11228  resultBaseOffset = lastSuballoc.offset - allocSize;
11229  if(allocSize > lastSuballoc.offset)
11230  {
11231  return false;
11232  }
11233  }
11234 
11235  // Start from offset equal to end of free space.
11236  VkDeviceSize resultOffset = resultBaseOffset;
11237 
11238  // Apply VMA_DEBUG_MARGIN at the end.
11239  if(VMA_DEBUG_MARGIN > 0)
11240  {
11241  if(resultOffset < VMA_DEBUG_MARGIN)
11242  {
11243  return false;
11244  }
11245  resultOffset -= VMA_DEBUG_MARGIN;
11246  }
11247 
11248  // Apply alignment.
11249  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
11250 
11251  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
11252  // Make bigger alignment if necessary.
11253  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11254  {
11255  bool bufferImageGranularityConflict = false;
11256  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11257  {
11258  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11259  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11260  {
11261  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
11262  {
11263  bufferImageGranularityConflict = true;
11264  break;
11265  }
11266  }
11267  else
11268  // Already on previous page.
11269  break;
11270  }
11271  if(bufferImageGranularityConflict)
11272  {
11273  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
11274  }
11275  }
11276 
11277  // There is enough free space.
11278  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
11279  suballocations1st.back().offset + suballocations1st.back().size :
11280  0;
11281  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
11282  {
11283  // Check previous suballocations for BufferImageGranularity conflicts.
11284  // If conflict exists, allocation cannot be made here.
11285  if(bufferImageGranularity > 1)
11286  {
11287  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11288  {
11289  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11290  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11291  {
11292  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
11293  {
11294  return false;
11295  }
11296  }
11297  else
11298  {
11299  // Already on next page.
11300  break;
11301  }
11302  }
11303  }
11304 
11305  // All tests passed: Success.
11306  pAllocationRequest->offset = resultOffset;
11307  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
11308  pAllocationRequest->sumItemSize = 0;
11309  // pAllocationRequest->item unused.
11310  pAllocationRequest->itemsToMakeLostCount = 0;
11311  pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
11312  return true;
11313  }
11314 
11315  return false;
11316 }
11317 
11318 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
11319  uint32_t currentFrameIndex,
11320  uint32_t frameInUseCount,
11321  VkDeviceSize bufferImageGranularity,
11322  VkDeviceSize allocSize,
11323  VkDeviceSize allocAlignment,
11324  VmaSuballocationType allocType,
11325  bool canMakeOtherLost,
11326  uint32_t strategy,
11327  VmaAllocationRequest* pAllocationRequest)
11328 {
11329  const VkDeviceSize size = GetSize();
11330  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11331  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11332 
11333  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11334  {
11335  // Try to allocate at the end of 1st vector.
11336 
11337  VkDeviceSize resultBaseOffset = 0;
11338  if(!suballocations1st.empty())
11339  {
11340  const VmaSuballocation& lastSuballoc = suballocations1st.back();
11341  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11342  }
11343 
11344  // Start from offset equal to beginning of free space.
11345  VkDeviceSize resultOffset = resultBaseOffset;
11346 
11347  // Apply VMA_DEBUG_MARGIN at the beginning.
11348  if(VMA_DEBUG_MARGIN > 0)
11349  {
11350  resultOffset += VMA_DEBUG_MARGIN;
11351  }
11352 
11353  // Apply alignment.
11354  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11355 
11356  // Check previous suballocations for BufferImageGranularity conflicts.
11357  // Make bigger alignment if necessary.
11358  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
11359  {
11360  bool bufferImageGranularityConflict = false;
11361  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
11362  {
11363  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
11364  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11365  {
11366  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11367  {
11368  bufferImageGranularityConflict = true;
11369  break;
11370  }
11371  }
11372  else
11373  // Already on previous page.
11374  break;
11375  }
11376  if(bufferImageGranularityConflict)
11377  {
11378  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11379  }
11380  }
11381 
11382  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11383  suballocations2nd.back().offset : size;
11384 
11385  // There is enough free space at the end after alignment.
11386  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11387  {
11388  // Check next suballocations for BufferImageGranularity conflicts.
11389  // If conflict exists, allocation cannot be made here.
11390  if((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11391  {
11392  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11393  {
11394  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11395  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11396  {
11397  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11398  {
11399  return false;
11400  }
11401  }
11402  else
11403  {
11404  // Already on previous page.
11405  break;
11406  }
11407  }
11408  }
11409 
11410  // All tests passed: Success.
11411  pAllocationRequest->offset = resultOffset;
11412  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11413  pAllocationRequest->sumItemSize = 0;
11414  // pAllocationRequest->item, customData unused.
11415  pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11416  pAllocationRequest->itemsToMakeLostCount = 0;
11417  return true;
11418  }
11419  }
11420 
11421  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11422  // beginning of 1st vector as the end of free space.
11423  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11424  {
11425  VMA_ASSERT(!suballocations1st.empty());
11426 
11427  VkDeviceSize resultBaseOffset = 0;
11428  if(!suballocations2nd.empty())
11429  {
11430  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11431  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11432  }
11433 
11434  // Start from offset equal to beginning of free space.
11435  VkDeviceSize resultOffset = resultBaseOffset;
11436 
11437  // Apply VMA_DEBUG_MARGIN at the beginning.
11438  if(VMA_DEBUG_MARGIN > 0)
11439  {
11440  resultOffset += VMA_DEBUG_MARGIN;
11441  }
11442 
11443  // Apply alignment.
11444  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11445 
11446  // Check previous suballocations for BufferImageGranularity conflicts.
11447  // Make bigger alignment if necessary.
11448  if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
11449  {
11450  bool bufferImageGranularityConflict = false;
11451  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11452  {
11453  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11454  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11455  {
11456  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11457  {
11458  bufferImageGranularityConflict = true;
11459  break;
11460  }
11461  }
11462  else
11463  // Already on previous page.
11464  break;
11465  }
11466  if(bufferImageGranularityConflict)
11467  {
11468  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11469  }
11470  }
11471 
11472  pAllocationRequest->itemsToMakeLostCount = 0;
11473  pAllocationRequest->sumItemSize = 0;
11474  size_t index1st = m_1stNullItemsBeginCount;
11475 
11476  if(canMakeOtherLost)
11477  {
11478  while(index1st < suballocations1st.size() &&
11479  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11480  {
11481  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11482  const VmaSuballocation& suballoc = suballocations1st[index1st];
11483  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11484  {
11485  // No problem.
11486  }
11487  else
11488  {
11489  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11490  if(suballoc.hAllocation->CanBecomeLost() &&
11491  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11492  {
11493  ++pAllocationRequest->itemsToMakeLostCount;
11494  pAllocationRequest->sumItemSize += suballoc.size;
11495  }
11496  else
11497  {
11498  return false;
11499  }
11500  }
11501  ++index1st;
11502  }
11503 
11504  // Check next suballocations for BufferImageGranularity conflicts.
11505  // If conflict exists, we must mark more allocations lost or fail.
11506  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11507  {
11508  while(index1st < suballocations1st.size())
11509  {
11510  const VmaSuballocation& suballoc = suballocations1st[index1st];
11511  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11512  {
11513  if(suballoc.hAllocation != VK_NULL_HANDLE)
11514  {
11515  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11516  if(suballoc.hAllocation->CanBecomeLost() &&
11517  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11518  {
11519  ++pAllocationRequest->itemsToMakeLostCount;
11520  pAllocationRequest->sumItemSize += suballoc.size;
11521  }
11522  else
11523  {
11524  return false;
11525  }
11526  }
11527  }
11528  else
11529  {
11530  // Already on next page.
11531  break;
11532  }
11533  ++index1st;
11534  }
11535  }
11536 
11537  // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11538  if(index1st == suballocations1st.size() &&
11539  resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11540  {
11541  // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11542  VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11543  }
11544  }
11545 
11546  // There is enough free space at the end after alignment.
11547  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11548  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11549  {
11550  // Check next suballocations for BufferImageGranularity conflicts.
11551  // If conflict exists, allocation cannot be made here.
11552  if(allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
11553  {
11554  for(size_t nextSuballocIndex = index1st;
11555  nextSuballocIndex < suballocations1st.size();
11556  nextSuballocIndex++)
11557  {
11558  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11559  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11560  {
11561  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11562  {
11563  return false;
11564  }
11565  }
11566  else
11567  {
11568  // Already on next page.
11569  break;
11570  }
11571  }
11572  }
11573 
11574  // All tests passed: Success.
11575  pAllocationRequest->offset = resultOffset;
11576  pAllocationRequest->sumFreeSize =
11577  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11578  - resultBaseOffset
11579  - pAllocationRequest->sumItemSize;
11580  pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11581  // pAllocationRequest->item, customData unused.
11582  return true;
11583  }
11584  }
11585 
11586  return false;
11587 }
11588 
11589 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11590  uint32_t currentFrameIndex,
11591  uint32_t frameInUseCount,
11592  VmaAllocationRequest* pAllocationRequest)
11593 {
11594  if(pAllocationRequest->itemsToMakeLostCount == 0)
11595  {
11596  return true;
11597  }
11598 
11599  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11600 
11601  // We always start from 1st.
11602  SuballocationVectorType* suballocations = &AccessSuballocations1st();
11603  size_t index = m_1stNullItemsBeginCount;
11604  size_t madeLostCount = 0;
11605  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11606  {
11607  if(index == suballocations->size())
11608  {
11609  index = 0;
11610  // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11611  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11612  {
11613  suballocations = &AccessSuballocations2nd();
11614  }
11615  // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11616  // suballocations continues pointing at AccessSuballocations1st().
11617  VMA_ASSERT(!suballocations->empty());
11618  }
11619  VmaSuballocation& suballoc = (*suballocations)[index];
11620  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11621  {
11622  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11623  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11624  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11625  {
11626  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11627  suballoc.hAllocation = VK_NULL_HANDLE;
11628  m_SumFreeSize += suballoc.size;
11629  if(suballocations == &AccessSuballocations1st())
11630  {
11631  ++m_1stNullItemsMiddleCount;
11632  }
11633  else
11634  {
11635  ++m_2ndNullItemsCount;
11636  }
11637  ++madeLostCount;
11638  }
11639  else
11640  {
11641  return false;
11642  }
11643  }
11644  ++index;
11645  }
11646 
11647  CleanupAfterFree();
11648  //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11649 
11650  return true;
11651 }
11652 
11653 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11654 {
11655  uint32_t lostAllocationCount = 0;
11656 
11657  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11658  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11659  {
11660  VmaSuballocation& suballoc = suballocations1st[i];
11661  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11662  suballoc.hAllocation->CanBecomeLost() &&
11663  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11664  {
11665  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11666  suballoc.hAllocation = VK_NULL_HANDLE;
11667  ++m_1stNullItemsMiddleCount;
11668  m_SumFreeSize += suballoc.size;
11669  ++lostAllocationCount;
11670  }
11671  }
11672 
11673  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11674  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11675  {
11676  VmaSuballocation& suballoc = suballocations2nd[i];
11677  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11678  suballoc.hAllocation->CanBecomeLost() &&
11679  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11680  {
11681  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11682  suballoc.hAllocation = VK_NULL_HANDLE;
11683  ++m_2ndNullItemsCount;
11684  m_SumFreeSize += suballoc.size;
11685  ++lostAllocationCount;
11686  }
11687  }
11688 
11689  if(lostAllocationCount)
11690  {
11691  CleanupAfterFree();
11692  }
11693 
11694  return lostAllocationCount;
11695 }
11696 
11697 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11698 {
11699  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11700  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11701  {
11702  const VmaSuballocation& suballoc = suballocations1st[i];
11703  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11704  {
11705  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11706  {
11707  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11708  return VK_ERROR_VALIDATION_FAILED_EXT;
11709  }
11710  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11711  {
11712  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11713  return VK_ERROR_VALIDATION_FAILED_EXT;
11714  }
11715  }
11716  }
11717 
11718  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11719  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11720  {
11721  const VmaSuballocation& suballoc = suballocations2nd[i];
11722  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11723  {
11724  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11725  {
11726  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11727  return VK_ERROR_VALIDATION_FAILED_EXT;
11728  }
11729  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11730  {
11731  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11732  return VK_ERROR_VALIDATION_FAILED_EXT;
11733  }
11734  }
11735  }
11736 
11737  return VK_SUCCESS;
11738 }
11739 
11740 void VmaBlockMetadata_Linear::Alloc(
11741  const VmaAllocationRequest& request,
11742  VmaSuballocationType type,
11743  VkDeviceSize allocSize,
11744  VmaAllocation hAllocation)
11745 {
11746  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11747 
11748  switch(request.type)
11749  {
11750  case VmaAllocationRequestType::UpperAddress:
11751  {
11752  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11753  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11754  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11755  suballocations2nd.push_back(newSuballoc);
11756  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11757  }
11758  break;
11759  case VmaAllocationRequestType::EndOf1st:
11760  {
11761  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11762 
11763  VMA_ASSERT(suballocations1st.empty() ||
11764  request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11765  // Check if it fits before the end of the block.
11766  VMA_ASSERT(request.offset + allocSize <= GetSize());
11767 
11768  suballocations1st.push_back(newSuballoc);
11769  }
11770  break;
11771  case VmaAllocationRequestType::EndOf2nd:
11772  {
11773  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11774  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11775  VMA_ASSERT(!suballocations1st.empty() &&
11776  request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11777  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11778 
11779  switch(m_2ndVectorMode)
11780  {
11781  case SECOND_VECTOR_EMPTY:
11782  // First allocation from second part ring buffer.
11783  VMA_ASSERT(suballocations2nd.empty());
11784  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11785  break;
11786  case SECOND_VECTOR_RING_BUFFER:
11787  // 2-part ring buffer is already started.
11788  VMA_ASSERT(!suballocations2nd.empty());
11789  break;
11790  case SECOND_VECTOR_DOUBLE_STACK:
11791  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11792  break;
11793  default:
11794  VMA_ASSERT(0);
11795  }
11796 
11797  suballocations2nd.push_back(newSuballoc);
11798  }
11799  break;
11800  default:
11801  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11802  }
11803 
11804  m_SumFreeSize -= newSuballoc.size;
11805 }
11806 
11807 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11808 {
11809  FreeAtOffset(allocation->GetOffset());
11810 }
11811 
11812 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11813 {
11814  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11815  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11816 
11817  if(!suballocations1st.empty())
11818  {
11819  // First allocation: Mark it as next empty at the beginning.
11820  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11821  if(firstSuballoc.offset == offset)
11822  {
11823  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11824  firstSuballoc.hAllocation = VK_NULL_HANDLE;
11825  m_SumFreeSize += firstSuballoc.size;
11826  ++m_1stNullItemsBeginCount;
11827  CleanupAfterFree();
11828  return;
11829  }
11830  }
11831 
11832  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11833  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11834  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11835  {
11836  VmaSuballocation& lastSuballoc = suballocations2nd.back();
11837  if(lastSuballoc.offset == offset)
11838  {
11839  m_SumFreeSize += lastSuballoc.size;
11840  suballocations2nd.pop_back();
11841  CleanupAfterFree();
11842  return;
11843  }
11844  }
11845  // Last allocation in 1st vector.
11846  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11847  {
11848  VmaSuballocation& lastSuballoc = suballocations1st.back();
11849  if(lastSuballoc.offset == offset)
11850  {
11851  m_SumFreeSize += lastSuballoc.size;
11852  suballocations1st.pop_back();
11853  CleanupAfterFree();
11854  return;
11855  }
11856  }
11857 
11858  // Item from the middle of 1st vector.
11859  {
11860  VmaSuballocation refSuballoc;
11861  refSuballoc.offset = offset;
11862  // Rest of members stays uninitialized intentionally for better performance.
11863  SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11864  suballocations1st.begin() + m_1stNullItemsBeginCount,
11865  suballocations1st.end(),
11866  refSuballoc,
11867  VmaSuballocationOffsetLess());
11868  if(it != suballocations1st.end())
11869  {
11870  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11871  it->hAllocation = VK_NULL_HANDLE;
11872  ++m_1stNullItemsMiddleCount;
11873  m_SumFreeSize += it->size;
11874  CleanupAfterFree();
11875  return;
11876  }
11877  }
11878 
11879  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11880  {
11881  // Item from the middle of 2nd vector.
11882  VmaSuballocation refSuballoc;
11883  refSuballoc.offset = offset;
11884  // Rest of members stays uninitialized intentionally for better performance.
11885  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11886  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11887  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11888  if(it != suballocations2nd.end())
11889  {
11890  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11891  it->hAllocation = VK_NULL_HANDLE;
11892  ++m_2ndNullItemsCount;
11893  m_SumFreeSize += it->size;
11894  CleanupAfterFree();
11895  return;
11896  }
11897  }
11898 
11899  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11900 }
11901 
11902 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11903 {
11904  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11905  const size_t suballocCount = AccessSuballocations1st().size();
11906  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11907 }
11908 
11909 void VmaBlockMetadata_Linear::CleanupAfterFree()
11910 {
11911  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11912  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11913 
11914  if(IsEmpty())
11915  {
11916  suballocations1st.clear();
11917  suballocations2nd.clear();
11918  m_1stNullItemsBeginCount = 0;
11919  m_1stNullItemsMiddleCount = 0;
11920  m_2ndNullItemsCount = 0;
11921  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11922  }
11923  else
11924  {
11925  const size_t suballoc1stCount = suballocations1st.size();
11926  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11927  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11928 
11929  // Find more null items at the beginning of 1st vector.
11930  while(m_1stNullItemsBeginCount < suballoc1stCount &&
11931  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11932  {
11933  ++m_1stNullItemsBeginCount;
11934  --m_1stNullItemsMiddleCount;
11935  }
11936 
11937  // Find more null items at the end of 1st vector.
11938  while(m_1stNullItemsMiddleCount > 0 &&
11939  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11940  {
11941  --m_1stNullItemsMiddleCount;
11942  suballocations1st.pop_back();
11943  }
11944 
11945  // Find more null items at the end of 2nd vector.
11946  while(m_2ndNullItemsCount > 0 &&
11947  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11948  {
11949  --m_2ndNullItemsCount;
11950  suballocations2nd.pop_back();
11951  }
11952 
11953  // Find more null items at the beginning of 2nd vector.
11954  while(m_2ndNullItemsCount > 0 &&
11955  suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11956  {
11957  --m_2ndNullItemsCount;
11958  VmaVectorRemove(suballocations2nd, 0);
11959  }
11960 
11961  if(ShouldCompact1st())
11962  {
11963  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11964  size_t srcIndex = m_1stNullItemsBeginCount;
11965  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11966  {
11967  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11968  {
11969  ++srcIndex;
11970  }
11971  if(dstIndex != srcIndex)
11972  {
11973  suballocations1st[dstIndex] = suballocations1st[srcIndex];
11974  }
11975  ++srcIndex;
11976  }
11977  suballocations1st.resize(nonNullItemCount);
11978  m_1stNullItemsBeginCount = 0;
11979  m_1stNullItemsMiddleCount = 0;
11980  }
11981 
11982  // 2nd vector became empty.
11983  if(suballocations2nd.empty())
11984  {
11985  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11986  }
11987 
11988  // 1st vector became empty.
11989  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11990  {
11991  suballocations1st.clear();
11992  m_1stNullItemsBeginCount = 0;
11993 
11994  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11995  {
11996  // Swap 1st with 2nd. Now 2nd is empty.
11997  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11998  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11999  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
12000  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
12001  {
12002  ++m_1stNullItemsBeginCount;
12003  --m_1stNullItemsMiddleCount;
12004  }
12005  m_2ndNullItemsCount = 0;
12006  m_1stVectorIndex ^= 1;
12007  }
12008  }
12009  }
12010 
12011  VMA_HEAVY_ASSERT(Validate());
12012 }
12013 
12014 
12016 // class VmaBlockMetadata_Buddy
12017 
12018 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
12019  VmaBlockMetadata(hAllocator),
12020  m_Root(VMA_NULL),
12021  m_AllocationCount(0),
12022  m_FreeCount(1),
12023  m_SumFreeSize(0)
12024 {
12025  memset(m_FreeList, 0, sizeof(m_FreeList));
12026 }
12027 
12028 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
12029 {
12030  DeleteNode(m_Root);
12031 }
12032 
12033 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
12034 {
12035  VmaBlockMetadata::Init(size);
12036 
12037  m_UsableSize = VmaPrevPow2(size);
12038  m_SumFreeSize = m_UsableSize;
12039 
12040  // Calculate m_LevelCount.
12041  m_LevelCount = 1;
12042  while(m_LevelCount < MAX_LEVELS &&
12043  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
12044  {
12045  ++m_LevelCount;
12046  }
12047 
12048  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
12049  rootNode->offset = 0;
12050  rootNode->type = Node::TYPE_FREE;
12051  rootNode->parent = VMA_NULL;
12052  rootNode->buddy = VMA_NULL;
12053 
12054  m_Root = rootNode;
12055  AddToFreeListFront(0, rootNode);
12056 }
12057 
12058 bool VmaBlockMetadata_Buddy::Validate() const
12059 {
12060  // Validate tree.
12061  ValidationContext ctx;
12062  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
12063  {
12064  VMA_VALIDATE(false && "ValidateNode failed.");
12065  }
12066  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
12067  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
12068 
12069  // Validate free node lists.
12070  for(uint32_t level = 0; level < m_LevelCount; ++level)
12071  {
12072  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
12073  m_FreeList[level].front->free.prev == VMA_NULL);
12074 
12075  for(Node* node = m_FreeList[level].front;
12076  node != VMA_NULL;
12077  node = node->free.next)
12078  {
12079  VMA_VALIDATE(node->type == Node::TYPE_FREE);
12080 
12081  if(node->free.next == VMA_NULL)
12082  {
12083  VMA_VALIDATE(m_FreeList[level].back == node);
12084  }
12085  else
12086  {
12087  VMA_VALIDATE(node->free.next->free.prev == node);
12088  }
12089  }
12090  }
12091 
12092  // Validate that free lists ar higher levels are empty.
12093  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
12094  {
12095  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
12096  }
12097 
12098  return true;
12099 }
12100 
12101 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
12102 {
12103  for(uint32_t level = 0; level < m_LevelCount; ++level)
12104  {
12105  if(m_FreeList[level].front != VMA_NULL)
12106  {
12107  return LevelToNodeSize(level);
12108  }
12109  }
12110  return 0;
12111 }
12112 
12113 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
12114 {
12115  const VkDeviceSize unusableSize = GetUnusableSize();
12116 
12117  outInfo.blockCount = 1;
12118 
12119  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
12120  outInfo.usedBytes = outInfo.unusedBytes = 0;
12121 
12122  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
12123  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
12124  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
12125 
12126  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
12127 
12128  if(unusableSize > 0)
12129  {
12130  ++outInfo.unusedRangeCount;
12131  outInfo.unusedBytes += unusableSize;
12132  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
12133  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
12134  }
12135 }
12136 
12137 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
12138 {
12139  const VkDeviceSize unusableSize = GetUnusableSize();
12140 
12141  inoutStats.size += GetSize();
12142  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
12143  inoutStats.allocationCount += m_AllocationCount;
12144  inoutStats.unusedRangeCount += m_FreeCount;
12145  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
12146 
12147  if(unusableSize > 0)
12148  {
12149  ++inoutStats.unusedRangeCount;
12150  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
12151  }
12152 }
12153 
12154 #if VMA_STATS_STRING_ENABLED
12155 
12156 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
12157 {
12158  // TODO optimize
12159  VmaStatInfo stat;
12160  CalcAllocationStatInfo(stat);
12161 
12162  PrintDetailedMap_Begin(
12163  json,
12164  stat.unusedBytes,
12165  stat.allocationCount,
12166  stat.unusedRangeCount);
12167 
12168  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
12169 
12170  const VkDeviceSize unusableSize = GetUnusableSize();
12171  if(unusableSize > 0)
12172  {
12173  PrintDetailedMap_UnusedRange(json,
12174  m_UsableSize, // offset
12175  unusableSize); // size
12176  }
12177 
12178  PrintDetailedMap_End(json);
12179 }
12180 
12181 #endif // #if VMA_STATS_STRING_ENABLED
12182 
12183 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
12184  uint32_t currentFrameIndex,
12185  uint32_t frameInUseCount,
12186  VkDeviceSize bufferImageGranularity,
12187  VkDeviceSize allocSize,
12188  VkDeviceSize allocAlignment,
12189  bool upperAddress,
12190  VmaSuballocationType allocType,
12191  bool canMakeOtherLost,
12192  uint32_t strategy,
12193  VmaAllocationRequest* pAllocationRequest)
12194 {
12195  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
12196 
12197  // Simple way to respect bufferImageGranularity. May be optimized some day.
12198  // Whenever it might be an OPTIMAL image...
12199  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
12200  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
12201  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
12202  {
12203  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
12204  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
12205  }
12206 
12207  if(allocSize > m_UsableSize)
12208  {
12209  return false;
12210  }
12211 
12212  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12213  for(uint32_t level = targetLevel + 1; level--; )
12214  {
12215  for(Node* freeNode = m_FreeList[level].front;
12216  freeNode != VMA_NULL;
12217  freeNode = freeNode->free.next)
12218  {
12219  if(freeNode->offset % allocAlignment == 0)
12220  {
12221  pAllocationRequest->type = VmaAllocationRequestType::Normal;
12222  pAllocationRequest->offset = freeNode->offset;
12223  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
12224  pAllocationRequest->sumItemSize = 0;
12225  pAllocationRequest->itemsToMakeLostCount = 0;
12226  pAllocationRequest->customData = (void*)(uintptr_t)level;
12227  return true;
12228  }
12229  }
12230  }
12231 
12232  return false;
12233 }
12234 
12235 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
12236  uint32_t currentFrameIndex,
12237  uint32_t frameInUseCount,
12238  VmaAllocationRequest* pAllocationRequest)
12239 {
12240  /*
12241  Lost allocations are not supported in buddy allocator at the moment.
12242  Support might be added in the future.
12243  */
12244  return pAllocationRequest->itemsToMakeLostCount == 0;
12245 }
12246 
12247 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
12248 {
12249  /*
12250  Lost allocations are not supported in buddy allocator at the moment.
12251  Support might be added in the future.
12252  */
12253  return 0;
12254 }
12255 
12256 void VmaBlockMetadata_Buddy::Alloc(
12257  const VmaAllocationRequest& request,
12258  VmaSuballocationType type,
12259  VkDeviceSize allocSize,
12260  VmaAllocation hAllocation)
12261 {
12262  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
12263 
12264  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
12265  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
12266 
12267  Node* currNode = m_FreeList[currLevel].front;
12268  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12269  while(currNode->offset != request.offset)
12270  {
12271  currNode = currNode->free.next;
12272  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
12273  }
12274 
12275  // Go down, splitting free nodes.
12276  while(currLevel < targetLevel)
12277  {
12278  // currNode is already first free node at currLevel.
12279  // Remove it from list of free nodes at this currLevel.
12280  RemoveFromFreeList(currLevel, currNode);
12281 
12282  const uint32_t childrenLevel = currLevel + 1;
12283 
12284  // Create two free sub-nodes.
12285  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
12286  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
12287 
12288  leftChild->offset = currNode->offset;
12289  leftChild->type = Node::TYPE_FREE;
12290  leftChild->parent = currNode;
12291  leftChild->buddy = rightChild;
12292 
12293  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
12294  rightChild->type = Node::TYPE_FREE;
12295  rightChild->parent = currNode;
12296  rightChild->buddy = leftChild;
12297 
12298  // Convert current currNode to split type.
12299  currNode->type = Node::TYPE_SPLIT;
12300  currNode->split.leftChild = leftChild;
12301 
12302  // Add child nodes to free list. Order is important!
12303  AddToFreeListFront(childrenLevel, rightChild);
12304  AddToFreeListFront(childrenLevel, leftChild);
12305 
12306  ++m_FreeCount;
12307  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
12308  ++currLevel;
12309  currNode = m_FreeList[currLevel].front;
12310 
12311  /*
12312  We can be sure that currNode, as left child of node previously split,
12313  also fullfills the alignment requirement.
12314  */
12315  }
12316 
12317  // Remove from free list.
12318  VMA_ASSERT(currLevel == targetLevel &&
12319  currNode != VMA_NULL &&
12320  currNode->type == Node::TYPE_FREE);
12321  RemoveFromFreeList(currLevel, currNode);
12322 
12323  // Convert to allocation node.
12324  currNode->type = Node::TYPE_ALLOCATION;
12325  currNode->allocation.alloc = hAllocation;
12326 
12327  ++m_AllocationCount;
12328  --m_FreeCount;
12329  m_SumFreeSize -= allocSize;
12330 }
12331 
12332 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
12333 {
12334  if(node->type == Node::TYPE_SPLIT)
12335  {
12336  DeleteNode(node->split.leftChild->buddy);
12337  DeleteNode(node->split.leftChild);
12338  }
12339 
12340  vma_delete(GetAllocationCallbacks(), node);
12341 }
12342 
12343 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
12344 {
12345  VMA_VALIDATE(level < m_LevelCount);
12346  VMA_VALIDATE(curr->parent == parent);
12347  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
12348  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
12349  switch(curr->type)
12350  {
12351  case Node::TYPE_FREE:
12352  // curr->free.prev, next are validated separately.
12353  ctx.calculatedSumFreeSize += levelNodeSize;
12354  ++ctx.calculatedFreeCount;
12355  break;
12356  case Node::TYPE_ALLOCATION:
12357  ++ctx.calculatedAllocationCount;
12358  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
12359  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
12360  break;
12361  case Node::TYPE_SPLIT:
12362  {
12363  const uint32_t childrenLevel = level + 1;
12364  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
12365  const Node* const leftChild = curr->split.leftChild;
12366  VMA_VALIDATE(leftChild != VMA_NULL);
12367  VMA_VALIDATE(leftChild->offset == curr->offset);
12368  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12369  {
12370  VMA_VALIDATE(false && "ValidateNode for left child failed.");
12371  }
12372  const Node* const rightChild = leftChild->buddy;
12373  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12374  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12375  {
12376  VMA_VALIDATE(false && "ValidateNode for right child failed.");
12377  }
12378  }
12379  break;
12380  default:
12381  return false;
12382  }
12383 
12384  return true;
12385 }
12386 
12387 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12388 {
12389  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12390  uint32_t level = 0;
12391  VkDeviceSize currLevelNodeSize = m_UsableSize;
12392  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12393  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12394  {
12395  ++level;
12396  currLevelNodeSize = nextLevelNodeSize;
12397  nextLevelNodeSize = currLevelNodeSize >> 1;
12398  }
12399  return level;
12400 }
12401 
12402 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12403 {
12404  // Find node and level.
12405  Node* node = m_Root;
12406  VkDeviceSize nodeOffset = 0;
12407  uint32_t level = 0;
12408  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12409  while(node->type == Node::TYPE_SPLIT)
12410  {
12411  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12412  if(offset < nodeOffset + nextLevelSize)
12413  {
12414  node = node->split.leftChild;
12415  }
12416  else
12417  {
12418  node = node->split.leftChild->buddy;
12419  nodeOffset += nextLevelSize;
12420  }
12421  ++level;
12422  levelNodeSize = nextLevelSize;
12423  }
12424 
12425  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12426  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12427 
12428  ++m_FreeCount;
12429  --m_AllocationCount;
12430  m_SumFreeSize += alloc->GetSize();
12431 
12432  node->type = Node::TYPE_FREE;
12433 
12434  // Join free nodes if possible.
12435  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12436  {
12437  RemoveFromFreeList(level, node->buddy);
12438  Node* const parent = node->parent;
12439 
12440  vma_delete(GetAllocationCallbacks(), node->buddy);
12441  vma_delete(GetAllocationCallbacks(), node);
12442  parent->type = Node::TYPE_FREE;
12443 
12444  node = parent;
12445  --level;
12446  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12447  --m_FreeCount;
12448  }
12449 
12450  AddToFreeListFront(level, node);
12451 }
12452 
12453 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12454 {
12455  switch(node->type)
12456  {
12457  case Node::TYPE_FREE:
12458  ++outInfo.unusedRangeCount;
12459  outInfo.unusedBytes += levelNodeSize;
12460  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12461  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12462  break;
12463  case Node::TYPE_ALLOCATION:
12464  {
12465  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12466  ++outInfo.allocationCount;
12467  outInfo.usedBytes += allocSize;
12468  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12469  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12470 
12471  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12472  if(unusedRangeSize > 0)
12473  {
12474  ++outInfo.unusedRangeCount;
12475  outInfo.unusedBytes += unusedRangeSize;
12476  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12477  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12478  }
12479  }
12480  break;
12481  case Node::TYPE_SPLIT:
12482  {
12483  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12484  const Node* const leftChild = node->split.leftChild;
12485  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12486  const Node* const rightChild = leftChild->buddy;
12487  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12488  }
12489  break;
12490  default:
12491  VMA_ASSERT(0);
12492  }
12493 }
12494 
12495 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12496 {
12497  VMA_ASSERT(node->type == Node::TYPE_FREE);
12498 
12499  // List is empty.
12500  Node* const frontNode = m_FreeList[level].front;
12501  if(frontNode == VMA_NULL)
12502  {
12503  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12504  node->free.prev = node->free.next = VMA_NULL;
12505  m_FreeList[level].front = m_FreeList[level].back = node;
12506  }
12507  else
12508  {
12509  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12510  node->free.prev = VMA_NULL;
12511  node->free.next = frontNode;
12512  frontNode->free.prev = node;
12513  m_FreeList[level].front = node;
12514  }
12515 }
12516 
12517 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12518 {
12519  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12520 
12521  // It is at the front.
12522  if(node->free.prev == VMA_NULL)
12523  {
12524  VMA_ASSERT(m_FreeList[level].front == node);
12525  m_FreeList[level].front = node->free.next;
12526  }
12527  else
12528  {
12529  Node* const prevFreeNode = node->free.prev;
12530  VMA_ASSERT(prevFreeNode->free.next == node);
12531  prevFreeNode->free.next = node->free.next;
12532  }
12533 
12534  // It is at the back.
12535  if(node->free.next == VMA_NULL)
12536  {
12537  VMA_ASSERT(m_FreeList[level].back == node);
12538  m_FreeList[level].back = node->free.prev;
12539  }
12540  else
12541  {
12542  Node* const nextFreeNode = node->free.next;
12543  VMA_ASSERT(nextFreeNode->free.prev == node);
12544  nextFreeNode->free.prev = node->free.prev;
12545  }
12546 }
12547 
12548 #if VMA_STATS_STRING_ENABLED
12549 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12550 {
12551  switch(node->type)
12552  {
12553  case Node::TYPE_FREE:
12554  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12555  break;
12556  case Node::TYPE_ALLOCATION:
12557  {
12558  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12559  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12560  if(allocSize < levelNodeSize)
12561  {
12562  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12563  }
12564  }
12565  break;
12566  case Node::TYPE_SPLIT:
12567  {
12568  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12569  const Node* const leftChild = node->split.leftChild;
12570  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12571  const Node* const rightChild = leftChild->buddy;
12572  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12573  }
12574  break;
12575  default:
12576  VMA_ASSERT(0);
12577  }
12578 }
12579 #endif // #if VMA_STATS_STRING_ENABLED
12580 
12581 
12583 // class VmaDeviceMemoryBlock
12584 
12585 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12586  m_pMetadata(VMA_NULL),
12587  m_MemoryTypeIndex(UINT32_MAX),
12588  m_Id(0),
12589  m_hMemory(VK_NULL_HANDLE),
12590  m_MapCount(0),
12591  m_pMappedData(VMA_NULL)
12592 {
12593 }
12594 
12595 void VmaDeviceMemoryBlock::Init(
12596  VmaAllocator hAllocator,
12597  VmaPool hParentPool,
12598  uint32_t newMemoryTypeIndex,
12599  VkDeviceMemory newMemory,
12600  VkDeviceSize newSize,
12601  uint32_t id,
12602  uint32_t algorithm)
12603 {
12604  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12605 
12606  m_hParentPool = hParentPool;
12607  m_MemoryTypeIndex = newMemoryTypeIndex;
12608  m_Id = id;
12609  m_hMemory = newMemory;
12610 
12611  switch(algorithm)
12612  {
12614  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12615  break;
12617  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12618  break;
12619  default:
12620  VMA_ASSERT(0);
12621  // Fall-through.
12622  case 0:
12623  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12624  }
12625  m_pMetadata->Init(newSize);
12626 }
12627 
12628 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12629 {
12630  // This is the most important assert in the entire library.
12631  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12632  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12633 
12634  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12635  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12636  m_hMemory = VK_NULL_HANDLE;
12637 
12638  vma_delete(allocator, m_pMetadata);
12639  m_pMetadata = VMA_NULL;
12640 }
12641 
12642 bool VmaDeviceMemoryBlock::Validate() const
12643 {
12644  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12645  (m_pMetadata->GetSize() != 0));
12646 
12647  return m_pMetadata->Validate();
12648 }
12649 
12650 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12651 {
12652  void* pData = nullptr;
12653  VkResult res = Map(hAllocator, 1, &pData);
12654  if(res != VK_SUCCESS)
12655  {
12656  return res;
12657  }
12658 
12659  res = m_pMetadata->CheckCorruption(pData);
12660 
12661  Unmap(hAllocator, 1);
12662 
12663  return res;
12664 }
12665 
12666 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12667 {
12668  if(count == 0)
12669  {
12670  return VK_SUCCESS;
12671  }
12672 
12673  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12674  if(m_MapCount != 0)
12675  {
12676  m_MapCount += count;
12677  VMA_ASSERT(m_pMappedData != VMA_NULL);
12678  if(ppData != VMA_NULL)
12679  {
12680  *ppData = m_pMappedData;
12681  }
12682  return VK_SUCCESS;
12683  }
12684  else
12685  {
12686  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12687  hAllocator->m_hDevice,
12688  m_hMemory,
12689  0, // offset
12690  VK_WHOLE_SIZE,
12691  0, // flags
12692  &m_pMappedData);
12693  if(result == VK_SUCCESS)
12694  {
12695  if(ppData != VMA_NULL)
12696  {
12697  *ppData = m_pMappedData;
12698  }
12699  m_MapCount = count;
12700  }
12701  return result;
12702  }
12703 }
12704 
12705 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12706 {
12707  if(count == 0)
12708  {
12709  return;
12710  }
12711 
12712  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12713  if(m_MapCount >= count)
12714  {
12715  m_MapCount -= count;
12716  if(m_MapCount == 0)
12717  {
12718  m_pMappedData = VMA_NULL;
12719  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12720  }
12721  }
12722  else
12723  {
12724  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12725  }
12726 }
12727 
12728 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12729 {
12730  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12731  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12732 
12733  void* pData;
12734  VkResult res = Map(hAllocator, 1, &pData);
12735  if(res != VK_SUCCESS)
12736  {
12737  return res;
12738  }
12739 
12740  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12741  VmaWriteMagicValue(pData, allocOffset + allocSize);
12742 
12743  Unmap(hAllocator, 1);
12744 
12745  return VK_SUCCESS;
12746 }
12747 
12748 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12749 {
12750  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12751  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12752 
12753  void* pData;
12754  VkResult res = Map(hAllocator, 1, &pData);
12755  if(res != VK_SUCCESS)
12756  {
12757  return res;
12758  }
12759 
12760  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12761  {
12762  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12763  }
12764  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12765  {
12766  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12767  }
12768 
12769  Unmap(hAllocator, 1);
12770 
12771  return VK_SUCCESS;
12772 }
12773 
12774 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12775  const VmaAllocator hAllocator,
12776  const VmaAllocation hAllocation,
12777  VkDeviceSize allocationLocalOffset,
12778  VkBuffer hBuffer,
12779  const void* pNext)
12780 {
12781  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12782  hAllocation->GetBlock() == this);
12783  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12784  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12785  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12786  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12787  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12788  return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12789 }
12790 
12791 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12792  const VmaAllocator hAllocator,
12793  const VmaAllocation hAllocation,
12794  VkDeviceSize allocationLocalOffset,
12795  VkImage hImage,
12796  const void* pNext)
12797 {
12798  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12799  hAllocation->GetBlock() == this);
12800  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12801  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12802  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12803  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12804  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12805  return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12806 }
12807 
12808 static void InitStatInfo(VmaStatInfo& outInfo)
12809 {
12810  memset(&outInfo, 0, sizeof(outInfo));
12811  outInfo.allocationSizeMin = UINT64_MAX;
12812  outInfo.unusedRangeSizeMin = UINT64_MAX;
12813 }
12814 
12815 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12816 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12817 {
12818  inoutInfo.blockCount += srcInfo.blockCount;
12819  inoutInfo.allocationCount += srcInfo.allocationCount;
12820  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12821  inoutInfo.usedBytes += srcInfo.usedBytes;
12822  inoutInfo.unusedBytes += srcInfo.unusedBytes;
12823  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12824  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12825  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12826  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12827 }
12828 
12829 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12830 {
12831  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12832  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12833  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12834  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12835 }
12836 
12837 VmaPool_T::VmaPool_T(
12838  VmaAllocator hAllocator,
12839  const VmaPoolCreateInfo& createInfo,
12840  VkDeviceSize preferredBlockSize) :
12841  m_BlockVector(
12842  hAllocator,
12843  this, // hParentPool
12844  createInfo.memoryTypeIndex,
12845  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12846  createInfo.minBlockCount,
12847  createInfo.maxBlockCount,
12848  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12849  createInfo.frameInUseCount,
12850  createInfo.blockSize != 0, // explicitBlockSize
12851  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK,
12852  createInfo.priority), // algorithm
12853  m_Id(0),
12854  m_Name(VMA_NULL)
12855 {
12856 }
12857 
12858 VmaPool_T::~VmaPool_T()
12859 {
12860  VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
12861 }
12862 
12863 void VmaPool_T::SetName(const char* pName)
12864 {
12865  const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12866  VmaFreeString(allocs, m_Name);
12867 
12868  if(pName != VMA_NULL)
12869  {
12870  m_Name = VmaCreateStringCopy(allocs, pName);
12871  }
12872  else
12873  {
12874  m_Name = VMA_NULL;
12875  }
12876 }
12877 
12878 #if VMA_STATS_STRING_ENABLED
12879 
12880 #endif // #if VMA_STATS_STRING_ENABLED
12881 
12882 VmaBlockVector::VmaBlockVector(
12883  VmaAllocator hAllocator,
12884  VmaPool hParentPool,
12885  uint32_t memoryTypeIndex,
12886  VkDeviceSize preferredBlockSize,
12887  size_t minBlockCount,
12888  size_t maxBlockCount,
12889  VkDeviceSize bufferImageGranularity,
12890  uint32_t frameInUseCount,
12891  bool explicitBlockSize,
12892  uint32_t algorithm,
12893  float priority) :
12894  m_hAllocator(hAllocator),
12895  m_hParentPool(hParentPool),
12896  m_MemoryTypeIndex(memoryTypeIndex),
12897  m_PreferredBlockSize(preferredBlockSize),
12898  m_MinBlockCount(minBlockCount),
12899  m_MaxBlockCount(maxBlockCount),
12900  m_BufferImageGranularity(bufferImageGranularity),
12901  m_FrameInUseCount(frameInUseCount),
12902  m_ExplicitBlockSize(explicitBlockSize),
12903  m_Algorithm(algorithm),
12904  m_Priority(priority),
12905  m_HasEmptyBlock(false),
12906  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12907  m_NextBlockId(0)
12908 {
12909 }
12910 
12911 VmaBlockVector::~VmaBlockVector()
12912 {
12913  for(size_t i = m_Blocks.size(); i--; )
12914  {
12915  m_Blocks[i]->Destroy(m_hAllocator);
12916  vma_delete(m_hAllocator, m_Blocks[i]);
12917  }
12918 }
12919 
12920 VkResult VmaBlockVector::CreateMinBlocks()
12921 {
12922  for(size_t i = 0; i < m_MinBlockCount; ++i)
12923  {
12924  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12925  if(res != VK_SUCCESS)
12926  {
12927  return res;
12928  }
12929  }
12930  return VK_SUCCESS;
12931 }
12932 
12933 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12934 {
12935  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12936 
12937  const size_t blockCount = m_Blocks.size();
12938 
12939  pStats->size = 0;
12940  pStats->unusedSize = 0;
12941  pStats->allocationCount = 0;
12942  pStats->unusedRangeCount = 0;
12943  pStats->unusedRangeSizeMax = 0;
12944  pStats->blockCount = blockCount;
12945 
12946  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12947  {
12948  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12949  VMA_ASSERT(pBlock);
12950  VMA_HEAVY_ASSERT(pBlock->Validate());
12951  pBlock->m_pMetadata->AddPoolStats(*pStats);
12952  }
12953 }
12954 
12955 bool VmaBlockVector::IsEmpty()
12956 {
12957  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12958  return m_Blocks.empty();
12959 }
12960 
12961 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12962 {
12963  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12964  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12965  (VMA_DEBUG_MARGIN > 0) &&
12966  (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12967  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12968 }
12969 
12970 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12971 
12972 VkResult VmaBlockVector::Allocate(
12973  uint32_t currentFrameIndex,
12974  VkDeviceSize size,
12975  VkDeviceSize alignment,
12976  const VmaAllocationCreateInfo& createInfo,
12977  VmaSuballocationType suballocType,
12978  size_t allocationCount,
12979  VmaAllocation* pAllocations)
12980 {
12981  size_t allocIndex;
12982  VkResult res = VK_SUCCESS;
12983 
12984  if(IsCorruptionDetectionEnabled())
12985  {
12986  size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12987  alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12988  }
12989 
12990  {
12991  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12992  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12993  {
12994  res = AllocatePage(
12995  currentFrameIndex,
12996  size,
12997  alignment,
12998  createInfo,
12999  suballocType,
13000  pAllocations + allocIndex);
13001  if(res != VK_SUCCESS)
13002  {
13003  break;
13004  }
13005  }
13006  }
13007 
13008  if(res != VK_SUCCESS)
13009  {
13010  // Free all already created allocations.
13011  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13012  while(allocIndex--)
13013  {
13014  VmaAllocation_T* const alloc = pAllocations[allocIndex];
13015  const VkDeviceSize allocSize = alloc->GetSize();
13016  Free(alloc);
13017  m_hAllocator->m_Budget.RemoveAllocation(heapIndex, allocSize);
13018  }
13019  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
13020  }
13021 
13022  return res;
13023 }
13024 
13025 VkResult VmaBlockVector::AllocatePage(
13026  uint32_t currentFrameIndex,
13027  VkDeviceSize size,
13028  VkDeviceSize alignment,
13029  const VmaAllocationCreateInfo& createInfo,
13030  VmaSuballocationType suballocType,
13031  VmaAllocation* pAllocation)
13032 {
13033  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13034  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
13035  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13036  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13037 
13038  VkDeviceSize freeMemory;
13039  {
13040  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13041  VmaBudget heapBudget = {};
13042  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13043  freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
13044  }
13045 
13046  const bool canFallbackToDedicated = !IsCustomPool();
13047  const bool canCreateNewBlock =
13048  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
13049  (m_Blocks.size() < m_MaxBlockCount) &&
13050  (freeMemory >= size || !canFallbackToDedicated);
13051  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
13052 
13053  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
13054  // Which in turn is available only when maxBlockCount = 1.
13055  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
13056  {
13057  canMakeOtherLost = false;
13058  }
13059 
13060  // Upper address can only be used with linear allocator and within single memory block.
13061  if(isUpperAddress &&
13062  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
13063  {
13064  return VK_ERROR_FEATURE_NOT_PRESENT;
13065  }
13066 
13067  // Validate strategy.
13068  switch(strategy)
13069  {
13070  case 0:
13072  break;
13076  break;
13077  default:
13078  return VK_ERROR_FEATURE_NOT_PRESENT;
13079  }
13080 
13081  // Early reject: requested allocation size is larger that maximum block size for this block vector.
13082  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
13083  {
13084  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13085  }
13086 
13087  /*
13088  Under certain condition, this whole section can be skipped for optimization, so
13089  we move on directly to trying to allocate with canMakeOtherLost. That's the case
13090  e.g. for custom pools with linear algorithm.
13091  */
13092  if(!canMakeOtherLost || canCreateNewBlock)
13093  {
13094  // 1. Search existing allocations. Try to allocate without making other allocations lost.
13095  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
13097 
13098  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13099  {
13100  // Use only last block.
13101  if(!m_Blocks.empty())
13102  {
13103  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
13104  VMA_ASSERT(pCurrBlock);
13105  VkResult res = AllocateFromBlock(
13106  pCurrBlock,
13107  currentFrameIndex,
13108  size,
13109  alignment,
13110  allocFlagsCopy,
13111  createInfo.pUserData,
13112  suballocType,
13113  strategy,
13114  pAllocation);
13115  if(res == VK_SUCCESS)
13116  {
13117  VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
13118  return VK_SUCCESS;
13119  }
13120  }
13121  }
13122  else
13123  {
13125  {
13126  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13127  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13128  {
13129  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13130  VMA_ASSERT(pCurrBlock);
13131  VkResult res = AllocateFromBlock(
13132  pCurrBlock,
13133  currentFrameIndex,
13134  size,
13135  alignment,
13136  allocFlagsCopy,
13137  createInfo.pUserData,
13138  suballocType,
13139  strategy,
13140  pAllocation);
13141  if(res == VK_SUCCESS)
13142  {
13143  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13144  return VK_SUCCESS;
13145  }
13146  }
13147  }
13148  else // WORST_FIT, FIRST_FIT
13149  {
13150  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13151  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13152  {
13153  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13154  VMA_ASSERT(pCurrBlock);
13155  VkResult res = AllocateFromBlock(
13156  pCurrBlock,
13157  currentFrameIndex,
13158  size,
13159  alignment,
13160  allocFlagsCopy,
13161  createInfo.pUserData,
13162  suballocType,
13163  strategy,
13164  pAllocation);
13165  if(res == VK_SUCCESS)
13166  {
13167  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
13168  return VK_SUCCESS;
13169  }
13170  }
13171  }
13172  }
13173 
13174  // 2. Try to create new block.
13175  if(canCreateNewBlock)
13176  {
13177  // Calculate optimal size for new block.
13178  VkDeviceSize newBlockSize = m_PreferredBlockSize;
13179  uint32_t newBlockSizeShift = 0;
13180  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
13181 
13182  if(!m_ExplicitBlockSize)
13183  {
13184  // Allocate 1/8, 1/4, 1/2 as first blocks.
13185  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
13186  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
13187  {
13188  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13189  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
13190  {
13191  newBlockSize = smallerNewBlockSize;
13192  ++newBlockSizeShift;
13193  }
13194  else
13195  {
13196  break;
13197  }
13198  }
13199  }
13200 
13201  size_t newBlockIndex = 0;
13202  VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13203  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13204  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
13205  if(!m_ExplicitBlockSize)
13206  {
13207  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
13208  {
13209  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
13210  if(smallerNewBlockSize >= size)
13211  {
13212  newBlockSize = smallerNewBlockSize;
13213  ++newBlockSizeShift;
13214  res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
13215  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
13216  }
13217  else
13218  {
13219  break;
13220  }
13221  }
13222  }
13223 
13224  if(res == VK_SUCCESS)
13225  {
13226  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
13227  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
13228 
13229  res = AllocateFromBlock(
13230  pBlock,
13231  currentFrameIndex,
13232  size,
13233  alignment,
13234  allocFlagsCopy,
13235  createInfo.pUserData,
13236  suballocType,
13237  strategy,
13238  pAllocation);
13239  if(res == VK_SUCCESS)
13240  {
13241  VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
13242  return VK_SUCCESS;
13243  }
13244  else
13245  {
13246  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
13247  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13248  }
13249  }
13250  }
13251  }
13252 
13253  // 3. Try to allocate from existing blocks with making other allocations lost.
13254  if(canMakeOtherLost)
13255  {
13256  uint32_t tryIndex = 0;
13257  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
13258  {
13259  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
13260  VmaAllocationRequest bestRequest = {};
13261  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
13262 
13263  // 1. Search existing allocations.
13265  {
13266  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
13267  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
13268  {
13269  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13270  VMA_ASSERT(pCurrBlock);
13271  VmaAllocationRequest currRequest = {};
13272  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13273  currentFrameIndex,
13274  m_FrameInUseCount,
13275  m_BufferImageGranularity,
13276  size,
13277  alignment,
13278  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13279  suballocType,
13280  canMakeOtherLost,
13281  strategy,
13282  &currRequest))
13283  {
13284  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13285  if(pBestRequestBlock == VMA_NULL ||
13286  currRequestCost < bestRequestCost)
13287  {
13288  pBestRequestBlock = pCurrBlock;
13289  bestRequest = currRequest;
13290  bestRequestCost = currRequestCost;
13291 
13292  if(bestRequestCost == 0)
13293  {
13294  break;
13295  }
13296  }
13297  }
13298  }
13299  }
13300  else // WORST_FIT, FIRST_FIT
13301  {
13302  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13303  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13304  {
13305  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13306  VMA_ASSERT(pCurrBlock);
13307  VmaAllocationRequest currRequest = {};
13308  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13309  currentFrameIndex,
13310  m_FrameInUseCount,
13311  m_BufferImageGranularity,
13312  size,
13313  alignment,
13314  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13315  suballocType,
13316  canMakeOtherLost,
13317  strategy,
13318  &currRequest))
13319  {
13320  const VkDeviceSize currRequestCost = currRequest.CalcCost();
13321  if(pBestRequestBlock == VMA_NULL ||
13322  currRequestCost < bestRequestCost ||
13324  {
13325  pBestRequestBlock = pCurrBlock;
13326  bestRequest = currRequest;
13327  bestRequestCost = currRequestCost;
13328 
13329  if(bestRequestCost == 0 ||
13331  {
13332  break;
13333  }
13334  }
13335  }
13336  }
13337  }
13338 
13339  if(pBestRequestBlock != VMA_NULL)
13340  {
13341  if(mapped)
13342  {
13343  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13344  if(res != VK_SUCCESS)
13345  {
13346  return res;
13347  }
13348  }
13349 
13350  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13351  currentFrameIndex,
13352  m_FrameInUseCount,
13353  &bestRequest))
13354  {
13355  // Allocate from this pBlock.
13356  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13357  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13358  UpdateHasEmptyBlock();
13359  (*pAllocation)->InitBlockAllocation(
13360  pBestRequestBlock,
13361  bestRequest.offset,
13362  alignment,
13363  size,
13364  m_MemoryTypeIndex,
13365  suballocType,
13366  mapped,
13367  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13368  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13369  VMA_DEBUG_LOG(" Returned from existing block");
13370  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13371  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13372  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13373  {
13374  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13375  }
13376  if(IsCorruptionDetectionEnabled())
13377  {
13378  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13379  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13380  }
13381  return VK_SUCCESS;
13382  }
13383  // else: Some allocations must have been touched while we are here. Next try.
13384  }
13385  else
13386  {
13387  // Could not find place in any of the blocks - break outer loop.
13388  break;
13389  }
13390  }
13391  /* Maximum number of tries exceeded - a very unlike event when many other
13392  threads are simultaneously touching allocations making it impossible to make
13393  lost at the same time as we try to allocate. */
13394  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13395  {
13396  return VK_ERROR_TOO_MANY_OBJECTS;
13397  }
13398  }
13399 
13400  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13401 }
13402 
13403 void VmaBlockVector::Free(
13404  const VmaAllocation hAllocation)
13405 {
13406  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13407 
13408  bool budgetExceeded = false;
13409  {
13410  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13411  VmaBudget heapBudget = {};
13412  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13413  budgetExceeded = heapBudget.usage >= heapBudget.budget;
13414  }
13415 
13416  // Scope for lock.
13417  {
13418  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13419 
13420  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13421 
13422  if(IsCorruptionDetectionEnabled())
13423  {
13424  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13425  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13426  }
13427 
13428  if(hAllocation->IsPersistentMap())
13429  {
13430  pBlock->Unmap(m_hAllocator, 1);
13431  }
13432 
13433  pBlock->m_pMetadata->Free(hAllocation);
13434  VMA_HEAVY_ASSERT(pBlock->Validate());
13435 
13436  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13437 
13438  const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13439  // pBlock became empty after this deallocation.
13440  if(pBlock->m_pMetadata->IsEmpty())
13441  {
13442  // Already has empty block. We don't want to have two, so delete this one.
13443  if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13444  {
13445  pBlockToDelete = pBlock;
13446  Remove(pBlock);
13447  }
13448  // else: We now have an empty block - leave it.
13449  }
13450  // pBlock didn't become empty, but we have another empty block - find and free that one.
13451  // (This is optional, heuristics.)
13452  else if(m_HasEmptyBlock && canDeleteBlock)
13453  {
13454  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13455  if(pLastBlock->m_pMetadata->IsEmpty())
13456  {
13457  pBlockToDelete = pLastBlock;
13458  m_Blocks.pop_back();
13459  }
13460  }
13461 
13462  UpdateHasEmptyBlock();
13463  IncrementallySortBlocks();
13464  }
13465 
13466  // Destruction of a free block. Deferred until this point, outside of mutex
13467  // lock, for performance reason.
13468  if(pBlockToDelete != VMA_NULL)
13469  {
13470  VMA_DEBUG_LOG(" Deleted empty block");
13471  pBlockToDelete->Destroy(m_hAllocator);
13472  vma_delete(m_hAllocator, pBlockToDelete);
13473  }
13474 }
13475 
13476 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13477 {
13478  VkDeviceSize result = 0;
13479  for(size_t i = m_Blocks.size(); i--; )
13480  {
13481  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13482  if(result >= m_PreferredBlockSize)
13483  {
13484  break;
13485  }
13486  }
13487  return result;
13488 }
13489 
13490 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13491 {
13492  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13493  {
13494  if(m_Blocks[blockIndex] == pBlock)
13495  {
13496  VmaVectorRemove(m_Blocks, blockIndex);
13497  return;
13498  }
13499  }
13500  VMA_ASSERT(0);
13501 }
13502 
13503 void VmaBlockVector::IncrementallySortBlocks()
13504 {
13505  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13506  {
13507  // Bubble sort only until first swap.
13508  for(size_t i = 1; i < m_Blocks.size(); ++i)
13509  {
13510  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13511  {
13512  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13513  return;
13514  }
13515  }
13516  }
13517 }
13518 
13519 VkResult VmaBlockVector::AllocateFromBlock(
13520  VmaDeviceMemoryBlock* pBlock,
13521  uint32_t currentFrameIndex,
13522  VkDeviceSize size,
13523  VkDeviceSize alignment,
13524  VmaAllocationCreateFlags allocFlags,
13525  void* pUserData,
13526  VmaSuballocationType suballocType,
13527  uint32_t strategy,
13528  VmaAllocation* pAllocation)
13529 {
13530  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13531  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13532  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13533  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13534 
13535  VmaAllocationRequest currRequest = {};
13536  if(pBlock->m_pMetadata->CreateAllocationRequest(
13537  currentFrameIndex,
13538  m_FrameInUseCount,
13539  m_BufferImageGranularity,
13540  size,
13541  alignment,
13542  isUpperAddress,
13543  suballocType,
13544  false, // canMakeOtherLost
13545  strategy,
13546  &currRequest))
13547  {
13548  // Allocate from pCurrBlock.
13549  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13550 
13551  if(mapped)
13552  {
13553  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13554  if(res != VK_SUCCESS)
13555  {
13556  return res;
13557  }
13558  }
13559 
13560  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13561  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13562  UpdateHasEmptyBlock();
13563  (*pAllocation)->InitBlockAllocation(
13564  pBlock,
13565  currRequest.offset,
13566  alignment,
13567  size,
13568  m_MemoryTypeIndex,
13569  suballocType,
13570  mapped,
13571  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13572  VMA_HEAVY_ASSERT(pBlock->Validate());
13573  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13574  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13575  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13576  {
13577  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13578  }
13579  if(IsCorruptionDetectionEnabled())
13580  {
13581  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13582  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13583  }
13584  return VK_SUCCESS;
13585  }
13586  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13587 }
13588 
13589 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13590 {
13591  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13592  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13593  allocInfo.allocationSize = blockSize;
13594 
13595 #if VMA_BUFFER_DEVICE_ADDRESS
13596  // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13597  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13598  if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13599  {
13600  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13601  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13602  }
13603 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13604 
13605 #if VMA_MEMORY_PRIORITY
13606  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
13607  if(m_hAllocator->m_UseExtMemoryPriority)
13608  {
13609  priorityInfo.priority = m_Priority;
13610  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
13611  }
13612 #endif // #if VMA_MEMORY_PRIORITY
13613 
13614  VkDeviceMemory mem = VK_NULL_HANDLE;
13615  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13616  if(res < 0)
13617  {
13618  return res;
13619  }
13620 
13621  // New VkDeviceMemory successfully created.
13622 
13623  // Create new Allocation for it.
13624  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13625  pBlock->Init(
13626  m_hAllocator,
13627  m_hParentPool,
13628  m_MemoryTypeIndex,
13629  mem,
13630  allocInfo.allocationSize,
13631  m_NextBlockId++,
13632  m_Algorithm);
13633 
13634  m_Blocks.push_back(pBlock);
13635  if(pNewBlockIndex != VMA_NULL)
13636  {
13637  *pNewBlockIndex = m_Blocks.size() - 1;
13638  }
13639 
13640  return VK_SUCCESS;
13641 }
13642 
13643 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13644  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13645  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13646 {
13647  const size_t blockCount = m_Blocks.size();
13648  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13649 
13650  enum BLOCK_FLAG
13651  {
13652  BLOCK_FLAG_USED = 0x00000001,
13653  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13654  };
13655 
13656  struct BlockInfo
13657  {
13658  uint32_t flags;
13659  void* pMappedData;
13660  };
13661  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13662  blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13663  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13664 
13665  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13666  const size_t moveCount = moves.size();
13667  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13668  {
13669  const VmaDefragmentationMove& move = moves[moveIndex];
13670  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13671  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13672  }
13673 
13674  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13675 
13676  // Go over all blocks. Get mapped pointer or map if necessary.
13677  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13678  {
13679  BlockInfo& currBlockInfo = blockInfo[blockIndex];
13680  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13681  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13682  {
13683  currBlockInfo.pMappedData = pBlock->GetMappedData();
13684  // It is not originally mapped - map it.
13685  if(currBlockInfo.pMappedData == VMA_NULL)
13686  {
13687  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13688  if(pDefragCtx->res == VK_SUCCESS)
13689  {
13690  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13691  }
13692  }
13693  }
13694  }
13695 
13696  // Go over all moves. Do actual data transfer.
13697  if(pDefragCtx->res == VK_SUCCESS)
13698  {
13699  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13700  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13701 
13702  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13703  {
13704  const VmaDefragmentationMove& move = moves[moveIndex];
13705 
13706  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13707  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13708 
13709  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13710 
13711  // Invalidate source.
13712  if(isNonCoherent)
13713  {
13714  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13715  memRange.memory = pSrcBlock->GetDeviceMemory();
13716  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13717  memRange.size = VMA_MIN(
13718  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13719  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13720  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13721  }
13722 
13723  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13724  memmove(
13725  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13726  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13727  static_cast<size_t>(move.size));
13728 
13729  if(IsCorruptionDetectionEnabled())
13730  {
13731  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13732  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13733  }
13734 
13735  // Flush destination.
13736  if(isNonCoherent)
13737  {
13738  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13739  memRange.memory = pDstBlock->GetDeviceMemory();
13740  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13741  memRange.size = VMA_MIN(
13742  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13743  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13744  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13745  }
13746  }
13747  }
13748 
13749  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13750  // Regardless of pCtx->res == VK_SUCCESS.
13751  for(size_t blockIndex = blockCount; blockIndex--; )
13752  {
13753  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13754  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13755  {
13756  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13757  pBlock->Unmap(m_hAllocator, 1);
13758  }
13759  }
13760 }
13761 
13762 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13763  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13764  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13765  VkCommandBuffer commandBuffer)
13766 {
13767  const size_t blockCount = m_Blocks.size();
13768 
13769  pDefragCtx->blockContexts.resize(blockCount);
13770  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13771 
13772  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13773  const size_t moveCount = moves.size();
13774  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13775  {
13776  const VmaDefragmentationMove& move = moves[moveIndex];
13777 
13778  //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13779  {
13780  // Old school move still require us to map the whole block
13781  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13782  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13783  }
13784  }
13785 
13786  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13787 
13788  // Go over all blocks. Create and bind buffer for whole block if necessary.
13789  {
13790  VkBufferCreateInfo bufCreateInfo;
13791  VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13792 
13793  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13794  {
13795  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13796  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13797  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13798  {
13799  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13800  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13801  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13802  if(pDefragCtx->res == VK_SUCCESS)
13803  {
13804  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13805  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13806  }
13807  }
13808  }
13809  }
13810 
13811  // Go over all moves. Post data transfer commands to command buffer.
13812  if(pDefragCtx->res == VK_SUCCESS)
13813  {
13814  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13815  {
13816  const VmaDefragmentationMove& move = moves[moveIndex];
13817 
13818  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13819  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13820 
13821  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13822 
13823  VkBufferCopy region = {
13824  move.srcOffset,
13825  move.dstOffset,
13826  move.size };
13827  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13828  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13829  }
13830  }
13831 
13832  // Save buffers to defrag context for later destruction.
13833  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13834  {
13835  pDefragCtx->res = VK_NOT_READY;
13836  }
13837 }
13838 
13839 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13840 {
13841  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13842  {
13843  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13844  if(pBlock->m_pMetadata->IsEmpty())
13845  {
13846  if(m_Blocks.size() > m_MinBlockCount)
13847  {
13848  if(pDefragmentationStats != VMA_NULL)
13849  {
13850  ++pDefragmentationStats->deviceMemoryBlocksFreed;
13851  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13852  }
13853 
13854  VmaVectorRemove(m_Blocks, blockIndex);
13855  pBlock->Destroy(m_hAllocator);
13856  vma_delete(m_hAllocator, pBlock);
13857  }
13858  else
13859  {
13860  break;
13861  }
13862  }
13863  }
13864  UpdateHasEmptyBlock();
13865 }
13866 
13867 void VmaBlockVector::UpdateHasEmptyBlock()
13868 {
13869  m_HasEmptyBlock = false;
13870  for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13871  {
13872  VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13873  if(pBlock->m_pMetadata->IsEmpty())
13874  {
13875  m_HasEmptyBlock = true;
13876  break;
13877  }
13878  }
13879 }
13880 
13881 #if VMA_STATS_STRING_ENABLED
13882 
13883 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13884 {
13885  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13886 
13887  json.BeginObject();
13888 
13889  if(IsCustomPool())
13890  {
13891  const char* poolName = m_hParentPool->GetName();
13892  if(poolName != VMA_NULL && poolName[0] != '\0')
13893  {
13894  json.WriteString("Name");
13895  json.WriteString(poolName);
13896  }
13897 
13898  json.WriteString("MemoryTypeIndex");
13899  json.WriteNumber(m_MemoryTypeIndex);
13900 
13901  json.WriteString("BlockSize");
13902  json.WriteNumber(m_PreferredBlockSize);
13903 
13904  json.WriteString("BlockCount");
13905  json.BeginObject(true);
13906  if(m_MinBlockCount > 0)
13907  {
13908  json.WriteString("Min");
13909  json.WriteNumber((uint64_t)m_MinBlockCount);
13910  }
13911  if(m_MaxBlockCount < SIZE_MAX)
13912  {
13913  json.WriteString("Max");
13914  json.WriteNumber((uint64_t)m_MaxBlockCount);
13915  }
13916  json.WriteString("Cur");
13917  json.WriteNumber((uint64_t)m_Blocks.size());
13918  json.EndObject();
13919 
13920  if(m_FrameInUseCount > 0)
13921  {
13922  json.WriteString("FrameInUseCount");
13923  json.WriteNumber(m_FrameInUseCount);
13924  }
13925 
13926  if(m_Algorithm != 0)
13927  {
13928  json.WriteString("Algorithm");
13929  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13930  }
13931  }
13932  else
13933  {
13934  json.WriteString("PreferredBlockSize");
13935  json.WriteNumber(m_PreferredBlockSize);
13936  }
13937 
13938  json.WriteString("Blocks");
13939  json.BeginObject();
13940  for(size_t i = 0; i < m_Blocks.size(); ++i)
13941  {
13942  json.BeginString();
13943  json.ContinueString(m_Blocks[i]->GetId());
13944  json.EndString();
13945 
13946  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13947  }
13948  json.EndObject();
13949 
13950  json.EndObject();
13951 }
13952 
13953 #endif // #if VMA_STATS_STRING_ENABLED
13954 
13955 void VmaBlockVector::Defragment(
13956  class VmaBlockVectorDefragmentationContext* pCtx,
13958  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13959  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13960  VkCommandBuffer commandBuffer)
13961 {
13962  pCtx->res = VK_SUCCESS;
13963 
13964  const VkMemoryPropertyFlags memPropFlags =
13965  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13966  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13967 
13968  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13969  isHostVisible;
13970  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13971  !IsCorruptionDetectionEnabled() &&
13972  ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13973 
13974  // There are options to defragment this memory type.
13975  if(canDefragmentOnCpu || canDefragmentOnGpu)
13976  {
13977  bool defragmentOnGpu;
13978  // There is only one option to defragment this memory type.
13979  if(canDefragmentOnGpu != canDefragmentOnCpu)
13980  {
13981  defragmentOnGpu = canDefragmentOnGpu;
13982  }
13983  // Both options are available: Heuristics to choose the best one.
13984  else
13985  {
13986  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13987  m_hAllocator->IsIntegratedGpu();
13988  }
13989 
13990  bool overlappingMoveSupported = !defragmentOnGpu;
13991 
13992  if(m_hAllocator->m_UseMutex)
13993  {
13995  {
13996  if(!m_Mutex.TryLockWrite())
13997  {
13998  pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13999  return;
14000  }
14001  }
14002  else
14003  {
14004  m_Mutex.LockWrite();
14005  pCtx->mutexLocked = true;
14006  }
14007  }
14008 
14009  pCtx->Begin(overlappingMoveSupported, flags);
14010 
14011  // Defragment.
14012 
14013  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
14014  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
14015  pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
14016 
14017  // Accumulate statistics.
14018  if(pStats != VMA_NULL)
14019  {
14020  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
14021  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
14022  pStats->bytesMoved += bytesMoved;
14023  pStats->allocationsMoved += allocationsMoved;
14024  VMA_ASSERT(bytesMoved <= maxBytesToMove);
14025  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
14026  if(defragmentOnGpu)
14027  {
14028  maxGpuBytesToMove -= bytesMoved;
14029  maxGpuAllocationsToMove -= allocationsMoved;
14030  }
14031  else
14032  {
14033  maxCpuBytesToMove -= bytesMoved;
14034  maxCpuAllocationsToMove -= allocationsMoved;
14035  }
14036  }
14037 
14039  {
14040  if(m_hAllocator->m_UseMutex)
14041  m_Mutex.UnlockWrite();
14042 
14043  if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
14044  pCtx->res = VK_NOT_READY;
14045 
14046  return;
14047  }
14048 
14049  if(pCtx->res >= VK_SUCCESS)
14050  {
14051  if(defragmentOnGpu)
14052  {
14053  ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
14054  }
14055  else
14056  {
14057  ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
14058  }
14059  }
14060  }
14061 }
14062 
14063 void VmaBlockVector::DefragmentationEnd(
14064  class VmaBlockVectorDefragmentationContext* pCtx,
14065  uint32_t flags,
14066  VmaDefragmentationStats* pStats)
14067 {
14068  if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
14069  {
14070  VMA_ASSERT(pCtx->mutexLocked == false);
14071 
14072  // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
14073  // lock protecting us. Since we mutate state here, we have to take the lock out now
14074  m_Mutex.LockWrite();
14075  pCtx->mutexLocked = true;
14076  }
14077 
14078  // If the mutex isn't locked we didn't do any work and there is nothing to delete.
14079  if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
14080  {
14081  // Destroy buffers.
14082  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
14083  {
14084  VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
14085  if(blockCtx.hBuffer)
14086  {
14087  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
14088  }
14089  }
14090 
14091  if(pCtx->res >= VK_SUCCESS)
14092  {
14093  FreeEmptyBlocks(pStats);
14094  }
14095  }
14096 
14097  if(pCtx->mutexLocked)
14098  {
14099  VMA_ASSERT(m_hAllocator->m_UseMutex);
14100  m_Mutex.UnlockWrite();
14101  }
14102 }
14103 
14104 uint32_t VmaBlockVector::ProcessDefragmentations(
14105  class VmaBlockVectorDefragmentationContext *pCtx,
14106  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
14107 {
14108  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14109 
14110  const uint32_t moveCount = VMA_MIN(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
14111 
14112  for(uint32_t i = 0; i < moveCount; ++ i)
14113  {
14114  VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
14115 
14116  pMove->allocation = move.hAllocation;
14117  pMove->memory = move.pDstBlock->GetDeviceMemory();
14118  pMove->offset = move.dstOffset;
14119 
14120  ++ pMove;
14121  }
14122 
14123  pCtx->defragmentationMovesProcessed += moveCount;
14124 
14125  return moveCount;
14126 }
14127 
14128 void VmaBlockVector::CommitDefragmentations(
14129  class VmaBlockVectorDefragmentationContext *pCtx,
14130  VmaDefragmentationStats* pStats)
14131 {
14132  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14133 
14134  for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
14135  {
14136  const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
14137 
14138  move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
14139  move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
14140  }
14141 
14142  pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
14143  FreeEmptyBlocks(pStats);
14144 }
14145 
14146 size_t VmaBlockVector::CalcAllocationCount() const
14147 {
14148  size_t result = 0;
14149  for(size_t i = 0; i < m_Blocks.size(); ++i)
14150  {
14151  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
14152  }
14153  return result;
14154 }
14155 
14156 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
14157 {
14158  if(m_BufferImageGranularity == 1)
14159  {
14160  return false;
14161  }
14162  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
14163  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
14164  {
14165  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
14166  VMA_ASSERT(m_Algorithm == 0);
14167  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
14168  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
14169  {
14170  return true;
14171  }
14172  }
14173  return false;
14174 }
14175 
14176 void VmaBlockVector::MakePoolAllocationsLost(
14177  uint32_t currentFrameIndex,
14178  size_t* pLostAllocationCount)
14179 {
14180  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
14181  size_t lostAllocationCount = 0;
14182  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14183  {
14184  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14185  VMA_ASSERT(pBlock);
14186  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
14187  }
14188  if(pLostAllocationCount != VMA_NULL)
14189  {
14190  *pLostAllocationCount = lostAllocationCount;
14191  }
14192 }
14193 
14194 VkResult VmaBlockVector::CheckCorruption()
14195 {
14196  if(!IsCorruptionDetectionEnabled())
14197  {
14198  return VK_ERROR_FEATURE_NOT_PRESENT;
14199  }
14200 
14201  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14202  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14203  {
14204  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14205  VMA_ASSERT(pBlock);
14206  VkResult res = pBlock->CheckCorruption(m_hAllocator);
14207  if(res != VK_SUCCESS)
14208  {
14209  return res;
14210  }
14211  }
14212  return VK_SUCCESS;
14213 }
14214 
14215 void VmaBlockVector::AddStats(VmaStats* pStats)
14216 {
14217  const uint32_t memTypeIndex = m_MemoryTypeIndex;
14218  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
14219 
14220  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
14221 
14222  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
14223  {
14224  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
14225  VMA_ASSERT(pBlock);
14226  VMA_HEAVY_ASSERT(pBlock->Validate());
14227  VmaStatInfo allocationStatInfo;
14228  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
14229  VmaAddStatInfo(pStats->total, allocationStatInfo);
14230  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14231  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14232  }
14233 }
14234 
14236 // VmaDefragmentationAlgorithm_Generic members definition
14237 
14238 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
14239  VmaAllocator hAllocator,
14240  VmaBlockVector* pBlockVector,
14241  uint32_t currentFrameIndex,
14242  bool overlappingMoveSupported) :
14243  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14244  m_AllocationCount(0),
14245  m_AllAllocations(false),
14246  m_BytesMoved(0),
14247  m_AllocationsMoved(0),
14248  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
14249 {
14250  // Create block info for each block.
14251  const size_t blockCount = m_pBlockVector->m_Blocks.size();
14252  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14253  {
14254  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
14255  pBlockInfo->m_OriginalBlockIndex = blockIndex;
14256  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
14257  m_Blocks.push_back(pBlockInfo);
14258  }
14259 
14260  // Sort them by m_pBlock pointer value.
14261  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
14262 }
14263 
14264 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14265 {
14266  for(size_t i = m_Blocks.size(); i--; )
14267  {
14268  vma_delete(m_hAllocator, m_Blocks[i]);
14269  }
14270 }
14271 
14272 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14273 {
14274  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14275  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14276  {
14277  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14278  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14279  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14280  {
14281  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14282  (*it)->m_Allocations.push_back(allocInfo);
14283  }
14284  else
14285  {
14286  VMA_ASSERT(0);
14287  }
14288 
14289  ++m_AllocationCount;
14290  }
14291 }
14292 
14293 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14294  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14295  VkDeviceSize maxBytesToMove,
14296  uint32_t maxAllocationsToMove,
14297  bool freeOldAllocations)
14298 {
14299  if(m_Blocks.empty())
14300  {
14301  return VK_SUCCESS;
14302  }
14303 
14304  // This is a choice based on research.
14305  // Option 1:
14306  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14307  // Option 2:
14308  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14309  // Option 3:
14310  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14311 
14312  size_t srcBlockMinIndex = 0;
14313  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14314  /*
14315  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14316  {
14317  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14318  if(blocksWithNonMovableCount > 0)
14319  {
14320  srcBlockMinIndex = blocksWithNonMovableCount - 1;
14321  }
14322  }
14323  */
14324 
14325  size_t srcBlockIndex = m_Blocks.size() - 1;
14326  size_t srcAllocIndex = SIZE_MAX;
14327  for(;;)
14328  {
14329  // 1. Find next allocation to move.
14330  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14331  // 1.2. Then start from last to first m_Allocations.
14332  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14333  {
14334  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14335  {
14336  // Finished: no more allocations to process.
14337  if(srcBlockIndex == srcBlockMinIndex)
14338  {
14339  return VK_SUCCESS;
14340  }
14341  else
14342  {
14343  --srcBlockIndex;
14344  srcAllocIndex = SIZE_MAX;
14345  }
14346  }
14347  else
14348  {
14349  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14350  }
14351  }
14352 
14353  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14354  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14355 
14356  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14357  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14358  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14359  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14360 
14361  // 2. Try to find new place for this allocation in preceding or current block.
14362  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14363  {
14364  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14365  VmaAllocationRequest dstAllocRequest;
14366  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14367  m_CurrentFrameIndex,
14368  m_pBlockVector->GetFrameInUseCount(),
14369  m_pBlockVector->GetBufferImageGranularity(),
14370  size,
14371  alignment,
14372  false, // upperAddress
14373  suballocType,
14374  false, // canMakeOtherLost
14375  strategy,
14376  &dstAllocRequest) &&
14377  MoveMakesSense(
14378  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14379  {
14380  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14381 
14382  // Reached limit on number of allocations or bytes to move.
14383  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14384  (m_BytesMoved + size > maxBytesToMove))
14385  {
14386  return VK_SUCCESS;
14387  }
14388 
14389  VmaDefragmentationMove move = {};
14390  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14391  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14392  move.srcOffset = srcOffset;
14393  move.dstOffset = dstAllocRequest.offset;
14394  move.size = size;
14395  move.hAllocation = allocInfo.m_hAllocation;
14396  move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14397  move.pDstBlock = pDstBlockInfo->m_pBlock;
14398 
14399  moves.push_back(move);
14400 
14401  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14402  dstAllocRequest,
14403  suballocType,
14404  size,
14405  allocInfo.m_hAllocation);
14406 
14407  if(freeOldAllocations)
14408  {
14409  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14410  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14411  }
14412 
14413  if(allocInfo.m_pChanged != VMA_NULL)
14414  {
14415  *allocInfo.m_pChanged = VK_TRUE;
14416  }
14417 
14418  ++m_AllocationsMoved;
14419  m_BytesMoved += size;
14420 
14421  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14422 
14423  break;
14424  }
14425  }
14426 
14427  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14428 
14429  if(srcAllocIndex > 0)
14430  {
14431  --srcAllocIndex;
14432  }
14433  else
14434  {
14435  if(srcBlockIndex > 0)
14436  {
14437  --srcBlockIndex;
14438  srcAllocIndex = SIZE_MAX;
14439  }
14440  else
14441  {
14442  return VK_SUCCESS;
14443  }
14444  }
14445  }
14446 }
14447 
14448 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14449 {
14450  size_t result = 0;
14451  for(size_t i = 0; i < m_Blocks.size(); ++i)
14452  {
14453  if(m_Blocks[i]->m_HasNonMovableAllocations)
14454  {
14455  ++result;
14456  }
14457  }
14458  return result;
14459 }
14460 
14461 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14462  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14463  VkDeviceSize maxBytesToMove,
14464  uint32_t maxAllocationsToMove,
14466 {
14467  if(!m_AllAllocations && m_AllocationCount == 0)
14468  {
14469  return VK_SUCCESS;
14470  }
14471 
14472  const size_t blockCount = m_Blocks.size();
14473  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14474  {
14475  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14476 
14477  if(m_AllAllocations)
14478  {
14479  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14480  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14481  it != pMetadata->m_Suballocations.end();
14482  ++it)
14483  {
14484  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14485  {
14486  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14487  pBlockInfo->m_Allocations.push_back(allocInfo);
14488  }
14489  }
14490  }
14491 
14492  pBlockInfo->CalcHasNonMovableAllocations();
14493 
14494  // This is a choice based on research.
14495  // Option 1:
14496  pBlockInfo->SortAllocationsByOffsetDescending();
14497  // Option 2:
14498  //pBlockInfo->SortAllocationsBySizeDescending();
14499  }
14500 
14501  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14502  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14503 
14504  // This is a choice based on research.
14505  const uint32_t roundCount = 2;
14506 
14507  // Execute defragmentation rounds (the main part).
14508  VkResult result = VK_SUCCESS;
14509  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14510  {
14511  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14512  }
14513 
14514  return result;
14515 }
14516 
14517 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14518  size_t dstBlockIndex, VkDeviceSize dstOffset,
14519  size_t srcBlockIndex, VkDeviceSize srcOffset)
14520 {
14521  if(dstBlockIndex < srcBlockIndex)
14522  {
14523  return true;
14524  }
14525  if(dstBlockIndex > srcBlockIndex)
14526  {
14527  return false;
14528  }
14529  if(dstOffset < srcOffset)
14530  {
14531  return true;
14532  }
14533  return false;
14534 }
14535 
14537 // VmaDefragmentationAlgorithm_Fast
14538 
14539 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14540  VmaAllocator hAllocator,
14541  VmaBlockVector* pBlockVector,
14542  uint32_t currentFrameIndex,
14543  bool overlappingMoveSupported) :
14544  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14545  m_OverlappingMoveSupported(overlappingMoveSupported),
14546  m_AllocationCount(0),
14547  m_AllAllocations(false),
14548  m_BytesMoved(0),
14549  m_AllocationsMoved(0),
14550  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14551 {
14552  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14553 
14554 }
14555 
14556 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14557 {
14558 }
14559 
14560 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14561  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14562  VkDeviceSize maxBytesToMove,
14563  uint32_t maxAllocationsToMove,
14565 {
14566  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14567 
14568  const size_t blockCount = m_pBlockVector->GetBlockCount();
14569  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14570  {
14571  return VK_SUCCESS;
14572  }
14573 
14574  PreprocessMetadata();
14575 
14576  // Sort blocks in order from most destination.
14577 
14578  m_BlockInfos.resize(blockCount);
14579  for(size_t i = 0; i < blockCount; ++i)
14580  {
14581  m_BlockInfos[i].origBlockIndex = i;
14582  }
14583 
14584  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14585  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14586  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14587  });
14588 
14589  // THE MAIN ALGORITHM
14590 
14591  FreeSpaceDatabase freeSpaceDb;
14592 
14593  size_t dstBlockInfoIndex = 0;
14594  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14595  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14596  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14597  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14598  VkDeviceSize dstOffset = 0;
14599 
14600  bool end = false;
14601  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14602  {
14603  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14604  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14605  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14606  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14607  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14608  {
14609  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14610  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14611  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14612  if(m_AllocationsMoved == maxAllocationsToMove ||
14613  m_BytesMoved + srcAllocSize > maxBytesToMove)
14614  {
14615  end = true;
14616  break;
14617  }
14618  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14619 
14620  VmaDefragmentationMove move = {};
14621  // Try to place it in one of free spaces from the database.
14622  size_t freeSpaceInfoIndex;
14623  VkDeviceSize dstAllocOffset;
14624  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14625  freeSpaceInfoIndex, dstAllocOffset))
14626  {
14627  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14628  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14629  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14630 
14631  // Same block
14632  if(freeSpaceInfoIndex == srcBlockInfoIndex)
14633  {
14634  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14635 
14636  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14637 
14638  VmaSuballocation suballoc = *srcSuballocIt;
14639  suballoc.offset = dstAllocOffset;
14640  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14641  m_BytesMoved += srcAllocSize;
14642  ++m_AllocationsMoved;
14643 
14644  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14645  ++nextSuballocIt;
14646  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14647  srcSuballocIt = nextSuballocIt;
14648 
14649  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14650 
14651  move.srcBlockIndex = srcOrigBlockIndex;
14652  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14653  move.srcOffset = srcAllocOffset;
14654  move.dstOffset = dstAllocOffset;
14655  move.size = srcAllocSize;
14656 
14657  moves.push_back(move);
14658  }
14659  // Different block
14660  else
14661  {
14662  // MOVE OPTION 2: Move the allocation to a different block.
14663 
14664  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14665 
14666  VmaSuballocation suballoc = *srcSuballocIt;
14667  suballoc.offset = dstAllocOffset;
14668  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14669  m_BytesMoved += srcAllocSize;
14670  ++m_AllocationsMoved;
14671 
14672  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14673  ++nextSuballocIt;
14674  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14675  srcSuballocIt = nextSuballocIt;
14676 
14677  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14678 
14679  move.srcBlockIndex = srcOrigBlockIndex;
14680  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14681  move.srcOffset = srcAllocOffset;
14682  move.dstOffset = dstAllocOffset;
14683  move.size = srcAllocSize;
14684 
14685  moves.push_back(move);
14686  }
14687  }
14688  else
14689  {
14690  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14691 
14692  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14693  while(dstBlockInfoIndex < srcBlockInfoIndex &&
14694  dstAllocOffset + srcAllocSize > dstBlockSize)
14695  {
14696  // But before that, register remaining free space at the end of dst block.
14697  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14698 
14699  ++dstBlockInfoIndex;
14700  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14701  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14702  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14703  dstBlockSize = pDstMetadata->GetSize();
14704  dstOffset = 0;
14705  dstAllocOffset = 0;
14706  }
14707 
14708  // Same block
14709  if(dstBlockInfoIndex == srcBlockInfoIndex)
14710  {
14711  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14712 
14713  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14714 
14715  bool skipOver = overlap;
14716  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14717  {
14718  // If destination and source place overlap, skip if it would move it
14719  // by only < 1/64 of its size.
14720  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14721  }
14722 
14723  if(skipOver)
14724  {
14725  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14726 
14727  dstOffset = srcAllocOffset + srcAllocSize;
14728  ++srcSuballocIt;
14729  }
14730  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14731  else
14732  {
14733  srcSuballocIt->offset = dstAllocOffset;
14734  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14735  dstOffset = dstAllocOffset + srcAllocSize;
14736  m_BytesMoved += srcAllocSize;
14737  ++m_AllocationsMoved;
14738  ++srcSuballocIt;
14739 
14740  move.srcBlockIndex = srcOrigBlockIndex;
14741  move.dstBlockIndex = dstOrigBlockIndex;
14742  move.srcOffset = srcAllocOffset;
14743  move.dstOffset = dstAllocOffset;
14744  move.size = srcAllocSize;
14745 
14746  moves.push_back(move);
14747  }
14748  }
14749  // Different block
14750  else
14751  {
14752  // MOVE OPTION 2: Move the allocation to a different block.
14753 
14754  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14755  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14756 
14757  VmaSuballocation suballoc = *srcSuballocIt;
14758  suballoc.offset = dstAllocOffset;
14759  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14760  dstOffset = dstAllocOffset + srcAllocSize;
14761  m_BytesMoved += srcAllocSize;
14762  ++m_AllocationsMoved;
14763 
14764  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14765  ++nextSuballocIt;
14766  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14767  srcSuballocIt = nextSuballocIt;
14768 
14769  pDstMetadata->m_Suballocations.push_back(suballoc);
14770 
14771  move.srcBlockIndex = srcOrigBlockIndex;
14772  move.dstBlockIndex = dstOrigBlockIndex;
14773  move.srcOffset = srcAllocOffset;
14774  move.dstOffset = dstAllocOffset;
14775  move.size = srcAllocSize;
14776 
14777  moves.push_back(move);
14778  }
14779  }
14780  }
14781  }
14782 
14783  m_BlockInfos.clear();
14784 
14785  PostprocessMetadata();
14786 
14787  return VK_SUCCESS;
14788 }
14789 
14790 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14791 {
14792  const size_t blockCount = m_pBlockVector->GetBlockCount();
14793  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14794  {
14795  VmaBlockMetadata_Generic* const pMetadata =
14796  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14797  pMetadata->m_FreeCount = 0;
14798  pMetadata->m_SumFreeSize = pMetadata->GetSize();
14799  pMetadata->m_FreeSuballocationsBySize.clear();
14800  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14801  it != pMetadata->m_Suballocations.end(); )
14802  {
14803  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14804  {
14805  VmaSuballocationList::iterator nextIt = it;
14806  ++nextIt;
14807  pMetadata->m_Suballocations.erase(it);
14808  it = nextIt;
14809  }
14810  else
14811  {
14812  ++it;
14813  }
14814  }
14815  }
14816 }
14817 
14818 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14819 {
14820  const size_t blockCount = m_pBlockVector->GetBlockCount();
14821  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14822  {
14823  VmaBlockMetadata_Generic* const pMetadata =
14824  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14825  const VkDeviceSize blockSize = pMetadata->GetSize();
14826 
14827  // No allocations in this block - entire area is free.
14828  if(pMetadata->m_Suballocations.empty())
14829  {
14830  pMetadata->m_FreeCount = 1;
14831  //pMetadata->m_SumFreeSize is already set to blockSize.
14832  VmaSuballocation suballoc = {
14833  0, // offset
14834  blockSize, // size
14835  VMA_NULL, // hAllocation
14836  VMA_SUBALLOCATION_TYPE_FREE };
14837  pMetadata->m_Suballocations.push_back(suballoc);
14838  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14839  }
14840  // There are some allocations in this block.
14841  else
14842  {
14843  VkDeviceSize offset = 0;
14844  VmaSuballocationList::iterator it;
14845  for(it = pMetadata->m_Suballocations.begin();
14846  it != pMetadata->m_Suballocations.end();
14847  ++it)
14848  {
14849  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14850  VMA_ASSERT(it->offset >= offset);
14851 
14852  // Need to insert preceding free space.
14853  if(it->offset > offset)
14854  {
14855  ++pMetadata->m_FreeCount;
14856  const VkDeviceSize freeSize = it->offset - offset;
14857  VmaSuballocation suballoc = {
14858  offset, // offset
14859  freeSize, // size
14860  VMA_NULL, // hAllocation
14861  VMA_SUBALLOCATION_TYPE_FREE };
14862  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14863  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14864  {
14865  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14866  }
14867  }
14868 
14869  pMetadata->m_SumFreeSize -= it->size;
14870  offset = it->offset + it->size;
14871  }
14872 
14873  // Need to insert trailing free space.
14874  if(offset < blockSize)
14875  {
14876  ++pMetadata->m_FreeCount;
14877  const VkDeviceSize freeSize = blockSize - offset;
14878  VmaSuballocation suballoc = {
14879  offset, // offset
14880  freeSize, // size
14881  VMA_NULL, // hAllocation
14882  VMA_SUBALLOCATION_TYPE_FREE };
14883  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14884  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14885  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14886  {
14887  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14888  }
14889  }
14890 
14891  VMA_SORT(
14892  pMetadata->m_FreeSuballocationsBySize.begin(),
14893  pMetadata->m_FreeSuballocationsBySize.end(),
14894  VmaSuballocationItemSizeLess());
14895  }
14896 
14897  VMA_HEAVY_ASSERT(pMetadata->Validate());
14898  }
14899 }
14900 
14901 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14902 {
14903  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14904  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14905  while(it != pMetadata->m_Suballocations.end())
14906  {
14907  if(it->offset < suballoc.offset)
14908  {
14909  ++it;
14910  }
14911  }
14912  pMetadata->m_Suballocations.insert(it, suballoc);
14913 }
14914 
14916 // VmaBlockVectorDefragmentationContext
14917 
14918 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14919  VmaAllocator hAllocator,
14920  VmaPool hCustomPool,
14921  VmaBlockVector* pBlockVector,
14922  uint32_t currFrameIndex) :
14923  res(VK_SUCCESS),
14924  mutexLocked(false),
14925  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14926  defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14927  defragmentationMovesProcessed(0),
14928  defragmentationMovesCommitted(0),
14929  hasDefragmentationPlan(0),
14930  m_hAllocator(hAllocator),
14931  m_hCustomPool(hCustomPool),
14932  m_pBlockVector(pBlockVector),
14933  m_CurrFrameIndex(currFrameIndex),
14934  m_pAlgorithm(VMA_NULL),
14935  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14936  m_AllAllocations(false)
14937 {
14938 }
14939 
14940 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14941 {
14942  vma_delete(m_hAllocator, m_pAlgorithm);
14943 }
14944 
14945 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14946 {
14947  AllocInfo info = { hAlloc, pChanged };
14948  m_Allocations.push_back(info);
14949 }
14950 
14951 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14952 {
14953  const bool allAllocations = m_AllAllocations ||
14954  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14955 
14956  /********************************
14957  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14958  ********************************/
14959 
14960  /*
14961  Fast algorithm is supported only when certain criteria are met:
14962  - VMA_DEBUG_MARGIN is 0.
14963  - All allocations in this block vector are moveable.
14964  - There is no possibility of image/buffer granularity conflict.
14965  - The defragmentation is not incremental
14966  */
14967  if(VMA_DEBUG_MARGIN == 0 &&
14968  allAllocations &&
14969  !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14971  {
14972  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14973  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14974  }
14975  else
14976  {
14977  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14978  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14979  }
14980 
14981  if(allAllocations)
14982  {
14983  m_pAlgorithm->AddAll();
14984  }
14985  else
14986  {
14987  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14988  {
14989  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14990  }
14991  }
14992 }
14993 
14995 // VmaDefragmentationContext
14996 
14997 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14998  VmaAllocator hAllocator,
14999  uint32_t currFrameIndex,
15000  uint32_t flags,
15001  VmaDefragmentationStats* pStats) :
15002  m_hAllocator(hAllocator),
15003  m_CurrFrameIndex(currFrameIndex),
15004  m_Flags(flags),
15005  m_pStats(pStats),
15006  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
15007 {
15008  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
15009 }
15010 
15011 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
15012 {
15013  for(size_t i = m_CustomPoolContexts.size(); i--; )
15014  {
15015  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
15016  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15017  vma_delete(m_hAllocator, pBlockVectorCtx);
15018  }
15019  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
15020  {
15021  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
15022  if(pBlockVectorCtx)
15023  {
15024  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
15025  vma_delete(m_hAllocator, pBlockVectorCtx);
15026  }
15027  }
15028 }
15029 
15030 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
15031 {
15032  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15033  {
15034  VmaPool pool = pPools[poolIndex];
15035  VMA_ASSERT(pool);
15036  // Pools with algorithm other than default are not defragmented.
15037  if(pool->m_BlockVector.GetAlgorithm() == 0)
15038  {
15039  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15040 
15041  for(size_t i = m_CustomPoolContexts.size(); i--; )
15042  {
15043  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
15044  {
15045  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15046  break;
15047  }
15048  }
15049 
15050  if(!pBlockVectorDefragCtx)
15051  {
15052  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15053  m_hAllocator,
15054  pool,
15055  &pool->m_BlockVector,
15056  m_CurrFrameIndex);
15057  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15058  }
15059 
15060  pBlockVectorDefragCtx->AddAll();
15061  }
15062  }
15063 }
15064 
15065 void VmaDefragmentationContext_T::AddAllocations(
15066  uint32_t allocationCount,
15067  const VmaAllocation* pAllocations,
15068  VkBool32* pAllocationsChanged)
15069 {
15070  // Dispatch pAllocations among defragmentators. Create them when necessary.
15071  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15072  {
15073  const VmaAllocation hAlloc = pAllocations[allocIndex];
15074  VMA_ASSERT(hAlloc);
15075  // DedicatedAlloc cannot be defragmented.
15076  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
15077  // Lost allocation cannot be defragmented.
15078  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
15079  {
15080  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
15081 
15082  const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
15083  // This allocation belongs to custom pool.
15084  if(hAllocPool != VK_NULL_HANDLE)
15085  {
15086  // Pools with algorithm other than default are not defragmented.
15087  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
15088  {
15089  for(size_t i = m_CustomPoolContexts.size(); i--; )
15090  {
15091  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
15092  {
15093  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
15094  break;
15095  }
15096  }
15097  if(!pBlockVectorDefragCtx)
15098  {
15099  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15100  m_hAllocator,
15101  hAllocPool,
15102  &hAllocPool->m_BlockVector,
15103  m_CurrFrameIndex);
15104  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
15105  }
15106  }
15107  }
15108  // This allocation belongs to default pool.
15109  else
15110  {
15111  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
15112  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
15113  if(!pBlockVectorDefragCtx)
15114  {
15115  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
15116  m_hAllocator,
15117  VMA_NULL, // hCustomPool
15118  m_hAllocator->m_pBlockVectors[memTypeIndex],
15119  m_CurrFrameIndex);
15120  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
15121  }
15122  }
15123 
15124  if(pBlockVectorDefragCtx)
15125  {
15126  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
15127  &pAllocationsChanged[allocIndex] : VMA_NULL;
15128  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
15129  }
15130  }
15131  }
15132 }
15133 
15134 VkResult VmaDefragmentationContext_T::Defragment(
15135  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
15136  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
15137  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
15138 {
15139  if(pStats)
15140  {
15141  memset(pStats, 0, sizeof(VmaDefragmentationStats));
15142  }
15143 
15145  {
15146  // For incremental defragmetnations, we just earmark how much we can move
15147  // The real meat is in the defragmentation steps
15148  m_MaxCpuBytesToMove = maxCpuBytesToMove;
15149  m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
15150 
15151  m_MaxGpuBytesToMove = maxGpuBytesToMove;
15152  m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
15153 
15154  if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
15155  m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
15156  return VK_SUCCESS;
15157 
15158  return VK_NOT_READY;
15159  }
15160 
15161  if(commandBuffer == VK_NULL_HANDLE)
15162  {
15163  maxGpuBytesToMove = 0;
15164  maxGpuAllocationsToMove = 0;
15165  }
15166 
15167  VkResult res = VK_SUCCESS;
15168 
15169  // Process default pools.
15170  for(uint32_t memTypeIndex = 0;
15171  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
15172  ++memTypeIndex)
15173  {
15174  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15175  if(pBlockVectorCtx)
15176  {
15177  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15178  pBlockVectorCtx->GetBlockVector()->Defragment(
15179  pBlockVectorCtx,
15180  pStats, flags,
15181  maxCpuBytesToMove, maxCpuAllocationsToMove,
15182  maxGpuBytesToMove, maxGpuAllocationsToMove,
15183  commandBuffer);
15184  if(pBlockVectorCtx->res != VK_SUCCESS)
15185  {
15186  res = pBlockVectorCtx->res;
15187  }
15188  }
15189  }
15190 
15191  // Process custom pools.
15192  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15193  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
15194  ++customCtxIndex)
15195  {
15196  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15197  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15198  pBlockVectorCtx->GetBlockVector()->Defragment(
15199  pBlockVectorCtx,
15200  pStats, flags,
15201  maxCpuBytesToMove, maxCpuAllocationsToMove,
15202  maxGpuBytesToMove, maxGpuAllocationsToMove,
15203  commandBuffer);
15204  if(pBlockVectorCtx->res != VK_SUCCESS)
15205  {
15206  res = pBlockVectorCtx->res;
15207  }
15208  }
15209 
15210  return res;
15211 }
15212 
15213 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
15214 {
15215  VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
15216  uint32_t movesLeft = pInfo->moveCount;
15217 
15218  // Process default pools.
15219  for(uint32_t memTypeIndex = 0;
15220  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15221  ++memTypeIndex)
15222  {
15223  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15224  if(pBlockVectorCtx)
15225  {
15226  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15227 
15228  if(!pBlockVectorCtx->hasDefragmentationPlan)
15229  {
15230  pBlockVectorCtx->GetBlockVector()->Defragment(
15231  pBlockVectorCtx,
15232  m_pStats, m_Flags,
15233  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15234  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15235  VK_NULL_HANDLE);
15236 
15237  if(pBlockVectorCtx->res < VK_SUCCESS)
15238  continue;
15239 
15240  pBlockVectorCtx->hasDefragmentationPlan = true;
15241  }
15242 
15243  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15244  pBlockVectorCtx,
15245  pCurrentMove, movesLeft);
15246 
15247  movesLeft -= processed;
15248  pCurrentMove += processed;
15249  }
15250  }
15251 
15252  // Process custom pools.
15253  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15254  customCtxIndex < customCtxCount;
15255  ++customCtxIndex)
15256  {
15257  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15258  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15259 
15260  if(!pBlockVectorCtx->hasDefragmentationPlan)
15261  {
15262  pBlockVectorCtx->GetBlockVector()->Defragment(
15263  pBlockVectorCtx,
15264  m_pStats, m_Flags,
15265  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15266  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15267  VK_NULL_HANDLE);
15268 
15269  if(pBlockVectorCtx->res < VK_SUCCESS)
15270  continue;
15271 
15272  pBlockVectorCtx->hasDefragmentationPlan = true;
15273  }
15274 
15275  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15276  pBlockVectorCtx,
15277  pCurrentMove, movesLeft);
15278 
15279  movesLeft -= processed;
15280  pCurrentMove += processed;
15281  }
15282 
15283  pInfo->moveCount = pInfo->moveCount - movesLeft;
15284 
15285  return VK_SUCCESS;
15286 }
15287 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15288 {
15289  VkResult res = VK_SUCCESS;
15290 
15291  // Process default pools.
15292  for(uint32_t memTypeIndex = 0;
15293  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15294  ++memTypeIndex)
15295  {
15296  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15297  if(pBlockVectorCtx)
15298  {
15299  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15300 
15301  if(!pBlockVectorCtx->hasDefragmentationPlan)
15302  {
15303  res = VK_NOT_READY;
15304  continue;
15305  }
15306 
15307  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15308  pBlockVectorCtx, m_pStats);
15309 
15310  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15311  res = VK_NOT_READY;
15312  }
15313  }
15314 
15315  // Process custom pools.
15316  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15317  customCtxIndex < customCtxCount;
15318  ++customCtxIndex)
15319  {
15320  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15321  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15322 
15323  if(!pBlockVectorCtx->hasDefragmentationPlan)
15324  {
15325  res = VK_NOT_READY;
15326  continue;
15327  }
15328 
15329  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15330  pBlockVectorCtx, m_pStats);
15331 
15332  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15333  res = VK_NOT_READY;
15334  }
15335 
15336  return res;
15337 }
15338 
15340 // VmaRecorder
15341 
15342 #if VMA_RECORDING_ENABLED
15343 
15344 VmaRecorder::VmaRecorder() :
15345  m_UseMutex(true),
15346  m_Flags(0),
15347  m_File(VMA_NULL),
15348  m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15349 {
15350 }
15351 
15352 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15353 {
15354  m_UseMutex = useMutex;
15355  m_Flags = settings.flags;
15356 
15357 #if defined(_WIN32)
15358  // Open file for writing.
15359  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15360 
15361  if(err != 0)
15362  {
15363  return VK_ERROR_INITIALIZATION_FAILED;
15364  }
15365 #else
15366  // Open file for writing.
15367  m_File = fopen(settings.pFilePath, "wb");
15368 
15369  if(m_File == 0)
15370  {
15371  return VK_ERROR_INITIALIZATION_FAILED;
15372  }
15373 #endif
15374 
15375  // Write header.
15376  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15377  fprintf(m_File, "%s\n", "1,8");
15378 
15379  return VK_SUCCESS;
15380 }
15381 
15382 VmaRecorder::~VmaRecorder()
15383 {
15384  if(m_File != VMA_NULL)
15385  {
15386  fclose(m_File);
15387  }
15388 }
15389 
15390 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15391 {
15392  CallParams callParams;
15393  GetBasicParams(callParams);
15394 
15395  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15396  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15397  Flush();
15398 }
15399 
15400 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15401 {
15402  CallParams callParams;
15403  GetBasicParams(callParams);
15404 
15405  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15406  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15407  Flush();
15408 }
15409 
15410 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15411 {
15412  CallParams callParams;
15413  GetBasicParams(callParams);
15414 
15415  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15416  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15417  createInfo.memoryTypeIndex,
15418  createInfo.flags,
15419  createInfo.blockSize,
15420  (uint64_t)createInfo.minBlockCount,
15421  (uint64_t)createInfo.maxBlockCount,
15422  createInfo.frameInUseCount,
15423  pool);
15424  Flush();
15425 }
15426 
15427 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15428 {
15429  CallParams callParams;
15430  GetBasicParams(callParams);
15431 
15432  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15433  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15434  pool);
15435  Flush();
15436 }
15437 
15438 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15439  const VkMemoryRequirements& vkMemReq,
15440  const VmaAllocationCreateInfo& createInfo,
15441  VmaAllocation allocation)
15442 {
15443  CallParams callParams;
15444  GetBasicParams(callParams);
15445 
15446  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15447  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15448  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15449  vkMemReq.size,
15450  vkMemReq.alignment,
15451  vkMemReq.memoryTypeBits,
15452  createInfo.flags,
15453  createInfo.usage,
15454  createInfo.requiredFlags,
15455  createInfo.preferredFlags,
15456  createInfo.memoryTypeBits,
15457  createInfo.pool,
15458  allocation,
15459  userDataStr.GetString());
15460  Flush();
15461 }
15462 
15463 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15464  const VkMemoryRequirements& vkMemReq,
15465  const VmaAllocationCreateInfo& createInfo,
15466  uint64_t allocationCount,
15467  const VmaAllocation* pAllocations)
15468 {
15469  CallParams callParams;
15470  GetBasicParams(callParams);
15471 
15472  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15473  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15474  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15475  vkMemReq.size,
15476  vkMemReq.alignment,
15477  vkMemReq.memoryTypeBits,
15478  createInfo.flags,
15479  createInfo.usage,
15480  createInfo.requiredFlags,
15481  createInfo.preferredFlags,
15482  createInfo.memoryTypeBits,
15483  createInfo.pool);
15484  PrintPointerList(allocationCount, pAllocations);
15485  fprintf(m_File, ",%s\n", userDataStr.GetString());
15486  Flush();
15487 }
15488 
15489 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15490  const VkMemoryRequirements& vkMemReq,
15491  bool requiresDedicatedAllocation,
15492  bool prefersDedicatedAllocation,
15493  const VmaAllocationCreateInfo& createInfo,
15494  VmaAllocation allocation)
15495 {
15496  CallParams callParams;
15497  GetBasicParams(callParams);
15498 
15499  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15500  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15501  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,
15502  vkMemReq.size,
15503  vkMemReq.alignment,
15504  vkMemReq.memoryTypeBits,
15505  requiresDedicatedAllocation ? 1 : 0,
15506  prefersDedicatedAllocation ? 1 : 0,
15507  createInfo.flags,
15508  createInfo.usage,
15509  createInfo.requiredFlags,
15510  createInfo.preferredFlags,
15511  createInfo.memoryTypeBits,
15512  createInfo.pool,
15513  allocation,
15514  userDataStr.GetString());
15515  Flush();
15516 }
15517 
15518 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15519  const VkMemoryRequirements& vkMemReq,
15520  bool requiresDedicatedAllocation,
15521  bool prefersDedicatedAllocation,
15522  const VmaAllocationCreateInfo& createInfo,
15523  VmaAllocation allocation)
15524 {
15525  CallParams callParams;
15526  GetBasicParams(callParams);
15527 
15528  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15529  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15530  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,
15531  vkMemReq.size,
15532  vkMemReq.alignment,
15533  vkMemReq.memoryTypeBits,
15534  requiresDedicatedAllocation ? 1 : 0,
15535  prefersDedicatedAllocation ? 1 : 0,
15536  createInfo.flags,
15537  createInfo.usage,
15538  createInfo.requiredFlags,
15539  createInfo.preferredFlags,
15540  createInfo.memoryTypeBits,
15541  createInfo.pool,
15542  allocation,
15543  userDataStr.GetString());
15544  Flush();
15545 }
15546 
15547 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15548  VmaAllocation allocation)
15549 {
15550  CallParams callParams;
15551  GetBasicParams(callParams);
15552 
15553  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15554  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15555  allocation);
15556  Flush();
15557 }
15558 
15559 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15560  uint64_t allocationCount,
15561  const VmaAllocation* pAllocations)
15562 {
15563  CallParams callParams;
15564  GetBasicParams(callParams);
15565 
15566  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15567  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15568  PrintPointerList(allocationCount, pAllocations);
15569  fprintf(m_File, "\n");
15570  Flush();
15571 }
15572 
15573 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15574  VmaAllocation allocation,
15575  const void* pUserData)
15576 {
15577  CallParams callParams;
15578  GetBasicParams(callParams);
15579 
15580  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15581  UserDataString userDataStr(
15582  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15583  pUserData);
15584  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15585  allocation,
15586  userDataStr.GetString());
15587  Flush();
15588 }
15589 
15590 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15591  VmaAllocation allocation)
15592 {
15593  CallParams callParams;
15594  GetBasicParams(callParams);
15595 
15596  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15597  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15598  allocation);
15599  Flush();
15600 }
15601 
15602 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15603  VmaAllocation allocation)
15604 {
15605  CallParams callParams;
15606  GetBasicParams(callParams);
15607 
15608  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15609  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15610  allocation);
15611  Flush();
15612 }
15613 
15614 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15615  VmaAllocation allocation)
15616 {
15617  CallParams callParams;
15618  GetBasicParams(callParams);
15619 
15620  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15621  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15622  allocation);
15623  Flush();
15624 }
15625 
15626 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15627  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15628 {
15629  CallParams callParams;
15630  GetBasicParams(callParams);
15631 
15632  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15633  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15634  allocation,
15635  offset,
15636  size);
15637  Flush();
15638 }
15639 
15640 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15641  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15642 {
15643  CallParams callParams;
15644  GetBasicParams(callParams);
15645 
15646  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15647  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15648  allocation,
15649  offset,
15650  size);
15651  Flush();
15652 }
15653 
15654 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15655  const VkBufferCreateInfo& bufCreateInfo,
15656  const VmaAllocationCreateInfo& allocCreateInfo,
15657  VmaAllocation allocation)
15658 {
15659  CallParams callParams;
15660  GetBasicParams(callParams);
15661 
15662  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15663  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15664  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,
15665  bufCreateInfo.flags,
15666  bufCreateInfo.size,
15667  bufCreateInfo.usage,
15668  bufCreateInfo.sharingMode,
15669  allocCreateInfo.flags,
15670  allocCreateInfo.usage,
15671  allocCreateInfo.requiredFlags,
15672  allocCreateInfo.preferredFlags,
15673  allocCreateInfo.memoryTypeBits,
15674  allocCreateInfo.pool,
15675  allocation,
15676  userDataStr.GetString());
15677  Flush();
15678 }
15679 
15680 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15681  const VkImageCreateInfo& imageCreateInfo,
15682  const VmaAllocationCreateInfo& allocCreateInfo,
15683  VmaAllocation allocation)
15684 {
15685  CallParams callParams;
15686  GetBasicParams(callParams);
15687 
15688  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15689  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15690  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,
15691  imageCreateInfo.flags,
15692  imageCreateInfo.imageType,
15693  imageCreateInfo.format,
15694  imageCreateInfo.extent.width,
15695  imageCreateInfo.extent.height,
15696  imageCreateInfo.extent.depth,
15697  imageCreateInfo.mipLevels,
15698  imageCreateInfo.arrayLayers,
15699  imageCreateInfo.samples,
15700  imageCreateInfo.tiling,
15701  imageCreateInfo.usage,
15702  imageCreateInfo.sharingMode,
15703  imageCreateInfo.initialLayout,
15704  allocCreateInfo.flags,
15705  allocCreateInfo.usage,
15706  allocCreateInfo.requiredFlags,
15707  allocCreateInfo.preferredFlags,
15708  allocCreateInfo.memoryTypeBits,
15709  allocCreateInfo.pool,
15710  allocation,
15711  userDataStr.GetString());
15712  Flush();
15713 }
15714 
15715 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15716  VmaAllocation allocation)
15717 {
15718  CallParams callParams;
15719  GetBasicParams(callParams);
15720 
15721  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15722  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15723  allocation);
15724  Flush();
15725 }
15726 
15727 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15728  VmaAllocation allocation)
15729 {
15730  CallParams callParams;
15731  GetBasicParams(callParams);
15732 
15733  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15734  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15735  allocation);
15736  Flush();
15737 }
15738 
15739 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15740  VmaAllocation allocation)
15741 {
15742  CallParams callParams;
15743  GetBasicParams(callParams);
15744 
15745  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15746  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15747  allocation);
15748  Flush();
15749 }
15750 
15751 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15752  VmaAllocation allocation)
15753 {
15754  CallParams callParams;
15755  GetBasicParams(callParams);
15756 
15757  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15758  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15759  allocation);
15760  Flush();
15761 }
15762 
15763 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15764  VmaPool pool)
15765 {
15766  CallParams callParams;
15767  GetBasicParams(callParams);
15768 
15769  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15770  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15771  pool);
15772  Flush();
15773 }
15774 
15775 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15776  const VmaDefragmentationInfo2& info,
15778 {
15779  CallParams callParams;
15780  GetBasicParams(callParams);
15781 
15782  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15783  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15784  info.flags);
15785  PrintPointerList(info.allocationCount, info.pAllocations);
15786  fprintf(m_File, ",");
15787  PrintPointerList(info.poolCount, info.pPools);
15788  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15789  info.maxCpuBytesToMove,
15791  info.maxGpuBytesToMove,
15793  info.commandBuffer,
15794  ctx);
15795  Flush();
15796 }
15797 
15798 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15800 {
15801  CallParams callParams;
15802  GetBasicParams(callParams);
15803 
15804  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15805  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15806  ctx);
15807  Flush();
15808 }
15809 
15810 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15811  VmaPool pool,
15812  const char* name)
15813 {
15814  CallParams callParams;
15815  GetBasicParams(callParams);
15816 
15817  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15818  fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15819  pool, name != VMA_NULL ? name : "");
15820  Flush();
15821 }
15822 
15823 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15824 {
15825  if(pUserData != VMA_NULL)
15826  {
15827  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15828  {
15829  m_Str = (const char*)pUserData;
15830  }
15831  else
15832  {
15833  // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15834  snprintf(m_PtrStr, 17, "%p", pUserData);
15835  m_Str = m_PtrStr;
15836  }
15837  }
15838  else
15839  {
15840  m_Str = "";
15841  }
15842 }
15843 
15844 void VmaRecorder::WriteConfiguration(
15845  const VkPhysicalDeviceProperties& devProps,
15846  const VkPhysicalDeviceMemoryProperties& memProps,
15847  uint32_t vulkanApiVersion,
15848  bool dedicatedAllocationExtensionEnabled,
15849  bool bindMemory2ExtensionEnabled,
15850  bool memoryBudgetExtensionEnabled,
15851  bool deviceCoherentMemoryExtensionEnabled)
15852 {
15853  fprintf(m_File, "Config,Begin\n");
15854 
15855  fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15856 
15857  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15858  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15859  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15860  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15861  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15862  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15863 
15864  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15865  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15866  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15867 
15868  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15869  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15870  {
15871  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15872  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15873  }
15874  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15875  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15876  {
15877  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15878  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15879  }
15880 
15881  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15882  fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15883  fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15884  fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15885 
15886  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15887  fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15888  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15889  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15890  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15891  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15892  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15893  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15894  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15895 
15896  fprintf(m_File, "Config,End\n");
15897 }
15898 
15899 void VmaRecorder::GetBasicParams(CallParams& outParams)
15900 {
15901  #if defined(_WIN32)
15902  outParams.threadId = GetCurrentThreadId();
15903  #else
15904  // Use C++11 features to get thread id and convert it to uint32_t.
15905  // There is room for optimization since sstream is quite slow.
15906  // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15907  std::thread::id thread_id = std::this_thread::get_id();
15908  std::stringstream thread_id_to_string_converter;
15909  thread_id_to_string_converter << thread_id;
15910  std::string thread_id_as_string = thread_id_to_string_converter.str();
15911  outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15912  #endif
15913 
15914  auto current_time = std::chrono::high_resolution_clock::now();
15915 
15916  outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15917 }
15918 
15919 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15920 {
15921  if(count)
15922  {
15923  fprintf(m_File, "%p", pItems[0]);
15924  for(uint64_t i = 1; i < count; ++i)
15925  {
15926  fprintf(m_File, " %p", pItems[i]);
15927  }
15928  }
15929 }
15930 
15931 void VmaRecorder::Flush()
15932 {
15933  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15934  {
15935  fflush(m_File);
15936  }
15937 }
15938 
15939 #endif // #if VMA_RECORDING_ENABLED
15940 
15942 // VmaAllocationObjectAllocator
15943 
15944 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15945  m_Allocator(pAllocationCallbacks, 1024)
15946 {
15947 }
15948 
15949 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15950 {
15951  VmaMutexLock mutexLock(m_Mutex);
15952  return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15953 }
15954 
15955 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15956 {
15957  VmaMutexLock mutexLock(m_Mutex);
15958  m_Allocator.Free(hAlloc);
15959 }
15960 
15962 // VmaAllocator_T
15963 
15964 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15965  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15966  m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15967  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15968  m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15969  m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15970  m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15971  m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15972  m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
15973  m_hDevice(pCreateInfo->device),
15974  m_hInstance(pCreateInfo->instance),
15975  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15976  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15977  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15978  m_AllocationObjectAllocator(&m_AllocationCallbacks),
15979  m_HeapSizeLimitMask(0),
15980  m_DeviceMemoryCount(0),
15981  m_PreferredLargeHeapBlockSize(0),
15982  m_PhysicalDevice(pCreateInfo->physicalDevice),
15983  m_CurrentFrameIndex(0),
15984  m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15985  m_NextPoolId(0),
15986  m_GlobalMemoryTypeBits(UINT32_MAX)
15988  ,m_pRecorder(VMA_NULL)
15989 #endif
15990 {
15991  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15992  {
15993  m_UseKhrDedicatedAllocation = false;
15994  m_UseKhrBindMemory2 = false;
15995  }
15996 
15997  if(VMA_DEBUG_DETECT_CORRUPTION)
15998  {
15999  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
16000  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
16001  }
16002 
16003  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
16004 
16005  if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16006  {
16007 #if !(VMA_DEDICATED_ALLOCATION)
16009  {
16010  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
16011  }
16012 #endif
16013 #if !(VMA_BIND_MEMORY2)
16014  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
16015  {
16016  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
16017  }
16018 #endif
16019  }
16020 #if !(VMA_MEMORY_BUDGET)
16021  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
16022  {
16023  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
16024  }
16025 #endif
16026 #if !(VMA_BUFFER_DEVICE_ADDRESS)
16027  if(m_UseKhrBufferDeviceAddress)
16028  {
16029  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.");
16030  }
16031 #endif
16032 #if VMA_VULKAN_VERSION < 1002000
16033  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
16034  {
16035  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
16036  }
16037 #endif
16038 #if VMA_VULKAN_VERSION < 1001000
16039  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16040  {
16041  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
16042  }
16043 #endif
16044 #if !(VMA_MEMORY_PRIORITY)
16045  if(m_UseExtMemoryPriority)
16046  {
16047  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.");
16048  }
16049 #endif
16050 
16051  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
16052  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
16053  memset(&m_MemProps, 0, sizeof(m_MemProps));
16054 
16055  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
16056  memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
16057 
16058  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
16059  {
16060  m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
16061  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
16062  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
16063  }
16064 
16065  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
16066 
16067  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
16068  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
16069 
16070  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
16071  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
16072  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
16073  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
16074 
16075  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
16076  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
16077 
16078  m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
16079 
16080  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
16081  {
16082  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
16083  {
16084  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
16085  if(limit != VK_WHOLE_SIZE)
16086  {
16087  m_HeapSizeLimitMask |= 1u << heapIndex;
16088  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
16089  {
16090  m_MemProps.memoryHeaps[heapIndex].size = limit;
16091  }
16092  }
16093  }
16094  }
16095 
16096  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16097  {
16098  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
16099 
16100  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
16101  this,
16102  VK_NULL_HANDLE, // hParentPool
16103  memTypeIndex,
16104  preferredBlockSize,
16105  0,
16106  SIZE_MAX,
16107  GetBufferImageGranularity(),
16108  pCreateInfo->frameInUseCount,
16109  false, // explicitBlockSize
16110  false, // linearAlgorithm
16111  0.5f); // priority (0.5 is the default per Vulkan spec)
16112  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
16113  // becase minBlockCount is 0.
16114  }
16115 }
16116 
16117 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
16118 {
16119  VkResult res = VK_SUCCESS;
16120 
16121  if(pCreateInfo->pRecordSettings != VMA_NULL &&
16122  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
16123  {
16124 #if VMA_RECORDING_ENABLED
16125  m_pRecorder = vma_new(this, VmaRecorder)();
16126  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
16127  if(res != VK_SUCCESS)
16128  {
16129  return res;
16130  }
16131  m_pRecorder->WriteConfiguration(
16132  m_PhysicalDeviceProperties,
16133  m_MemProps,
16134  m_VulkanApiVersion,
16135  m_UseKhrDedicatedAllocation,
16136  m_UseKhrBindMemory2,
16137  m_UseExtMemoryBudget,
16138  m_UseAmdDeviceCoherentMemory);
16139  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
16140 #else
16141  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
16142  return VK_ERROR_FEATURE_NOT_PRESENT;
16143 #endif
16144  }
16145 
16146 #if VMA_MEMORY_BUDGET
16147  if(m_UseExtMemoryBudget)
16148  {
16149  UpdateVulkanBudget();
16150  }
16151 #endif // #if VMA_MEMORY_BUDGET
16152 
16153  return res;
16154 }
16155 
16156 VmaAllocator_T::~VmaAllocator_T()
16157 {
16158 #if VMA_RECORDING_ENABLED
16159  if(m_pRecorder != VMA_NULL)
16160  {
16161  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
16162  vma_delete(this, m_pRecorder);
16163  }
16164 #endif
16165 
16166  VMA_ASSERT(m_Pools.IsEmpty());
16167 
16168  for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
16169  {
16170  if(!m_DedicatedAllocations[memTypeIndex].IsEmpty())
16171  {
16172  VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
16173  }
16174 
16175  vma_delete(this, m_pBlockVectors[memTypeIndex]);
16176  }
16177 }
16178 
16179 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
16180 {
16181 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16182  ImportVulkanFunctions_Static();
16183 #endif
16184 
16185  if(pVulkanFunctions != VMA_NULL)
16186  {
16187  ImportVulkanFunctions_Custom(pVulkanFunctions);
16188  }
16189 
16190 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16191  ImportVulkanFunctions_Dynamic();
16192 #endif
16193 
16194  ValidateVulkanFunctions();
16195 }
16196 
16197 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16198 
16199 void VmaAllocator_T::ImportVulkanFunctions_Static()
16200 {
16201  // Vulkan 1.0
16202  m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
16203  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
16204  m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
16205  m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
16206  m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
16207  m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
16208  m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
16209  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
16210  m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
16211  m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
16212  m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
16213  m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
16214  m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
16215  m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
16216  m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
16217  m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
16218  m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
16219 
16220  // Vulkan 1.1
16221 #if VMA_VULKAN_VERSION >= 1001000
16222  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16223  {
16224  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
16225  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
16226  m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
16227  m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
16228  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
16229  }
16230 #endif
16231 }
16232 
16233 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
16234 
16235 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
16236 {
16237  VMA_ASSERT(pVulkanFunctions != VMA_NULL);
16238 
16239 #define VMA_COPY_IF_NOT_NULL(funcName) \
16240  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
16241 
16242  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
16243  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
16244  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
16245  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
16246  VMA_COPY_IF_NOT_NULL(vkMapMemory);
16247  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
16248  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
16249  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
16250  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
16251  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
16252  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
16253  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
16254  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16255  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16256  VMA_COPY_IF_NOT_NULL(vkCreateImage);
16257  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16258  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16259 
16260 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16261  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16262  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16263 #endif
16264 
16265 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16266  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16267  VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16268 #endif
16269 
16270 #if VMA_MEMORY_BUDGET
16271  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16272 #endif
16273 
16274 #undef VMA_COPY_IF_NOT_NULL
16275 }
16276 
16277 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16278 
16279 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16280 {
16281 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16282  if(m_VulkanFunctions.memberName == VMA_NULL) \
16283  m_VulkanFunctions.memberName = \
16284  (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16285 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16286  if(m_VulkanFunctions.memberName == VMA_NULL) \
16287  m_VulkanFunctions.memberName = \
16288  (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16289 
16290  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16291  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16292  VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16293  VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16294  VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16295  VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16296  VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16297  VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16298  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16299  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16300  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16301  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16302  VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16303  VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16304  VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16305  VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16306  VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16307 
16308 #if VMA_VULKAN_VERSION >= 1001000
16309  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16310  {
16311  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
16312  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
16313  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
16314  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
16315  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
16316  }
16317 #endif
16318 
16319 #if VMA_DEDICATED_ALLOCATION
16320  if(m_UseKhrDedicatedAllocation)
16321  {
16322  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16323  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16324  }
16325 #endif
16326 
16327 #if VMA_BIND_MEMORY2
16328  if(m_UseKhrBindMemory2)
16329  {
16330  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16331  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16332  }
16333 #endif // #if VMA_BIND_MEMORY2
16334 
16335 #if VMA_MEMORY_BUDGET
16336  if(m_UseExtMemoryBudget)
16337  {
16338  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16339  }
16340 #endif // #if VMA_MEMORY_BUDGET
16341 
16342 #undef VMA_FETCH_DEVICE_FUNC
16343 #undef VMA_FETCH_INSTANCE_FUNC
16344 }
16345 
16346 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16347 
16348 void VmaAllocator_T::ValidateVulkanFunctions()
16349 {
16350  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16351  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16352  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16353  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16354  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16355  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16356  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16357  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16358  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16359  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16360  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16361  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16362  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16363  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16364  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16365  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16366  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16367 
16368 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16369  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16370  {
16371  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16372  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16373  }
16374 #endif
16375 
16376 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16377  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16378  {
16379  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16380  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16381  }
16382 #endif
16383 
16384 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16385  if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16386  {
16387  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16388  }
16389 #endif
16390 }
16391 
16392 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16393 {
16394  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16395  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16396  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16397  return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16398 }
16399 
16400 VkResult VmaAllocator_T::AllocateMemoryOfType(
16401  VkDeviceSize size,
16402  VkDeviceSize alignment,
16403  bool dedicatedAllocation,
16404  VkBuffer dedicatedBuffer,
16405  VkBufferUsageFlags dedicatedBufferUsage,
16406  VkImage dedicatedImage,
16407  const VmaAllocationCreateInfo& createInfo,
16408  uint32_t memTypeIndex,
16409  VmaSuballocationType suballocType,
16410  size_t allocationCount,
16411  VmaAllocation* pAllocations)
16412 {
16413  VMA_ASSERT(pAllocations != VMA_NULL);
16414  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16415 
16416  VmaAllocationCreateInfo finalCreateInfo = createInfo;
16417 
16418  // If memory type is not HOST_VISIBLE, disable MAPPED.
16419  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16420  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16421  {
16422  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16423  }
16424  // If memory is lazily allocated, it should be always dedicated.
16425  if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16426  {
16428  }
16429 
16430  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16431  VMA_ASSERT(blockVector);
16432 
16433  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16434  bool preferDedicatedMemory =
16435  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16436  dedicatedAllocation ||
16437  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16438  size > preferredBlockSize / 2;
16439 
16440  if(preferDedicatedMemory &&
16441  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16442  finalCreateInfo.pool == VK_NULL_HANDLE)
16443  {
16445  }
16446 
16447  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16448  {
16449  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16450  {
16451  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16452  }
16453  else
16454  {
16455  return AllocateDedicatedMemory(
16456  size,
16457  suballocType,
16458  memTypeIndex,
16459  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16460  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16461  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16462  finalCreateInfo.pUserData,
16463  finalCreateInfo.priority,
16464  dedicatedBuffer,
16465  dedicatedBufferUsage,
16466  dedicatedImage,
16467  allocationCount,
16468  pAllocations);
16469  }
16470  }
16471  else
16472  {
16473  VkResult res = blockVector->Allocate(
16474  m_CurrentFrameIndex.load(),
16475  size,
16476  alignment,
16477  finalCreateInfo,
16478  suballocType,
16479  allocationCount,
16480  pAllocations);
16481  if(res == VK_SUCCESS)
16482  {
16483  return res;
16484  }
16485 
16486  // 5. Try dedicated memory.
16487  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16488  {
16489  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16490  }
16491 
16492  // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
16493  // which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
16494  // 3/4 of the maximum allocation count.
16495  if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
16496  {
16497  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16498  }
16499 
16500  res = AllocateDedicatedMemory(
16501  size,
16502  suballocType,
16503  memTypeIndex,
16504  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16505  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16506  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16507  finalCreateInfo.pUserData,
16508  finalCreateInfo.priority,
16509  dedicatedBuffer,
16510  dedicatedBufferUsage,
16511  dedicatedImage,
16512  allocationCount,
16513  pAllocations);
16514  if(res == VK_SUCCESS)
16515  {
16516  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16517  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16518  return VK_SUCCESS;
16519  }
16520  else
16521  {
16522  // Everything failed: Return error code.
16523  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16524  return res;
16525  }
16526  }
16527 }
16528 
16529 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16530  VkDeviceSize size,
16531  VmaSuballocationType suballocType,
16532  uint32_t memTypeIndex,
16533  bool withinBudget,
16534  bool map,
16535  bool isUserDataString,
16536  void* pUserData,
16537  float priority,
16538  VkBuffer dedicatedBuffer,
16539  VkBufferUsageFlags dedicatedBufferUsage,
16540  VkImage dedicatedImage,
16541  size_t allocationCount,
16542  VmaAllocation* pAllocations)
16543 {
16544  VMA_ASSERT(allocationCount > 0 && pAllocations);
16545 
16546  if(withinBudget)
16547  {
16548  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16549  VmaBudget heapBudget = {};
16550  GetBudget(&heapBudget, heapIndex, 1);
16551  if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16552  {
16553  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16554  }
16555  }
16556 
16557  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16558  allocInfo.memoryTypeIndex = memTypeIndex;
16559  allocInfo.allocationSize = size;
16560 
16561 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16562  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16563  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16564  {
16565  if(dedicatedBuffer != VK_NULL_HANDLE)
16566  {
16567  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16568  dedicatedAllocInfo.buffer = dedicatedBuffer;
16569  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16570  }
16571  else if(dedicatedImage != VK_NULL_HANDLE)
16572  {
16573  dedicatedAllocInfo.image = dedicatedImage;
16574  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16575  }
16576  }
16577 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16578 
16579 #if VMA_BUFFER_DEVICE_ADDRESS
16580  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16581  if(m_UseKhrBufferDeviceAddress)
16582  {
16583  bool canContainBufferWithDeviceAddress = true;
16584  if(dedicatedBuffer != VK_NULL_HANDLE)
16585  {
16586  canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16587  (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16588  }
16589  else if(dedicatedImage != VK_NULL_HANDLE)
16590  {
16591  canContainBufferWithDeviceAddress = false;
16592  }
16593  if(canContainBufferWithDeviceAddress)
16594  {
16595  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16596  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16597  }
16598  }
16599 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16600 
16601 #if VMA_MEMORY_PRIORITY
16602  VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
16603  if(m_UseExtMemoryPriority)
16604  {
16605  priorityInfo.priority = priority;
16606  VmaPnextChainPushFront(&allocInfo, &priorityInfo);
16607  }
16608 #endif // #if VMA_MEMORY_PRIORITY
16609 
16610  size_t allocIndex;
16611  VkResult res = VK_SUCCESS;
16612  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16613  {
16614  res = AllocateDedicatedMemoryPage(
16615  size,
16616  suballocType,
16617  memTypeIndex,
16618  allocInfo,
16619  map,
16620  isUserDataString,
16621  pUserData,
16622  pAllocations + allocIndex);
16623  if(res != VK_SUCCESS)
16624  {
16625  break;
16626  }
16627  }
16628 
16629  if(res == VK_SUCCESS)
16630  {
16631  // Register them in m_DedicatedAllocations.
16632  {
16633  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16634  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
16635  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16636  {
16637  dedicatedAllocations.PushBack(pAllocations[allocIndex]);
16638  }
16639  }
16640 
16641  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16642  }
16643  else
16644  {
16645  // Free all already created allocations.
16646  while(allocIndex--)
16647  {
16648  VmaAllocation currAlloc = pAllocations[allocIndex];
16649  VkDeviceMemory hMemory = currAlloc->GetMemory();
16650 
16651  /*
16652  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16653  before vkFreeMemory.
16654 
16655  if(currAlloc->GetMappedData() != VMA_NULL)
16656  {
16657  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16658  }
16659  */
16660 
16661  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16662  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16663  currAlloc->SetUserData(this, VMA_NULL);
16664  m_AllocationObjectAllocator.Free(currAlloc);
16665  }
16666 
16667  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16668  }
16669 
16670  return res;
16671 }
16672 
16673 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16674  VkDeviceSize size,
16675  VmaSuballocationType suballocType,
16676  uint32_t memTypeIndex,
16677  const VkMemoryAllocateInfo& allocInfo,
16678  bool map,
16679  bool isUserDataString,
16680  void* pUserData,
16681  VmaAllocation* pAllocation)
16682 {
16683  VkDeviceMemory hMemory = VK_NULL_HANDLE;
16684  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16685  if(res < 0)
16686  {
16687  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16688  return res;
16689  }
16690 
16691  void* pMappedData = VMA_NULL;
16692  if(map)
16693  {
16694  res = (*m_VulkanFunctions.vkMapMemory)(
16695  m_hDevice,
16696  hMemory,
16697  0,
16698  VK_WHOLE_SIZE,
16699  0,
16700  &pMappedData);
16701  if(res < 0)
16702  {
16703  VMA_DEBUG_LOG(" vkMapMemory FAILED");
16704  FreeVulkanMemory(memTypeIndex, size, hMemory);
16705  return res;
16706  }
16707  }
16708 
16709  *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16710  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16711  (*pAllocation)->SetUserData(this, pUserData);
16712  m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16713  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16714  {
16715  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16716  }
16717 
16718  return VK_SUCCESS;
16719 }
16720 
16721 void VmaAllocator_T::GetBufferMemoryRequirements(
16722  VkBuffer hBuffer,
16723  VkMemoryRequirements& memReq,
16724  bool& requiresDedicatedAllocation,
16725  bool& prefersDedicatedAllocation) const
16726 {
16727 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16728  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16729  {
16730  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16731  memReqInfo.buffer = hBuffer;
16732 
16733  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16734 
16735  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16736  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16737 
16738  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16739 
16740  memReq = memReq2.memoryRequirements;
16741  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16742  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16743  }
16744  else
16745 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16746  {
16747  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16748  requiresDedicatedAllocation = false;
16749  prefersDedicatedAllocation = false;
16750  }
16751 }
16752 
16753 void VmaAllocator_T::GetImageMemoryRequirements(
16754  VkImage hImage,
16755  VkMemoryRequirements& memReq,
16756  bool& requiresDedicatedAllocation,
16757  bool& prefersDedicatedAllocation) const
16758 {
16759 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16760  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16761  {
16762  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16763  memReqInfo.image = hImage;
16764 
16765  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16766 
16767  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16768  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16769 
16770  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16771 
16772  memReq = memReq2.memoryRequirements;
16773  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16774  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16775  }
16776  else
16777 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16778  {
16779  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16780  requiresDedicatedAllocation = false;
16781  prefersDedicatedAllocation = false;
16782  }
16783 }
16784 
16785 VkResult VmaAllocator_T::AllocateMemory(
16786  const VkMemoryRequirements& vkMemReq,
16787  bool requiresDedicatedAllocation,
16788  bool prefersDedicatedAllocation,
16789  VkBuffer dedicatedBuffer,
16790  VkBufferUsageFlags dedicatedBufferUsage,
16791  VkImage dedicatedImage,
16792  const VmaAllocationCreateInfo& createInfo,
16793  VmaSuballocationType suballocType,
16794  size_t allocationCount,
16795  VmaAllocation* pAllocations)
16796 {
16797  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16798 
16799  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16800 
16801  if(vkMemReq.size == 0)
16802  {
16803  return VK_ERROR_VALIDATION_FAILED_EXT;
16804  }
16805  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16806  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16807  {
16808  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16809  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16810  }
16811  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16813  {
16814  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16815  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16816  }
16817  if(requiresDedicatedAllocation)
16818  {
16819  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16820  {
16821  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16822  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16823  }
16824  if(createInfo.pool != VK_NULL_HANDLE)
16825  {
16826  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16827  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16828  }
16829  }
16830  if((createInfo.pool != VK_NULL_HANDLE) &&
16831  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16832  {
16833  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16834  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16835  }
16836 
16837  if(createInfo.pool != VK_NULL_HANDLE)
16838  {
16839  const VkDeviceSize alignmentForPool = VMA_MAX(
16840  vkMemReq.alignment,
16841  GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16842 
16843  VmaAllocationCreateInfo createInfoForPool = createInfo;
16844  // If memory type is not HOST_VISIBLE, disable MAPPED.
16845  if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16846  (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16847  {
16848  createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16849  }
16850 
16851  return createInfo.pool->m_BlockVector.Allocate(
16852  m_CurrentFrameIndex.load(),
16853  vkMemReq.size,
16854  alignmentForPool,
16855  createInfoForPool,
16856  suballocType,
16857  allocationCount,
16858  pAllocations);
16859  }
16860  else
16861  {
16862  // Bit mask of memory Vulkan types acceptable for this allocation.
16863  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16864  uint32_t memTypeIndex = UINT32_MAX;
16865  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16866  if(res == VK_SUCCESS)
16867  {
16868  VkDeviceSize alignmentForMemType = VMA_MAX(
16869  vkMemReq.alignment,
16870  GetMemoryTypeMinAlignment(memTypeIndex));
16871 
16872  res = AllocateMemoryOfType(
16873  vkMemReq.size,
16874  alignmentForMemType,
16875  requiresDedicatedAllocation || prefersDedicatedAllocation,
16876  dedicatedBuffer,
16877  dedicatedBufferUsage,
16878  dedicatedImage,
16879  createInfo,
16880  memTypeIndex,
16881  suballocType,
16882  allocationCount,
16883  pAllocations);
16884  // Succeeded on first try.
16885  if(res == VK_SUCCESS)
16886  {
16887  return res;
16888  }
16889  // Allocation from this memory type failed. Try other compatible memory types.
16890  else
16891  {
16892  for(;;)
16893  {
16894  // Remove old memTypeIndex from list of possibilities.
16895  memoryTypeBits &= ~(1u << memTypeIndex);
16896  // Find alternative memTypeIndex.
16897  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16898  if(res == VK_SUCCESS)
16899  {
16900  alignmentForMemType = VMA_MAX(
16901  vkMemReq.alignment,
16902  GetMemoryTypeMinAlignment(memTypeIndex));
16903 
16904  res = AllocateMemoryOfType(
16905  vkMemReq.size,
16906  alignmentForMemType,
16907  requiresDedicatedAllocation || prefersDedicatedAllocation,
16908  dedicatedBuffer,
16909  dedicatedBufferUsage,
16910  dedicatedImage,
16911  createInfo,
16912  memTypeIndex,
16913  suballocType,
16914  allocationCount,
16915  pAllocations);
16916  // Allocation from this alternative memory type succeeded.
16917  if(res == VK_SUCCESS)
16918  {
16919  return res;
16920  }
16921  // else: Allocation from this memory type failed. Try next one - next loop iteration.
16922  }
16923  // No other matching memory type index could be found.
16924  else
16925  {
16926  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16927  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16928  }
16929  }
16930  }
16931  }
16932  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16933  else
16934  return res;
16935  }
16936 }
16937 
16938 void VmaAllocator_T::FreeMemory(
16939  size_t allocationCount,
16940  const VmaAllocation* pAllocations)
16941 {
16942  VMA_ASSERT(pAllocations);
16943 
16944  for(size_t allocIndex = allocationCount; allocIndex--; )
16945  {
16946  VmaAllocation allocation = pAllocations[allocIndex];
16947 
16948  if(allocation != VK_NULL_HANDLE)
16949  {
16950  if(TouchAllocation(allocation))
16951  {
16952  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16953  {
16954  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16955  }
16956 
16957  switch(allocation->GetType())
16958  {
16959  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16960  {
16961  VmaBlockVector* pBlockVector = VMA_NULL;
16962  VmaPool hPool = allocation->GetBlock()->GetParentPool();
16963  if(hPool != VK_NULL_HANDLE)
16964  {
16965  pBlockVector = &hPool->m_BlockVector;
16966  }
16967  else
16968  {
16969  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16970  pBlockVector = m_pBlockVectors[memTypeIndex];
16971  }
16972  pBlockVector->Free(allocation);
16973  }
16974  break;
16975  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16976  FreeDedicatedMemory(allocation);
16977  break;
16978  default:
16979  VMA_ASSERT(0);
16980  }
16981  }
16982 
16983  // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16984  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16985  allocation->SetUserData(this, VMA_NULL);
16986  m_AllocationObjectAllocator.Free(allocation);
16987  }
16988  }
16989 }
16990 
16991 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16992 {
16993  // Initialize.
16994  InitStatInfo(pStats->total);
16995  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16996  InitStatInfo(pStats->memoryType[i]);
16997  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16998  InitStatInfo(pStats->memoryHeap[i]);
16999 
17000  // Process default pools.
17001  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17002  {
17003  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17004  VMA_ASSERT(pBlockVector);
17005  pBlockVector->AddStats(pStats);
17006  }
17007 
17008  // Process custom pools.
17009  {
17010  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17011  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17012  {
17013  pool->m_BlockVector.AddStats(pStats);
17014  }
17015  }
17016 
17017  // Process dedicated allocations.
17018  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17019  {
17020  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
17021  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17022  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17023  for(VmaAllocation alloc = dedicatedAllocList.Front();
17024  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
17025  {
17026  VmaStatInfo allocationStatInfo;
17027  alloc->DedicatedAllocCalcStatsInfo(allocationStatInfo);
17028  VmaAddStatInfo(pStats->total, allocationStatInfo);
17029  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
17030  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
17031  }
17032  }
17033 
17034  // Postprocess.
17035  VmaPostprocessCalcStatInfo(pStats->total);
17036  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
17037  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
17038  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
17039  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
17040 }
17041 
17042 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
17043 {
17044 #if VMA_MEMORY_BUDGET
17045  if(m_UseExtMemoryBudget)
17046  {
17047  if(m_Budget.m_OperationsSinceBudgetFetch < 30)
17048  {
17049  VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
17050  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17051  {
17052  const uint32_t heapIndex = firstHeap + i;
17053 
17054  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17055  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17056 
17057  if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
17058  {
17059  outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
17060  outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17061  }
17062  else
17063  {
17064  outBudget->usage = 0;
17065  }
17066 
17067  // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
17068  outBudget->budget = VMA_MIN(
17069  m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
17070  }
17071  }
17072  else
17073  {
17074  UpdateVulkanBudget(); // Outside of mutex lock
17075  GetBudget(outBudget, firstHeap, heapCount); // Recursion
17076  }
17077  }
17078  else
17079 #endif
17080  {
17081  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
17082  {
17083  const uint32_t heapIndex = firstHeap + i;
17084 
17085  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
17086  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
17087 
17088  outBudget->usage = outBudget->blockBytes;
17089  outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17090  }
17091  }
17092 }
17093 
17094 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
17095 
17096 VkResult VmaAllocator_T::DefragmentationBegin(
17097  const VmaDefragmentationInfo2& info,
17098  VmaDefragmentationStats* pStats,
17099  VmaDefragmentationContext* pContext)
17100 {
17101  if(info.pAllocationsChanged != VMA_NULL)
17102  {
17103  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
17104  }
17105 
17106  *pContext = vma_new(this, VmaDefragmentationContext_T)(
17107  this, m_CurrentFrameIndex.load(), info.flags, pStats);
17108 
17109  (*pContext)->AddPools(info.poolCount, info.pPools);
17110  (*pContext)->AddAllocations(
17112 
17113  VkResult res = (*pContext)->Defragment(
17116  info.commandBuffer, pStats, info.flags);
17117 
17118  if(res != VK_NOT_READY)
17119  {
17120  vma_delete(this, *pContext);
17121  *pContext = VMA_NULL;
17122  }
17123 
17124  return res;
17125 }
17126 
17127 VkResult VmaAllocator_T::DefragmentationEnd(
17128  VmaDefragmentationContext context)
17129 {
17130  vma_delete(this, context);
17131  return VK_SUCCESS;
17132 }
17133 
17134 VkResult VmaAllocator_T::DefragmentationPassBegin(
17136  VmaDefragmentationContext context)
17137 {
17138  return context->DefragmentPassBegin(pInfo);
17139 }
17140 VkResult VmaAllocator_T::DefragmentationPassEnd(
17141  VmaDefragmentationContext context)
17142 {
17143  return context->DefragmentPassEnd();
17144 
17145 }
17146 
17147 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17148 {
17149  if(hAllocation->CanBecomeLost())
17150  {
17151  /*
17152  Warning: This is a carefully designed algorithm.
17153  Do not modify unless you really know what you're doing :)
17154  */
17155  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17156  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17157  for(;;)
17158  {
17159  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17160  {
17161  pAllocationInfo->memoryType = UINT32_MAX;
17162  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17163  pAllocationInfo->offset = 0;
17164  pAllocationInfo->size = hAllocation->GetSize();
17165  pAllocationInfo->pMappedData = VMA_NULL;
17166  pAllocationInfo->pUserData = hAllocation->GetUserData();
17167  return;
17168  }
17169  else if(localLastUseFrameIndex == localCurrFrameIndex)
17170  {
17171  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17172  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17173  pAllocationInfo->offset = hAllocation->GetOffset();
17174  pAllocationInfo->size = hAllocation->GetSize();
17175  pAllocationInfo->pMappedData = VMA_NULL;
17176  pAllocationInfo->pUserData = hAllocation->GetUserData();
17177  return;
17178  }
17179  else // Last use time earlier than current time.
17180  {
17181  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17182  {
17183  localLastUseFrameIndex = localCurrFrameIndex;
17184  }
17185  }
17186  }
17187  }
17188  else
17189  {
17190 #if VMA_STATS_STRING_ENABLED
17191  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17192  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17193  for(;;)
17194  {
17195  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17196  if(localLastUseFrameIndex == localCurrFrameIndex)
17197  {
17198  break;
17199  }
17200  else // Last use time earlier than current time.
17201  {
17202  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17203  {
17204  localLastUseFrameIndex = localCurrFrameIndex;
17205  }
17206  }
17207  }
17208 #endif
17209 
17210  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17211  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17212  pAllocationInfo->offset = hAllocation->GetOffset();
17213  pAllocationInfo->size = hAllocation->GetSize();
17214  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17215  pAllocationInfo->pUserData = hAllocation->GetUserData();
17216  }
17217 }
17218 
17219 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17220 {
17221  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17222  if(hAllocation->CanBecomeLost())
17223  {
17224  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17225  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17226  for(;;)
17227  {
17228  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17229  {
17230  return false;
17231  }
17232  else if(localLastUseFrameIndex == localCurrFrameIndex)
17233  {
17234  return true;
17235  }
17236  else // Last use time earlier than current time.
17237  {
17238  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17239  {
17240  localLastUseFrameIndex = localCurrFrameIndex;
17241  }
17242  }
17243  }
17244  }
17245  else
17246  {
17247 #if VMA_STATS_STRING_ENABLED
17248  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17249  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17250  for(;;)
17251  {
17252  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17253  if(localLastUseFrameIndex == localCurrFrameIndex)
17254  {
17255  break;
17256  }
17257  else // Last use time earlier than current time.
17258  {
17259  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17260  {
17261  localLastUseFrameIndex = localCurrFrameIndex;
17262  }
17263  }
17264  }
17265 #endif
17266 
17267  return true;
17268  }
17269 }
17270 
17271 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17272 {
17273  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17274 
17275  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17276 
17277  if(newCreateInfo.maxBlockCount == 0)
17278  {
17279  newCreateInfo.maxBlockCount = SIZE_MAX;
17280  }
17281  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17282  {
17283  return VK_ERROR_INITIALIZATION_FAILED;
17284  }
17285  // Memory type index out of range or forbidden.
17286  if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17287  ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17288  {
17289  return VK_ERROR_FEATURE_NOT_PRESENT;
17290  }
17291 
17292  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17293 
17294  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17295 
17296  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17297  if(res != VK_SUCCESS)
17298  {
17299  vma_delete(this, *pPool);
17300  *pPool = VMA_NULL;
17301  return res;
17302  }
17303 
17304  // Add to m_Pools.
17305  {
17306  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17307  (*pPool)->SetId(m_NextPoolId++);
17308  m_Pools.PushBack(*pPool);
17309  }
17310 
17311  return VK_SUCCESS;
17312 }
17313 
17314 void VmaAllocator_T::DestroyPool(VmaPool pool)
17315 {
17316  // Remove from m_Pools.
17317  {
17318  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17319  m_Pools.Remove(pool);
17320  }
17321 
17322  vma_delete(this, pool);
17323 }
17324 
17325 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17326 {
17327  pool->m_BlockVector.GetPoolStats(pPoolStats);
17328 }
17329 
17330 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17331 {
17332  m_CurrentFrameIndex.store(frameIndex);
17333 
17334 #if VMA_MEMORY_BUDGET
17335  if(m_UseExtMemoryBudget)
17336  {
17337  UpdateVulkanBudget();
17338  }
17339 #endif // #if VMA_MEMORY_BUDGET
17340 }
17341 
17342 void VmaAllocator_T::MakePoolAllocationsLost(
17343  VmaPool hPool,
17344  size_t* pLostAllocationCount)
17345 {
17346  hPool->m_BlockVector.MakePoolAllocationsLost(
17347  m_CurrentFrameIndex.load(),
17348  pLostAllocationCount);
17349 }
17350 
17351 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17352 {
17353  return hPool->m_BlockVector.CheckCorruption();
17354 }
17355 
17356 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17357 {
17358  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17359 
17360  // Process default pools.
17361  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17362  {
17363  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17364  {
17365  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17366  VMA_ASSERT(pBlockVector);
17367  VkResult localRes = pBlockVector->CheckCorruption();
17368  switch(localRes)
17369  {
17370  case VK_ERROR_FEATURE_NOT_PRESENT:
17371  break;
17372  case VK_SUCCESS:
17373  finalRes = VK_SUCCESS;
17374  break;
17375  default:
17376  return localRes;
17377  }
17378  }
17379  }
17380 
17381  // Process custom pools.
17382  {
17383  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17384  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
17385  {
17386  if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17387  {
17388  VkResult localRes = pool->m_BlockVector.CheckCorruption();
17389  switch(localRes)
17390  {
17391  case VK_ERROR_FEATURE_NOT_PRESENT:
17392  break;
17393  case VK_SUCCESS:
17394  finalRes = VK_SUCCESS;
17395  break;
17396  default:
17397  return localRes;
17398  }
17399  }
17400  }
17401  }
17402 
17403  return finalRes;
17404 }
17405 
17406 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17407 {
17408  *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17409  (*pAllocation)->InitLost();
17410 }
17411 
17412 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
17413 template<typename T>
17414 struct AtomicTransactionalIncrement
17415 {
17416 public:
17417  typedef std::atomic<T> AtomicT;
17418  ~AtomicTransactionalIncrement()
17419  {
17420  if(m_Atomic)
17421  --(*m_Atomic);
17422  }
17423  T Increment(AtomicT* atomic)
17424  {
17425  m_Atomic = atomic;
17426  return m_Atomic->fetch_add(1);
17427  }
17428  void Commit()
17429  {
17430  m_Atomic = nullptr;
17431  }
17432 
17433 private:
17434  AtomicT* m_Atomic = nullptr;
17435 };
17436 
17437 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17438 {
17439  AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
17440  const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
17441 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
17442  if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
17443  {
17444  return VK_ERROR_TOO_MANY_OBJECTS;
17445  }
17446 #endif
17447 
17448  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17449 
17450  // HeapSizeLimit is in effect for this heap.
17451  if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17452  {
17453  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17454  VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17455  for(;;)
17456  {
17457  const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17458  if(blockBytesAfterAllocation > heapSize)
17459  {
17460  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17461  }
17462  if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17463  {
17464  break;
17465  }
17466  }
17467  }
17468  else
17469  {
17470  m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17471  }
17472 
17473  // VULKAN CALL vkAllocateMemory.
17474  VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17475 
17476  if(res == VK_SUCCESS)
17477  {
17478 #if VMA_MEMORY_BUDGET
17479  ++m_Budget.m_OperationsSinceBudgetFetch;
17480 #endif
17481 
17482  // Informative callback.
17483  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17484  {
17485  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17486  }
17487 
17488  deviceMemoryCountIncrement.Commit();
17489  }
17490  else
17491  {
17492  m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17493  }
17494 
17495  return res;
17496 }
17497 
17498 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17499 {
17500  // Informative callback.
17501  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17502  {
17503  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17504  }
17505 
17506  // VULKAN CALL vkFreeMemory.
17507  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17508 
17509  m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17510 
17511  --m_DeviceMemoryCount;
17512 }
17513 
17514 VkResult VmaAllocator_T::BindVulkanBuffer(
17515  VkDeviceMemory memory,
17516  VkDeviceSize memoryOffset,
17517  VkBuffer buffer,
17518  const void* pNext)
17519 {
17520  if(pNext != VMA_NULL)
17521  {
17522 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17523  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17524  m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17525  {
17526  VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17527  bindBufferMemoryInfo.pNext = pNext;
17528  bindBufferMemoryInfo.buffer = buffer;
17529  bindBufferMemoryInfo.memory = memory;
17530  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17531  return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17532  }
17533  else
17534 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17535  {
17536  return VK_ERROR_EXTENSION_NOT_PRESENT;
17537  }
17538  }
17539  else
17540  {
17541  return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17542  }
17543 }
17544 
17545 VkResult VmaAllocator_T::BindVulkanImage(
17546  VkDeviceMemory memory,
17547  VkDeviceSize memoryOffset,
17548  VkImage image,
17549  const void* pNext)
17550 {
17551  if(pNext != VMA_NULL)
17552  {
17553 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17554  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17555  m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17556  {
17557  VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17558  bindBufferMemoryInfo.pNext = pNext;
17559  bindBufferMemoryInfo.image = image;
17560  bindBufferMemoryInfo.memory = memory;
17561  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17562  return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17563  }
17564  else
17565 #endif // #if VMA_BIND_MEMORY2
17566  {
17567  return VK_ERROR_EXTENSION_NOT_PRESENT;
17568  }
17569  }
17570  else
17571  {
17572  return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17573  }
17574 }
17575 
17576 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17577 {
17578  if(hAllocation->CanBecomeLost())
17579  {
17580  return VK_ERROR_MEMORY_MAP_FAILED;
17581  }
17582 
17583  switch(hAllocation->GetType())
17584  {
17585  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17586  {
17587  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17588  char *pBytes = VMA_NULL;
17589  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17590  if(res == VK_SUCCESS)
17591  {
17592  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17593  hAllocation->BlockAllocMap();
17594  }
17595  return res;
17596  }
17597  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17598  return hAllocation->DedicatedAllocMap(this, ppData);
17599  default:
17600  VMA_ASSERT(0);
17601  return VK_ERROR_MEMORY_MAP_FAILED;
17602  }
17603 }
17604 
17605 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17606 {
17607  switch(hAllocation->GetType())
17608  {
17609  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17610  {
17611  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17612  hAllocation->BlockAllocUnmap();
17613  pBlock->Unmap(this, 1);
17614  }
17615  break;
17616  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17617  hAllocation->DedicatedAllocUnmap(this);
17618  break;
17619  default:
17620  VMA_ASSERT(0);
17621  }
17622 }
17623 
17624 VkResult VmaAllocator_T::BindBufferMemory(
17625  VmaAllocation hAllocation,
17626  VkDeviceSize allocationLocalOffset,
17627  VkBuffer hBuffer,
17628  const void* pNext)
17629 {
17630  VkResult res = VK_SUCCESS;
17631  switch(hAllocation->GetType())
17632  {
17633  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17634  res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17635  break;
17636  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17637  {
17638  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17639  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17640  res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17641  break;
17642  }
17643  default:
17644  VMA_ASSERT(0);
17645  }
17646  return res;
17647 }
17648 
17649 VkResult VmaAllocator_T::BindImageMemory(
17650  VmaAllocation hAllocation,
17651  VkDeviceSize allocationLocalOffset,
17652  VkImage hImage,
17653  const void* pNext)
17654 {
17655  VkResult res = VK_SUCCESS;
17656  switch(hAllocation->GetType())
17657  {
17658  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17659  res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17660  break;
17661  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17662  {
17663  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17664  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17665  res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17666  break;
17667  }
17668  default:
17669  VMA_ASSERT(0);
17670  }
17671  return res;
17672 }
17673 
17674 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17675  VmaAllocation hAllocation,
17676  VkDeviceSize offset, VkDeviceSize size,
17677  VMA_CACHE_OPERATION op)
17678 {
17679  VkResult res = VK_SUCCESS;
17680 
17681  VkMappedMemoryRange memRange = {};
17682  if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17683  {
17684  switch(op)
17685  {
17686  case VMA_CACHE_FLUSH:
17687  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17688  break;
17689  case VMA_CACHE_INVALIDATE:
17690  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17691  break;
17692  default:
17693  VMA_ASSERT(0);
17694  }
17695  }
17696  // else: Just ignore this call.
17697  return res;
17698 }
17699 
17700 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17701  uint32_t allocationCount,
17702  const VmaAllocation* allocations,
17703  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17704  VMA_CACHE_OPERATION op)
17705 {
17706  typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17707  typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17708  RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17709 
17710  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17711  {
17712  const VmaAllocation alloc = allocations[allocIndex];
17713  const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17714  const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17715  VkMappedMemoryRange newRange;
17716  if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17717  {
17718  ranges.push_back(newRange);
17719  }
17720  }
17721 
17722  VkResult res = VK_SUCCESS;
17723  if(!ranges.empty())
17724  {
17725  switch(op)
17726  {
17727  case VMA_CACHE_FLUSH:
17728  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17729  break;
17730  case VMA_CACHE_INVALIDATE:
17731  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17732  break;
17733  default:
17734  VMA_ASSERT(0);
17735  }
17736  }
17737  // else: Just ignore this call.
17738  return res;
17739 }
17740 
17741 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17742 {
17743  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17744 
17745  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17746  {
17747  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17748  DedicatedAllocationLinkedList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex];
17749  dedicatedAllocations.Remove(allocation);
17750  }
17751 
17752  VkDeviceMemory hMemory = allocation->GetMemory();
17753 
17754  /*
17755  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17756  before vkFreeMemory.
17757 
17758  if(allocation->GetMappedData() != VMA_NULL)
17759  {
17760  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17761  }
17762  */
17763 
17764  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17765 
17766  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17767 }
17768 
17769 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17770 {
17771  VkBufferCreateInfo dummyBufCreateInfo;
17772  VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17773 
17774  uint32_t memoryTypeBits = 0;
17775 
17776  // Create buffer.
17777  VkBuffer buf = VK_NULL_HANDLE;
17778  VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17779  m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17780  if(res == VK_SUCCESS)
17781  {
17782  // Query for supported memory types.
17783  VkMemoryRequirements memReq;
17784  (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17785  memoryTypeBits = memReq.memoryTypeBits;
17786 
17787  // Destroy buffer.
17788  (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17789  }
17790 
17791  return memoryTypeBits;
17792 }
17793 
17794 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17795 {
17796  // Make sure memory information is already fetched.
17797  VMA_ASSERT(GetMemoryTypeCount() > 0);
17798 
17799  uint32_t memoryTypeBits = UINT32_MAX;
17800 
17801  if(!m_UseAmdDeviceCoherentMemory)
17802  {
17803  // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17804  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17805  {
17806  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17807  {
17808  memoryTypeBits &= ~(1u << memTypeIndex);
17809  }
17810  }
17811  }
17812 
17813  return memoryTypeBits;
17814 }
17815 
17816 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17817  VmaAllocation allocation,
17818  VkDeviceSize offset, VkDeviceSize size,
17819  VkMappedMemoryRange& outRange) const
17820 {
17821  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17822  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17823  {
17824  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17825  const VkDeviceSize allocationSize = allocation->GetSize();
17826  VMA_ASSERT(offset <= allocationSize);
17827 
17828  outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17829  outRange.pNext = VMA_NULL;
17830  outRange.memory = allocation->GetMemory();
17831 
17832  switch(allocation->GetType())
17833  {
17834  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17835  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17836  if(size == VK_WHOLE_SIZE)
17837  {
17838  outRange.size = allocationSize - outRange.offset;
17839  }
17840  else
17841  {
17842  VMA_ASSERT(offset + size <= allocationSize);
17843  outRange.size = VMA_MIN(
17844  VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17845  allocationSize - outRange.offset);
17846  }
17847  break;
17848  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17849  {
17850  // 1. Still within this allocation.
17851  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17852  if(size == VK_WHOLE_SIZE)
17853  {
17854  size = allocationSize - offset;
17855  }
17856  else
17857  {
17858  VMA_ASSERT(offset + size <= allocationSize);
17859  }
17860  outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17861 
17862  // 2. Adjust to whole block.
17863  const VkDeviceSize allocationOffset = allocation->GetOffset();
17864  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17865  const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17866  outRange.offset += allocationOffset;
17867  outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17868 
17869  break;
17870  }
17871  default:
17872  VMA_ASSERT(0);
17873  }
17874  return true;
17875  }
17876  return false;
17877 }
17878 
17879 #if VMA_MEMORY_BUDGET
17880 
17881 void VmaAllocator_T::UpdateVulkanBudget()
17882 {
17883  VMA_ASSERT(m_UseExtMemoryBudget);
17884 
17885  VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17886 
17887  VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17888  VmaPnextChainPushFront(&memProps, &budgetProps);
17889 
17890  GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17891 
17892  {
17893  VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17894 
17895  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17896  {
17897  m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17898  m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17899  m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17900 
17901  // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17902  if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17903  {
17904  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17905  }
17906  else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17907  {
17908  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17909  }
17910  if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17911  {
17912  m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17913  }
17914  }
17915  m_Budget.m_OperationsSinceBudgetFetch = 0;
17916  }
17917 }
17918 
17919 #endif // #if VMA_MEMORY_BUDGET
17920 
17921 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17922 {
17923  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17924  !hAllocation->CanBecomeLost() &&
17925  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17926  {
17927  void* pData = VMA_NULL;
17928  VkResult res = Map(hAllocation, &pData);
17929  if(res == VK_SUCCESS)
17930  {
17931  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17932  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17933  Unmap(hAllocation);
17934  }
17935  else
17936  {
17937  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17938  }
17939  }
17940 }
17941 
17942 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17943 {
17944  uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17945  if(memoryTypeBits == UINT32_MAX)
17946  {
17947  memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17948  m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17949  }
17950  return memoryTypeBits;
17951 }
17952 
17953 #if VMA_STATS_STRING_ENABLED
17954 
17955 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17956 {
17957  bool dedicatedAllocationsStarted = false;
17958  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17959  {
17960  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17961  DedicatedAllocationLinkedList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
17962  if(!dedicatedAllocList.IsEmpty())
17963  {
17964  if(dedicatedAllocationsStarted == false)
17965  {
17966  dedicatedAllocationsStarted = true;
17967  json.WriteString("DedicatedAllocations");
17968  json.BeginObject();
17969  }
17970 
17971  json.BeginString("Type ");
17972  json.ContinueString(memTypeIndex);
17973  json.EndString();
17974 
17975  json.BeginArray();
17976 
17977  for(VmaAllocation alloc = dedicatedAllocList.Front();
17978  alloc != VMA_NULL; alloc = dedicatedAllocList.GetNext(alloc))
17979  {
17980  json.BeginObject(true);
17981  alloc->PrintParameters(json);
17982  json.EndObject();
17983  }
17984 
17985  json.EndArray();
17986  }
17987  }
17988  if(dedicatedAllocationsStarted)
17989  {
17990  json.EndObject();
17991  }
17992 
17993  {
17994  bool allocationsStarted = false;
17995  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17996  {
17997  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17998  {
17999  if(allocationsStarted == false)
18000  {
18001  allocationsStarted = true;
18002  json.WriteString("DefaultPools");
18003  json.BeginObject();
18004  }
18005 
18006  json.BeginString("Type ");
18007  json.ContinueString(memTypeIndex);
18008  json.EndString();
18009 
18010  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
18011  }
18012  }
18013  if(allocationsStarted)
18014  {
18015  json.EndObject();
18016  }
18017  }
18018 
18019  // Custom pools
18020  {
18021  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
18022  if(!m_Pools.IsEmpty())
18023  {
18024  json.WriteString("Pools");
18025  json.BeginObject();
18026  for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
18027  {
18028  json.BeginString();
18029  json.ContinueString(pool->GetId());
18030  json.EndString();
18031 
18032  pool->m_BlockVector.PrintDetailedMap(json);
18033  }
18034  json.EndObject();
18035  }
18036  }
18037 }
18038 
18039 #endif // #if VMA_STATS_STRING_ENABLED
18040 
18042 // Public interface
18043 
18044 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
18045  const VmaAllocatorCreateInfo* pCreateInfo,
18046  VmaAllocator* pAllocator)
18047 {
18048  VMA_ASSERT(pCreateInfo && pAllocator);
18049  VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
18050  (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
18051  VMA_DEBUG_LOG("vmaCreateAllocator");
18052  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
18053  return (*pAllocator)->Init(pCreateInfo);
18054 }
18055 
18056 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
18057  VmaAllocator allocator)
18058 {
18059  if(allocator != VK_NULL_HANDLE)
18060  {
18061  VMA_DEBUG_LOG("vmaDestroyAllocator");
18062  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
18063  vma_delete(&allocationCallbacks, allocator);
18064  }
18065 }
18066 
18067 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
18068 {
18069  VMA_ASSERT(allocator && pAllocatorInfo);
18070  pAllocatorInfo->instance = allocator->m_hInstance;
18071  pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
18072  pAllocatorInfo->device = allocator->m_hDevice;
18073 }
18074 
18075 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
18076  VmaAllocator allocator,
18077  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
18078 {
18079  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
18080  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
18081 }
18082 
18083 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
18084  VmaAllocator allocator,
18085  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
18086 {
18087  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
18088  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
18089 }
18090 
18091 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
18092  VmaAllocator allocator,
18093  uint32_t memoryTypeIndex,
18094  VkMemoryPropertyFlags* pFlags)
18095 {
18096  VMA_ASSERT(allocator && pFlags);
18097  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
18098  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
18099 }
18100 
18101 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
18102  VmaAllocator allocator,
18103  uint32_t frameIndex)
18104 {
18105  VMA_ASSERT(allocator);
18106  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
18107 
18108  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18109 
18110  allocator->SetCurrentFrameIndex(frameIndex);
18111 }
18112 
18113 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
18114  VmaAllocator allocator,
18115  VmaStats* pStats)
18116 {
18117  VMA_ASSERT(allocator && pStats);
18118  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18119  allocator->CalculateStats(pStats);
18120 }
18121 
18122 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
18123  VmaAllocator allocator,
18124  VmaBudget* pBudget)
18125 {
18126  VMA_ASSERT(allocator && pBudget);
18127  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18128  allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
18129 }
18130 
18131 #if VMA_STATS_STRING_ENABLED
18132 
18133 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
18134  VmaAllocator allocator,
18135  char** ppStatsString,
18136  VkBool32 detailedMap)
18137 {
18138  VMA_ASSERT(allocator && ppStatsString);
18139  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18140 
18141  VmaStringBuilder sb(allocator);
18142  {
18143  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
18144  json.BeginObject();
18145 
18146  VmaBudget budget[VK_MAX_MEMORY_HEAPS];
18147  allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
18148 
18149  VmaStats stats;
18150  allocator->CalculateStats(&stats);
18151 
18152  json.WriteString("Total");
18153  VmaPrintStatInfo(json, stats.total);
18154 
18155  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18156  {
18157  json.BeginString("Heap ");
18158  json.ContinueString(heapIndex);
18159  json.EndString();
18160  json.BeginObject();
18161 
18162  json.WriteString("Size");
18163  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18164 
18165  json.WriteString("Flags");
18166  json.BeginArray(true);
18167  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18168  {
18169  json.WriteString("DEVICE_LOCAL");
18170  }
18171  json.EndArray();
18172 
18173  json.WriteString("Budget");
18174  json.BeginObject();
18175  {
18176  json.WriteString("BlockBytes");
18177  json.WriteNumber(budget[heapIndex].blockBytes);
18178  json.WriteString("AllocationBytes");
18179  json.WriteNumber(budget[heapIndex].allocationBytes);
18180  json.WriteString("Usage");
18181  json.WriteNumber(budget[heapIndex].usage);
18182  json.WriteString("Budget");
18183  json.WriteNumber(budget[heapIndex].budget);
18184  }
18185  json.EndObject();
18186 
18187  if(stats.memoryHeap[heapIndex].blockCount > 0)
18188  {
18189  json.WriteString("Stats");
18190  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18191  }
18192 
18193  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18194  {
18195  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18196  {
18197  json.BeginString("Type ");
18198  json.ContinueString(typeIndex);
18199  json.EndString();
18200 
18201  json.BeginObject();
18202 
18203  json.WriteString("Flags");
18204  json.BeginArray(true);
18205  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18206  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18207  {
18208  json.WriteString("DEVICE_LOCAL");
18209  }
18210  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18211  {
18212  json.WriteString("HOST_VISIBLE");
18213  }
18214  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18215  {
18216  json.WriteString("HOST_COHERENT");
18217  }
18218  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18219  {
18220  json.WriteString("HOST_CACHED");
18221  }
18222  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18223  {
18224  json.WriteString("LAZILY_ALLOCATED");
18225  }
18226 #if VMA_VULKAN_VERSION >= 1001000
18227  if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18228  {
18229  json.WriteString("PROTECTED");
18230  }
18231 #endif // #if VMA_VULKAN_VERSION >= 1001000
18232 #if VK_AMD_device_coherent_memory
18233  if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18234  {
18235  json.WriteString("DEVICE_COHERENT");
18236  }
18237  if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18238  {
18239  json.WriteString("DEVICE_UNCACHED");
18240  }
18241 #endif // #if VK_AMD_device_coherent_memory
18242  json.EndArray();
18243 
18244  if(stats.memoryType[typeIndex].blockCount > 0)
18245  {
18246  json.WriteString("Stats");
18247  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18248  }
18249 
18250  json.EndObject();
18251  }
18252  }
18253 
18254  json.EndObject();
18255  }
18256  if(detailedMap == VK_TRUE)
18257  {
18258  allocator->PrintDetailedMap(json);
18259  }
18260 
18261  json.EndObject();
18262  }
18263 
18264  const size_t len = sb.GetLength();
18265  char* const pChars = vma_new_array(allocator, char, len + 1);
18266  if(len > 0)
18267  {
18268  memcpy(pChars, sb.GetData(), len);
18269  }
18270  pChars[len] = '\0';
18271  *ppStatsString = pChars;
18272 }
18273 
18274 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18275  VmaAllocator allocator,
18276  char* pStatsString)
18277 {
18278  if(pStatsString != VMA_NULL)
18279  {
18280  VMA_ASSERT(allocator);
18281  size_t len = strlen(pStatsString);
18282  vma_delete_array(allocator, pStatsString, len + 1);
18283  }
18284 }
18285 
18286 #endif // #if VMA_STATS_STRING_ENABLED
18287 
18288 /*
18289 This function is not protected by any mutex because it just reads immutable data.
18290 */
18291 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18292  VmaAllocator allocator,
18293  uint32_t memoryTypeBits,
18294  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18295  uint32_t* pMemoryTypeIndex)
18296 {
18297  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18298  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18299  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18300 
18301  memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18302 
18303  if(pAllocationCreateInfo->memoryTypeBits != 0)
18304  {
18305  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18306  }
18307 
18308  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18309  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18310  uint32_t notPreferredFlags = 0;
18311 
18312  // Convert usage to requiredFlags and preferredFlags.
18313  switch(pAllocationCreateInfo->usage)
18314  {
18316  break;
18318  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18319  {
18320  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18321  }
18322  break;
18324  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18325  break;
18327  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18328  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18329  {
18330  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18331  }
18332  break;
18334  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18335  preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18336  break;
18338  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18339  break;
18341  requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18342  break;
18343  default:
18344  VMA_ASSERT(0);
18345  break;
18346  }
18347 
18348  // Avoid DEVICE_COHERENT unless explicitly requested.
18349  if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18350  (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18351  {
18352  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18353  }
18354 
18355  *pMemoryTypeIndex = UINT32_MAX;
18356  uint32_t minCost = UINT32_MAX;
18357  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18358  memTypeIndex < allocator->GetMemoryTypeCount();
18359  ++memTypeIndex, memTypeBit <<= 1)
18360  {
18361  // This memory type is acceptable according to memoryTypeBits bitmask.
18362  if((memTypeBit & memoryTypeBits) != 0)
18363  {
18364  const VkMemoryPropertyFlags currFlags =
18365  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18366  // This memory type contains requiredFlags.
18367  if((requiredFlags & ~currFlags) == 0)
18368  {
18369  // Calculate cost as number of bits from preferredFlags not present in this memory type.
18370  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18371  VmaCountBitsSet(currFlags & notPreferredFlags);
18372  // Remember memory type with lowest cost.
18373  if(currCost < minCost)
18374  {
18375  *pMemoryTypeIndex = memTypeIndex;
18376  if(currCost == 0)
18377  {
18378  return VK_SUCCESS;
18379  }
18380  minCost = currCost;
18381  }
18382  }
18383  }
18384  }
18385  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18386 }
18387 
18388 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18389  VmaAllocator allocator,
18390  const VkBufferCreateInfo* pBufferCreateInfo,
18391  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18392  uint32_t* pMemoryTypeIndex)
18393 {
18394  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18395  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18396  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18397  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18398 
18399  const VkDevice hDev = allocator->m_hDevice;
18400  VkBuffer hBuffer = VK_NULL_HANDLE;
18401  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18402  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18403  if(res == VK_SUCCESS)
18404  {
18405  VkMemoryRequirements memReq = {};
18406  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18407  hDev, hBuffer, &memReq);
18408 
18409  res = vmaFindMemoryTypeIndex(
18410  allocator,
18411  memReq.memoryTypeBits,
18412  pAllocationCreateInfo,
18413  pMemoryTypeIndex);
18414 
18415  allocator->GetVulkanFunctions().vkDestroyBuffer(
18416  hDev, hBuffer, allocator->GetAllocationCallbacks());
18417  }
18418  return res;
18419 }
18420 
18421 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18422  VmaAllocator allocator,
18423  const VkImageCreateInfo* pImageCreateInfo,
18424  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18425  uint32_t* pMemoryTypeIndex)
18426 {
18427  VMA_ASSERT(allocator != VK_NULL_HANDLE);
18428  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18429  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18430  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18431 
18432  const VkDevice hDev = allocator->m_hDevice;
18433  VkImage hImage = VK_NULL_HANDLE;
18434  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18435  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18436  if(res == VK_SUCCESS)
18437  {
18438  VkMemoryRequirements memReq = {};
18439  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18440  hDev, hImage, &memReq);
18441 
18442  res = vmaFindMemoryTypeIndex(
18443  allocator,
18444  memReq.memoryTypeBits,
18445  pAllocationCreateInfo,
18446  pMemoryTypeIndex);
18447 
18448  allocator->GetVulkanFunctions().vkDestroyImage(
18449  hDev, hImage, allocator->GetAllocationCallbacks());
18450  }
18451  return res;
18452 }
18453 
18454 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18455  VmaAllocator allocator,
18456  const VmaPoolCreateInfo* pCreateInfo,
18457  VmaPool* pPool)
18458 {
18459  VMA_ASSERT(allocator && pCreateInfo && pPool);
18460 
18461  VMA_DEBUG_LOG("vmaCreatePool");
18462 
18463  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18464 
18465  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18466 
18467 #if VMA_RECORDING_ENABLED
18468  if(allocator->GetRecorder() != VMA_NULL)
18469  {
18470  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18471  }
18472 #endif
18473 
18474  return res;
18475 }
18476 
18477 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18478  VmaAllocator allocator,
18479  VmaPool pool)
18480 {
18481  VMA_ASSERT(allocator);
18482 
18483  if(pool == VK_NULL_HANDLE)
18484  {
18485  return;
18486  }
18487 
18488  VMA_DEBUG_LOG("vmaDestroyPool");
18489 
18490  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18491 
18492 #if VMA_RECORDING_ENABLED
18493  if(allocator->GetRecorder() != VMA_NULL)
18494  {
18495  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18496  }
18497 #endif
18498 
18499  allocator->DestroyPool(pool);
18500 }
18501 
18502 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18503  VmaAllocator allocator,
18504  VmaPool pool,
18505  VmaPoolStats* pPoolStats)
18506 {
18507  VMA_ASSERT(allocator && pool && pPoolStats);
18508 
18509  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18510 
18511  allocator->GetPoolStats(pool, pPoolStats);
18512 }
18513 
18514 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18515  VmaAllocator allocator,
18516  VmaPool pool,
18517  size_t* pLostAllocationCount)
18518 {
18519  VMA_ASSERT(allocator && pool);
18520 
18521  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18522 
18523 #if VMA_RECORDING_ENABLED
18524  if(allocator->GetRecorder() != VMA_NULL)
18525  {
18526  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18527  }
18528 #endif
18529 
18530  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18531 }
18532 
18533 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18534 {
18535  VMA_ASSERT(allocator && pool);
18536 
18537  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18538 
18539  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18540 
18541  return allocator->CheckPoolCorruption(pool);
18542 }
18543 
18544 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18545  VmaAllocator allocator,
18546  VmaPool pool,
18547  const char** ppName)
18548 {
18549  VMA_ASSERT(allocator && pool && ppName);
18550 
18551  VMA_DEBUG_LOG("vmaGetPoolName");
18552 
18553  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18554 
18555  *ppName = pool->GetName();
18556 }
18557 
18558 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18559  VmaAllocator allocator,
18560  VmaPool pool,
18561  const char* pName)
18562 {
18563  VMA_ASSERT(allocator && pool);
18564 
18565  VMA_DEBUG_LOG("vmaSetPoolName");
18566 
18567  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18568 
18569  pool->SetName(pName);
18570 
18571 #if VMA_RECORDING_ENABLED
18572  if(allocator->GetRecorder() != VMA_NULL)
18573  {
18574  allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18575  }
18576 #endif
18577 }
18578 
18579 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18580  VmaAllocator allocator,
18581  const VkMemoryRequirements* pVkMemoryRequirements,
18582  const VmaAllocationCreateInfo* pCreateInfo,
18583  VmaAllocation* pAllocation,
18584  VmaAllocationInfo* pAllocationInfo)
18585 {
18586  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18587 
18588  VMA_DEBUG_LOG("vmaAllocateMemory");
18589 
18590  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18591 
18592  VkResult result = allocator->AllocateMemory(
18593  *pVkMemoryRequirements,
18594  false, // requiresDedicatedAllocation
18595  false, // prefersDedicatedAllocation
18596  VK_NULL_HANDLE, // dedicatedBuffer
18597  UINT32_MAX, // dedicatedBufferUsage
18598  VK_NULL_HANDLE, // dedicatedImage
18599  *pCreateInfo,
18600  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18601  1, // allocationCount
18602  pAllocation);
18603 
18604 #if VMA_RECORDING_ENABLED
18605  if(allocator->GetRecorder() != VMA_NULL)
18606  {
18607  allocator->GetRecorder()->RecordAllocateMemory(
18608  allocator->GetCurrentFrameIndex(),
18609  *pVkMemoryRequirements,
18610  *pCreateInfo,
18611  *pAllocation);
18612  }
18613 #endif
18614 
18615  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18616  {
18617  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18618  }
18619 
18620  return result;
18621 }
18622 
18623 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18624  VmaAllocator allocator,
18625  const VkMemoryRequirements* pVkMemoryRequirements,
18626  const VmaAllocationCreateInfo* pCreateInfo,
18627  size_t allocationCount,
18628  VmaAllocation* pAllocations,
18629  VmaAllocationInfo* pAllocationInfo)
18630 {
18631  if(allocationCount == 0)
18632  {
18633  return VK_SUCCESS;
18634  }
18635 
18636  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18637 
18638  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18639 
18640  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18641 
18642  VkResult result = allocator->AllocateMemory(
18643  *pVkMemoryRequirements,
18644  false, // requiresDedicatedAllocation
18645  false, // prefersDedicatedAllocation
18646  VK_NULL_HANDLE, // dedicatedBuffer
18647  UINT32_MAX, // dedicatedBufferUsage
18648  VK_NULL_HANDLE, // dedicatedImage
18649  *pCreateInfo,
18650  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18651  allocationCount,
18652  pAllocations);
18653 
18654 #if VMA_RECORDING_ENABLED
18655  if(allocator->GetRecorder() != VMA_NULL)
18656  {
18657  allocator->GetRecorder()->RecordAllocateMemoryPages(
18658  allocator->GetCurrentFrameIndex(),
18659  *pVkMemoryRequirements,
18660  *pCreateInfo,
18661  (uint64_t)allocationCount,
18662  pAllocations);
18663  }
18664 #endif
18665 
18666  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18667  {
18668  for(size_t i = 0; i < allocationCount; ++i)
18669  {
18670  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18671  }
18672  }
18673 
18674  return result;
18675 }
18676 
18677 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18678  VmaAllocator allocator,
18679  VkBuffer buffer,
18680  const VmaAllocationCreateInfo* pCreateInfo,
18681  VmaAllocation* pAllocation,
18682  VmaAllocationInfo* pAllocationInfo)
18683 {
18684  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18685 
18686  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18687 
18688  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18689 
18690  VkMemoryRequirements vkMemReq = {};
18691  bool requiresDedicatedAllocation = false;
18692  bool prefersDedicatedAllocation = false;
18693  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18694  requiresDedicatedAllocation,
18695  prefersDedicatedAllocation);
18696 
18697  VkResult result = allocator->AllocateMemory(
18698  vkMemReq,
18699  requiresDedicatedAllocation,
18700  prefersDedicatedAllocation,
18701  buffer, // dedicatedBuffer
18702  UINT32_MAX, // dedicatedBufferUsage
18703  VK_NULL_HANDLE, // dedicatedImage
18704  *pCreateInfo,
18705  VMA_SUBALLOCATION_TYPE_BUFFER,
18706  1, // allocationCount
18707  pAllocation);
18708 
18709 #if VMA_RECORDING_ENABLED
18710  if(allocator->GetRecorder() != VMA_NULL)
18711  {
18712  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18713  allocator->GetCurrentFrameIndex(),
18714  vkMemReq,
18715  requiresDedicatedAllocation,
18716  prefersDedicatedAllocation,
18717  *pCreateInfo,
18718  *pAllocation);
18719  }
18720 #endif
18721 
18722  if(pAllocationInfo && result == VK_SUCCESS)
18723  {
18724  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18725  }
18726 
18727  return result;
18728 }
18729 
18730 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18731  VmaAllocator allocator,
18732  VkImage image,
18733  const VmaAllocationCreateInfo* pCreateInfo,
18734  VmaAllocation* pAllocation,
18735  VmaAllocationInfo* pAllocationInfo)
18736 {
18737  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18738 
18739  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18740 
18741  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18742 
18743  VkMemoryRequirements vkMemReq = {};
18744  bool requiresDedicatedAllocation = false;
18745  bool prefersDedicatedAllocation = false;
18746  allocator->GetImageMemoryRequirements(image, vkMemReq,
18747  requiresDedicatedAllocation, prefersDedicatedAllocation);
18748 
18749  VkResult result = allocator->AllocateMemory(
18750  vkMemReq,
18751  requiresDedicatedAllocation,
18752  prefersDedicatedAllocation,
18753  VK_NULL_HANDLE, // dedicatedBuffer
18754  UINT32_MAX, // dedicatedBufferUsage
18755  image, // dedicatedImage
18756  *pCreateInfo,
18757  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18758  1, // allocationCount
18759  pAllocation);
18760 
18761 #if VMA_RECORDING_ENABLED
18762  if(allocator->GetRecorder() != VMA_NULL)
18763  {
18764  allocator->GetRecorder()->RecordAllocateMemoryForImage(
18765  allocator->GetCurrentFrameIndex(),
18766  vkMemReq,
18767  requiresDedicatedAllocation,
18768  prefersDedicatedAllocation,
18769  *pCreateInfo,
18770  *pAllocation);
18771  }
18772 #endif
18773 
18774  if(pAllocationInfo && result == VK_SUCCESS)
18775  {
18776  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18777  }
18778 
18779  return result;
18780 }
18781 
18782 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18783  VmaAllocator allocator,
18784  VmaAllocation allocation)
18785 {
18786  VMA_ASSERT(allocator);
18787 
18788  if(allocation == VK_NULL_HANDLE)
18789  {
18790  return;
18791  }
18792 
18793  VMA_DEBUG_LOG("vmaFreeMemory");
18794 
18795  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18796 
18797 #if VMA_RECORDING_ENABLED
18798  if(allocator->GetRecorder() != VMA_NULL)
18799  {
18800  allocator->GetRecorder()->RecordFreeMemory(
18801  allocator->GetCurrentFrameIndex(),
18802  allocation);
18803  }
18804 #endif
18805 
18806  allocator->FreeMemory(
18807  1, // allocationCount
18808  &allocation);
18809 }
18810 
18811 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18812  VmaAllocator allocator,
18813  size_t allocationCount,
18814  const VmaAllocation* pAllocations)
18815 {
18816  if(allocationCount == 0)
18817  {
18818  return;
18819  }
18820 
18821  VMA_ASSERT(allocator);
18822 
18823  VMA_DEBUG_LOG("vmaFreeMemoryPages");
18824 
18825  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18826 
18827 #if VMA_RECORDING_ENABLED
18828  if(allocator->GetRecorder() != VMA_NULL)
18829  {
18830  allocator->GetRecorder()->RecordFreeMemoryPages(
18831  allocator->GetCurrentFrameIndex(),
18832  (uint64_t)allocationCount,
18833  pAllocations);
18834  }
18835 #endif
18836 
18837  allocator->FreeMemory(allocationCount, pAllocations);
18838 }
18839 
18840 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18841  VmaAllocator allocator,
18842  VmaAllocation allocation,
18843  VmaAllocationInfo* pAllocationInfo)
18844 {
18845  VMA_ASSERT(allocator && allocation && pAllocationInfo);
18846 
18847  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18848 
18849 #if VMA_RECORDING_ENABLED
18850  if(allocator->GetRecorder() != VMA_NULL)
18851  {
18852  allocator->GetRecorder()->RecordGetAllocationInfo(
18853  allocator->GetCurrentFrameIndex(),
18854  allocation);
18855  }
18856 #endif
18857 
18858  allocator->GetAllocationInfo(allocation, pAllocationInfo);
18859 }
18860 
18861 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18862  VmaAllocator allocator,
18863  VmaAllocation allocation)
18864 {
18865  VMA_ASSERT(allocator && allocation);
18866 
18867  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18868 
18869 #if VMA_RECORDING_ENABLED
18870  if(allocator->GetRecorder() != VMA_NULL)
18871  {
18872  allocator->GetRecorder()->RecordTouchAllocation(
18873  allocator->GetCurrentFrameIndex(),
18874  allocation);
18875  }
18876 #endif
18877 
18878  return allocator->TouchAllocation(allocation);
18879 }
18880 
18881 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18882  VmaAllocator allocator,
18883  VmaAllocation allocation,
18884  void* pUserData)
18885 {
18886  VMA_ASSERT(allocator && allocation);
18887 
18888  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18889 
18890  allocation->SetUserData(allocator, pUserData);
18891 
18892 #if VMA_RECORDING_ENABLED
18893  if(allocator->GetRecorder() != VMA_NULL)
18894  {
18895  allocator->GetRecorder()->RecordSetAllocationUserData(
18896  allocator->GetCurrentFrameIndex(),
18897  allocation,
18898  pUserData);
18899  }
18900 #endif
18901 }
18902 
18903 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18904  VmaAllocator allocator,
18905  VmaAllocation* pAllocation)
18906 {
18907  VMA_ASSERT(allocator && pAllocation);
18908 
18909  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18910 
18911  allocator->CreateLostAllocation(pAllocation);
18912 
18913 #if VMA_RECORDING_ENABLED
18914  if(allocator->GetRecorder() != VMA_NULL)
18915  {
18916  allocator->GetRecorder()->RecordCreateLostAllocation(
18917  allocator->GetCurrentFrameIndex(),
18918  *pAllocation);
18919  }
18920 #endif
18921 }
18922 
18923 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18924  VmaAllocator allocator,
18925  VmaAllocation allocation,
18926  void** ppData)
18927 {
18928  VMA_ASSERT(allocator && allocation && ppData);
18929 
18930  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18931 
18932  VkResult res = allocator->Map(allocation, ppData);
18933 
18934 #if VMA_RECORDING_ENABLED
18935  if(allocator->GetRecorder() != VMA_NULL)
18936  {
18937  allocator->GetRecorder()->RecordMapMemory(
18938  allocator->GetCurrentFrameIndex(),
18939  allocation);
18940  }
18941 #endif
18942 
18943  return res;
18944 }
18945 
18946 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18947  VmaAllocator allocator,
18948  VmaAllocation allocation)
18949 {
18950  VMA_ASSERT(allocator && allocation);
18951 
18952  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18953 
18954 #if VMA_RECORDING_ENABLED
18955  if(allocator->GetRecorder() != VMA_NULL)
18956  {
18957  allocator->GetRecorder()->RecordUnmapMemory(
18958  allocator->GetCurrentFrameIndex(),
18959  allocation);
18960  }
18961 #endif
18962 
18963  allocator->Unmap(allocation);
18964 }
18965 
18966 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18967 {
18968  VMA_ASSERT(allocator && allocation);
18969 
18970  VMA_DEBUG_LOG("vmaFlushAllocation");
18971 
18972  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18973 
18974  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18975 
18976 #if VMA_RECORDING_ENABLED
18977  if(allocator->GetRecorder() != VMA_NULL)
18978  {
18979  allocator->GetRecorder()->RecordFlushAllocation(
18980  allocator->GetCurrentFrameIndex(),
18981  allocation, offset, size);
18982  }
18983 #endif
18984 
18985  return res;
18986 }
18987 
18988 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18989 {
18990  VMA_ASSERT(allocator && allocation);
18991 
18992  VMA_DEBUG_LOG("vmaInvalidateAllocation");
18993 
18994  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18995 
18996  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18997 
18998 #if VMA_RECORDING_ENABLED
18999  if(allocator->GetRecorder() != VMA_NULL)
19000  {
19001  allocator->GetRecorder()->RecordInvalidateAllocation(
19002  allocator->GetCurrentFrameIndex(),
19003  allocation, offset, size);
19004  }
19005 #endif
19006 
19007  return res;
19008 }
19009 
19010 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
19011  VmaAllocator allocator,
19012  uint32_t allocationCount,
19013  const VmaAllocation* allocations,
19014  const VkDeviceSize* offsets,
19015  const VkDeviceSize* sizes)
19016 {
19017  VMA_ASSERT(allocator);
19018 
19019  if(allocationCount == 0)
19020  {
19021  return VK_SUCCESS;
19022  }
19023 
19024  VMA_ASSERT(allocations);
19025 
19026  VMA_DEBUG_LOG("vmaFlushAllocations");
19027 
19028  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19029 
19030  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
19031 
19032 #if VMA_RECORDING_ENABLED
19033  if(allocator->GetRecorder() != VMA_NULL)
19034  {
19035  //TODO
19036  }
19037 #endif
19038 
19039  return res;
19040 }
19041 
19042 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19043  VmaAllocator allocator,
19044  uint32_t allocationCount,
19045  const VmaAllocation* allocations,
19046  const VkDeviceSize* offsets,
19047  const VkDeviceSize* sizes)
19048 {
19049  VMA_ASSERT(allocator);
19050 
19051  if(allocationCount == 0)
19052  {
19053  return VK_SUCCESS;
19054  }
19055 
19056  VMA_ASSERT(allocations);
19057 
19058  VMA_DEBUG_LOG("vmaInvalidateAllocations");
19059 
19060  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19061 
19062  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19063 
19064 #if VMA_RECORDING_ENABLED
19065  if(allocator->GetRecorder() != VMA_NULL)
19066  {
19067  //TODO
19068  }
19069 #endif
19070 
19071  return res;
19072 }
19073 
19074 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19075 {
19076  VMA_ASSERT(allocator);
19077 
19078  VMA_DEBUG_LOG("vmaCheckCorruption");
19079 
19080  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19081 
19082  return allocator->CheckCorruption(memoryTypeBits);
19083 }
19084 
19085 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19086  VmaAllocator allocator,
19087  const VmaAllocation* pAllocations,
19088  size_t allocationCount,
19089  VkBool32* pAllocationsChanged,
19090  const VmaDefragmentationInfo *pDefragmentationInfo,
19091  VmaDefragmentationStats* pDefragmentationStats)
19092 {
19093  // Deprecated interface, reimplemented using new one.
19094 
19095  VmaDefragmentationInfo2 info2 = {};
19096  info2.allocationCount = (uint32_t)allocationCount;
19097  info2.pAllocations = pAllocations;
19098  info2.pAllocationsChanged = pAllocationsChanged;
19099  if(pDefragmentationInfo != VMA_NULL)
19100  {
19101  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19102  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19103  }
19104  else
19105  {
19106  info2.maxCpuAllocationsToMove = UINT32_MAX;
19107  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19108  }
19109  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19110 
19112  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19113  if(res == VK_NOT_READY)
19114  {
19115  res = vmaDefragmentationEnd( allocator, ctx);
19116  }
19117  return res;
19118 }
19119 
19120 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19121  VmaAllocator allocator,
19122  const VmaDefragmentationInfo2* pInfo,
19123  VmaDefragmentationStats* pStats,
19124  VmaDefragmentationContext *pContext)
19125 {
19126  VMA_ASSERT(allocator && pInfo && pContext);
19127 
19128  // Degenerate case: Nothing to defragment.
19129  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19130  {
19131  return VK_SUCCESS;
19132  }
19133 
19134  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19135  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19136  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19137  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19138 
19139  VMA_DEBUG_LOG("vmaDefragmentationBegin");
19140 
19141  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19142 
19143  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19144 
19145 #if VMA_RECORDING_ENABLED
19146  if(allocator->GetRecorder() != VMA_NULL)
19147  {
19148  allocator->GetRecorder()->RecordDefragmentationBegin(
19149  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19150  }
19151 #endif
19152 
19153  return res;
19154 }
19155 
19156 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19157  VmaAllocator allocator,
19158  VmaDefragmentationContext context)
19159 {
19160  VMA_ASSERT(allocator);
19161 
19162  VMA_DEBUG_LOG("vmaDefragmentationEnd");
19163 
19164  if(context != VK_NULL_HANDLE)
19165  {
19166  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19167 
19168 #if VMA_RECORDING_ENABLED
19169  if(allocator->GetRecorder() != VMA_NULL)
19170  {
19171  allocator->GetRecorder()->RecordDefragmentationEnd(
19172  allocator->GetCurrentFrameIndex(), context);
19173  }
19174 #endif
19175 
19176  return allocator->DefragmentationEnd(context);
19177  }
19178  else
19179  {
19180  return VK_SUCCESS;
19181  }
19182 }
19183 
19184 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19185  VmaAllocator allocator,
19186  VmaDefragmentationContext context,
19188  )
19189 {
19190  VMA_ASSERT(allocator);
19191  VMA_ASSERT(pInfo);
19192 
19193  VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19194 
19195  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19196 
19197  if(context == VK_NULL_HANDLE)
19198  {
19199  pInfo->moveCount = 0;
19200  return VK_SUCCESS;
19201  }
19202 
19203  return allocator->DefragmentationPassBegin(pInfo, context);
19204 }
19205 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19206  VmaAllocator allocator,
19207  VmaDefragmentationContext context)
19208 {
19209  VMA_ASSERT(allocator);
19210 
19211  VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19212  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19213 
19214  if(context == VK_NULL_HANDLE)
19215  return VK_SUCCESS;
19216 
19217  return allocator->DefragmentationPassEnd(context);
19218 }
19219 
19220 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19221  VmaAllocator allocator,
19222  VmaAllocation allocation,
19223  VkBuffer buffer)
19224 {
19225  VMA_ASSERT(allocator && allocation && buffer);
19226 
19227  VMA_DEBUG_LOG("vmaBindBufferMemory");
19228 
19229  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19230 
19231  return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19232 }
19233 
19234 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19235  VmaAllocator allocator,
19236  VmaAllocation allocation,
19237  VkDeviceSize allocationLocalOffset,
19238  VkBuffer buffer,
19239  const void* pNext)
19240 {
19241  VMA_ASSERT(allocator && allocation && buffer);
19242 
19243  VMA_DEBUG_LOG("vmaBindBufferMemory2");
19244 
19245  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19246 
19247  return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19248 }
19249 
19250 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19251  VmaAllocator allocator,
19252  VmaAllocation allocation,
19253  VkImage image)
19254 {
19255  VMA_ASSERT(allocator && allocation && image);
19256 
19257  VMA_DEBUG_LOG("vmaBindImageMemory");
19258 
19259  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19260 
19261  return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19262 }
19263 
19264 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19265  VmaAllocator allocator,
19266  VmaAllocation allocation,
19267  VkDeviceSize allocationLocalOffset,
19268  VkImage image,
19269  const void* pNext)
19270 {
19271  VMA_ASSERT(allocator && allocation && image);
19272 
19273  VMA_DEBUG_LOG("vmaBindImageMemory2");
19274 
19275  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19276 
19277  return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19278 }
19279 
19280 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19281  VmaAllocator allocator,
19282  const VkBufferCreateInfo* pBufferCreateInfo,
19283  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19284  VkBuffer* pBuffer,
19285  VmaAllocation* pAllocation,
19286  VmaAllocationInfo* pAllocationInfo)
19287 {
19288  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19289 
19290  if(pBufferCreateInfo->size == 0)
19291  {
19292  return VK_ERROR_VALIDATION_FAILED_EXT;
19293  }
19294  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19295  !allocator->m_UseKhrBufferDeviceAddress)
19296  {
19297  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.");
19298  return VK_ERROR_VALIDATION_FAILED_EXT;
19299  }
19300 
19301  VMA_DEBUG_LOG("vmaCreateBuffer");
19302 
19303  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19304 
19305  *pBuffer = VK_NULL_HANDLE;
19306  *pAllocation = VK_NULL_HANDLE;
19307 
19308  // 1. Create VkBuffer.
19309  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19310  allocator->m_hDevice,
19311  pBufferCreateInfo,
19312  allocator->GetAllocationCallbacks(),
19313  pBuffer);
19314  if(res >= 0)
19315  {
19316  // 2. vkGetBufferMemoryRequirements.
19317  VkMemoryRequirements vkMemReq = {};
19318  bool requiresDedicatedAllocation = false;
19319  bool prefersDedicatedAllocation = false;
19320  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19321  requiresDedicatedAllocation, prefersDedicatedAllocation);
19322 
19323  // 3. Allocate memory using allocator.
19324  res = allocator->AllocateMemory(
19325  vkMemReq,
19326  requiresDedicatedAllocation,
19327  prefersDedicatedAllocation,
19328  *pBuffer, // dedicatedBuffer
19329  pBufferCreateInfo->usage, // dedicatedBufferUsage
19330  VK_NULL_HANDLE, // dedicatedImage
19331  *pAllocationCreateInfo,
19332  VMA_SUBALLOCATION_TYPE_BUFFER,
19333  1, // allocationCount
19334  pAllocation);
19335 
19336 #if VMA_RECORDING_ENABLED
19337  if(allocator->GetRecorder() != VMA_NULL)
19338  {
19339  allocator->GetRecorder()->RecordCreateBuffer(
19340  allocator->GetCurrentFrameIndex(),
19341  *pBufferCreateInfo,
19342  *pAllocationCreateInfo,
19343  *pAllocation);
19344  }
19345 #endif
19346 
19347  if(res >= 0)
19348  {
19349  // 3. Bind buffer with memory.
19350  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19351  {
19352  res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19353  }
19354  if(res >= 0)
19355  {
19356  // All steps succeeded.
19357  #if VMA_STATS_STRING_ENABLED
19358  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19359  #endif
19360  if(pAllocationInfo != VMA_NULL)
19361  {
19362  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19363  }
19364 
19365  return VK_SUCCESS;
19366  }
19367  allocator->FreeMemory(
19368  1, // allocationCount
19369  pAllocation);
19370  *pAllocation = VK_NULL_HANDLE;
19371  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19372  *pBuffer = VK_NULL_HANDLE;
19373  return res;
19374  }
19375  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19376  *pBuffer = VK_NULL_HANDLE;
19377  return res;
19378  }
19379  return res;
19380 }
19381 
19382 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19383  VmaAllocator allocator,
19384  VkBuffer buffer,
19385  VmaAllocation allocation)
19386 {
19387  VMA_ASSERT(allocator);
19388 
19389  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19390  {
19391  return;
19392  }
19393 
19394  VMA_DEBUG_LOG("vmaDestroyBuffer");
19395 
19396  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19397 
19398 #if VMA_RECORDING_ENABLED
19399  if(allocator->GetRecorder() != VMA_NULL)
19400  {
19401  allocator->GetRecorder()->RecordDestroyBuffer(
19402  allocator->GetCurrentFrameIndex(),
19403  allocation);
19404  }
19405 #endif
19406 
19407  if(buffer != VK_NULL_HANDLE)
19408  {
19409  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19410  }
19411 
19412  if(allocation != VK_NULL_HANDLE)
19413  {
19414  allocator->FreeMemory(
19415  1, // allocationCount
19416  &allocation);
19417  }
19418 }
19419 
19420 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19421  VmaAllocator allocator,
19422  const VkImageCreateInfo* pImageCreateInfo,
19423  const VmaAllocationCreateInfo* pAllocationCreateInfo,
19424  VkImage* pImage,
19425  VmaAllocation* pAllocation,
19426  VmaAllocationInfo* pAllocationInfo)
19427 {
19428  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19429 
19430  if(pImageCreateInfo->extent.width == 0 ||
19431  pImageCreateInfo->extent.height == 0 ||
19432  pImageCreateInfo->extent.depth == 0 ||
19433  pImageCreateInfo->mipLevels == 0 ||
19434  pImageCreateInfo->arrayLayers == 0)
19435  {
19436  return VK_ERROR_VALIDATION_FAILED_EXT;
19437  }
19438 
19439  VMA_DEBUG_LOG("vmaCreateImage");
19440 
19441  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19442 
19443  *pImage = VK_NULL_HANDLE;
19444  *pAllocation = VK_NULL_HANDLE;
19445 
19446  // 1. Create VkImage.
19447  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19448  allocator->m_hDevice,
19449  pImageCreateInfo,
19450  allocator->GetAllocationCallbacks(),
19451  pImage);
19452  if(res >= 0)
19453  {
19454  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19455  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19456  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19457 
19458  // 2. Allocate memory using allocator.
19459  VkMemoryRequirements vkMemReq = {};
19460  bool requiresDedicatedAllocation = false;
19461  bool prefersDedicatedAllocation = false;
19462  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19463  requiresDedicatedAllocation, prefersDedicatedAllocation);
19464 
19465  res = allocator->AllocateMemory(
19466  vkMemReq,
19467  requiresDedicatedAllocation,
19468  prefersDedicatedAllocation,
19469  VK_NULL_HANDLE, // dedicatedBuffer
19470  UINT32_MAX, // dedicatedBufferUsage
19471  *pImage, // dedicatedImage
19472  *pAllocationCreateInfo,
19473  suballocType,
19474  1, // allocationCount
19475  pAllocation);
19476 
19477 #if VMA_RECORDING_ENABLED
19478  if(allocator->GetRecorder() != VMA_NULL)
19479  {
19480  allocator->GetRecorder()->RecordCreateImage(
19481  allocator->GetCurrentFrameIndex(),
19482  *pImageCreateInfo,
19483  *pAllocationCreateInfo,
19484  *pAllocation);
19485  }
19486 #endif
19487 
19488  if(res >= 0)
19489  {
19490  // 3. Bind image with memory.
19491  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19492  {
19493  res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19494  }
19495  if(res >= 0)
19496  {
19497  // All steps succeeded.
19498  #if VMA_STATS_STRING_ENABLED
19499  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19500  #endif
19501  if(pAllocationInfo != VMA_NULL)
19502  {
19503  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19504  }
19505 
19506  return VK_SUCCESS;
19507  }
19508  allocator->FreeMemory(
19509  1, // allocationCount
19510  pAllocation);
19511  *pAllocation = VK_NULL_HANDLE;
19512  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19513  *pImage = VK_NULL_HANDLE;
19514  return res;
19515  }
19516  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19517  *pImage = VK_NULL_HANDLE;
19518  return res;
19519  }
19520  return res;
19521 }
19522 
19523 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19524  VmaAllocator allocator,
19525  VkImage image,
19526  VmaAllocation allocation)
19527 {
19528  VMA_ASSERT(allocator);
19529 
19530  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19531  {
19532  return;
19533  }
19534 
19535  VMA_DEBUG_LOG("vmaDestroyImage");
19536 
19537  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19538 
19539 #if VMA_RECORDING_ENABLED
19540  if(allocator->GetRecorder() != VMA_NULL)
19541  {
19542  allocator->GetRecorder()->RecordDestroyImage(
19543  allocator->GetCurrentFrameIndex(),
19544  allocation);
19545  }
19546 #endif
19547 
19548  if(image != VK_NULL_HANDLE)
19549  {
19550  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19551  }
19552  if(allocation != VK_NULL_HANDLE)
19553  {
19554  allocator->FreeMemory(
19555  1, // allocationCount
19556  &allocation);
19557  }
19558 }
19559 
19560 #endif // #ifdef VMA_IMPLEMENTATION
Definition: vk_mem_alloc.h:2881
uint32_t memoryTypeBits
Bitmask containing one bit set for every memory type acceptable for this allocation.
Definition: vk_mem_alloc.h:2907
VmaPool pool
Pool that this allocation should be created in.
Definition: vk_mem_alloc.h:2913
VkMemoryPropertyFlags preferredFlags
Flags that preferably should be set in a memory type chosen for an allocation.
Definition: vk_mem_alloc.h:2899
void * pUserData
Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
Definition: vk_mem_alloc.h:2920
VkMemoryPropertyFlags requiredFlags
Flags that must be set in a Memory Type chosen for an allocation.
Definition: vk_mem_alloc.h:2894
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:2927
VmaMemoryUsage usage
Intended usage of memory.
Definition: vk_mem_alloc.h:2889
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2883
Represents single memory allocation.
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
Definition: vk_mem_alloc.h:3231
VkDeviceSize offset
Offset in VkDeviceMemory object to the beginning of this allocation, in bytes. (deviceMemory,...
Definition: vk_mem_alloc.h:3255
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:3275
uint32_t memoryType
Memory type index that this allocation was allocated from.
Definition: vk_mem_alloc.h:3236
VkDeviceSize size
Size of this allocation, in bytes.
Definition: vk_mem_alloc.h:3266
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:3280
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:3245
Description of a Allocator to be created.
Definition: vk_mem_alloc.h:2415
VkPhysicalDevice physicalDevice
Vulkan physical device.
Definition: vk_mem_alloc.h:2420
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:2446
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:2471
VmaAllocatorCreateFlags flags
Flags for created allocator. Use VmaAllocatorCreateFlagBits enum.
Definition: vk_mem_alloc.h:2417
const VmaVulkanFunctions * pVulkanFunctions
Pointers to Vulkan functions. Can be null.
Definition: vk_mem_alloc.h:2477
const VkAllocationCallbacks * pAllocationCallbacks
Custom CPU memory allocation callbacks. Optional.
Definition: vk_mem_alloc.h:2429
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2489
VkDeviceSize preferredLargeHeapBlockSize
Preferred size of a single VkDeviceMemory block to be allocated from large heaps > 1 GiB....
Definition: vk_mem_alloc.h:2426
const VmaRecordSettings * pRecordSettings
Parameters for recording of VMA calls. Can be null.
Definition: vk_mem_alloc.h:2484
VkDevice device
Vulkan device.
Definition: vk_mem_alloc.h:2423
uint32_t vulkanApiVersion
Optional. The highest version of Vulkan that the application is designed to use.
Definition: vk_mem_alloc.h:2498
const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
Informative callbacks for vkAllocateMemory, vkFreeMemory. Optional.
Definition: vk_mem_alloc.h:2432
Represents main object of this library initialized.
Information about existing VmaAllocator object.
Definition: vk_mem_alloc.h:2513
VkDevice device
Handle to Vulkan device object.
Definition: vk_mem_alloc.h:2528
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2518
VkPhysicalDevice physicalDevice
Handle to Vulkan physical device object.
Definition: vk_mem_alloc.h:2523
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
Definition: vk_mem_alloc.h:2619
VkDeviceSize blockBytes
Sum size of all VkDeviceMemory blocks allocated from particular heap, in bytes.
Definition: vk_mem_alloc.h:2622
VkDeviceSize allocationBytes
Sum size of all allocations created in particular heap, in bytes.
Definition: vk_mem_alloc.h:2633
VkDeviceSize usage
Estimated current memory usage of the program, in bytes.
Definition: vk_mem_alloc.h:2643
VkDeviceSize budget
Estimated amount of memory available to the program, in bytes.
Definition: vk_mem_alloc.h:2654
Represents Opaque object that represents started defragmentation process.
Parameters for defragmentation.
Definition: vk_mem_alloc.h:3630
const VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:3670
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:3636
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:3690
VkDeviceSize maxGpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3685
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:3633
VkBool32 * pAllocationsChanged
Optional, output. Pointer to array that will be filled with information whether the allocation at cer...
Definition: vk_mem_alloc.h:3651
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:3654
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:3699
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:3680
const VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:3645
VkDeviceSize maxCpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:3675
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:3721
uint32_t maxAllocationsToMove
Maximum number of allocations that can be moved to different place.
Definition: vk_mem_alloc.h:3731
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3726
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:3712
uint32_t moveCount
Definition: vk_mem_alloc.h:3713
VmaDefragmentationPassMoveInfo * pMoves
Definition: vk_mem_alloc.h:3714
Definition: vk_mem_alloc.h:3702
VkDeviceMemory memory
Definition: vk_mem_alloc.h:3704
VkDeviceSize offset
Definition: vk_mem_alloc.h:3705
VmaAllocation allocation
Definition: vk_mem_alloc.h:3703
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:3735
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:3743
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3737
VkDeviceSize bytesFreed
Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects.
Definition: vk_mem_alloc.h:3739
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:3741
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
Definition: vk_mem_alloc.h:2224
void * pUserData
Optional, can be null.
Definition: vk_mem_alloc.h:2230
PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
Optional, can be null.
Definition: vk_mem_alloc.h:2226
PFN_vmaFreeDeviceMemoryFunction pfnFree
Optional, can be null.
Definition: vk_mem_alloc.h:2228
Describes parameter of created VmaPool.
Definition: vk_mem_alloc.h:3049
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:3097
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition: vk_mem_alloc.h:3052
VmaPoolCreateFlags flags
Use combination of VmaPoolCreateFlagBits.
Definition: vk_mem_alloc.h:3055
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:3091
VkDeviceSize blockSize
Size of a single VkDeviceMemory block to be allocated as part of this pool, in bytes....
Definition: vk_mem_alloc.h:3064
size_t minBlockCount
Minimum number of blocks to be always allocated in this pool, even if they stay empty.
Definition: vk_mem_alloc.h:3069
size_t maxBlockCount
Maximum number of blocks that can be allocated in this pool. Optional.
Definition: vk_mem_alloc.h:3077
Represents custom memory pool.
Describes parameter of existing VmaPool.
Definition: vk_mem_alloc.h:3102
VkDeviceSize size
Total amount of VkDeviceMemory allocated from Vulkan for this pool, in bytes.
Definition: vk_mem_alloc.h:3105
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:3124
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:3121
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost.
Definition: vk_mem_alloc.h:3111
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3108
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:3114
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
Definition: vk_mem_alloc.h:2400
const char * pFilePath
Path to the file that should be written by the recording.
Definition: vk_mem_alloc.h:2410
VmaRecordFlags flags
Flags for recording. Use VmaRecordFlagBits enum.
Definition: vk_mem_alloc.h:2402
Calculated statistics of memory usage in entire allocator.
Definition: vk_mem_alloc.h:2580
VkDeviceSize allocationSizeAvg
Definition: vk_mem_alloc.h:2591
VkDeviceSize allocationSizeMax
Definition: vk_mem_alloc.h:2591
VkDeviceSize unusedBytes
Total number of bytes occupied by unused ranges.
Definition: vk_mem_alloc.h:2590
VkDeviceSize unusedRangeSizeAvg
Definition: vk_mem_alloc.h:2592
uint32_t allocationCount
Number of VmaAllocation allocation objects allocated.
Definition: vk_mem_alloc.h:2584
VkDeviceSize unusedRangeSizeMax
Definition: vk_mem_alloc.h:2592
VkDeviceSize usedBytes
Total number of bytes occupied by all allocations.
Definition: vk_mem_alloc.h:2588
uint32_t blockCount
Number of VkDeviceMemory Vulkan memory blocks allocated.
Definition: vk_mem_alloc.h:2582
VkDeviceSize allocationSizeMin
Definition: vk_mem_alloc.h:2591
uint32_t unusedRangeCount
Number of free ranges of memory between allocations.
Definition: vk_mem_alloc.h:2586
VkDeviceSize unusedRangeSizeMin
Definition: vk_mem_alloc.h:2592
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:2597
VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
Definition: vk_mem_alloc.h:2599
VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
Definition: vk_mem_alloc.h:2598
VmaStatInfo total
Definition: vk_mem_alloc.h:2600
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:2354
PFN_vkBindImageMemory vkBindImageMemory
Definition: vk_mem_alloc.h:2364
PFN_vkCreateImage vkCreateImage
Definition: vk_mem_alloc.h:2369
PFN_vkAllocateMemory vkAllocateMemory
Definition: vk_mem_alloc.h:2357
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges
Definition: vk_mem_alloc.h:2361
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements
Definition: vk_mem_alloc.h:2366
PFN_vkFreeMemory vkFreeMemory
Definition: vk_mem_alloc.h:2358
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements
Definition: vk_mem_alloc.h:2365
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges
Definition: vk_mem_alloc.h:2362
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties
Definition: vk_mem_alloc.h:2356
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties
Definition: vk_mem_alloc.h:2355
PFN_vkDestroyBuffer vkDestroyBuffer
Definition: vk_mem_alloc.h:2368
PFN_vkDestroyImage vkDestroyImage
Definition: vk_mem_alloc.h:2370
PFN_vkBindBufferMemory vkBindBufferMemory
Definition: vk_mem_alloc.h:2363
PFN_vkMapMemory vkMapMemory
Definition: vk_mem_alloc.h:2359
PFN_vkUnmapMemory vkUnmapMemory
Definition: vk_mem_alloc.h:2360
PFN_vkCmdCopyBuffer vkCmdCopyBuffer
Definition: vk_mem_alloc.h:2371
PFN_vkCreateBuffer vkCreateBuffer
Definition: vk_mem_alloc.h:2367
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:2210
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:2031
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:3045
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:2386
@ VMA_RECORD_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2394
@ VMA_RECORD_FLUSH_AFTER_CALL_BIT
Enables flush after recording every function call.
Definition: vk_mem_alloc.h:2392
VmaAllocatorCreateFlagBits
Flags for created VmaAllocator.
Definition: vk_mem_alloc.h:2234
@ VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
Definition: vk_mem_alloc.h:2309
@ 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:2239
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
Definition: vk_mem_alloc.h:2291
@ VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
Definition: vk_mem_alloc.h:2327
@ VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
Definition: vk_mem_alloc.h:2279
@ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
Enables usage of VK_KHR_dedicated_allocation extension.
Definition: vk_mem_alloc.h:2264
@ VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2346
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
Definition: vk_mem_alloc.h:2344
VkFlags VmaAllocationCreateFlags
Definition: vk_mem_alloc.h:2878
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:3620
@ VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
Definition: vk_mem_alloc.h:3621
@ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3622
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:2203
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:3624
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:2989
@ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
Enables alternative, linear allocation algorithm in this pool.
Definition: vk_mem_alloc.h:3024
@ VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3043
@ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
Enables alternative, buddy allocation algorithm in this pool.
Definition: vk_mem_alloc.h:3035
@ 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:3007
@ VMA_POOL_CREATE_ALGORITHM_MASK
Definition: vk_mem_alloc.h:3039
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:2702
@ VMA_MEMORY_USAGE_MAX_ENUM
Definition: vk_mem_alloc.h:2765
@ VMA_MEMORY_USAGE_CPU_ONLY
Definition: vk_mem_alloc.h:2733
@ VMA_MEMORY_USAGE_CPU_COPY
Definition: vk_mem_alloc.h:2755
@ VMA_MEMORY_USAGE_GPU_TO_CPU
Definition: vk_mem_alloc.h:2749
@ VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
Definition: vk_mem_alloc.h:2763
@ VMA_MEMORY_USAGE_CPU_TO_GPU
Definition: vk_mem_alloc.h:2740
@ VMA_MEMORY_USAGE_GPU_ONLY
Definition: vk_mem_alloc.h:2723
@ VMA_MEMORY_USAGE_UNKNOWN
Definition: vk_mem_alloc.h:2706
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:2348
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:2769
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
Definition: vk_mem_alloc.h:2864
@ 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:2800
@ VMA_ALLOCATION_CREATE_DONT_BIND_BIT
Definition: vk_mem_alloc.h:2837
@ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
Definition: vk_mem_alloc.h:2857
@ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2776
@ VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
Definition: vk_mem_alloc.h:2831
@ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
Definition: vk_mem_alloc.h:2813
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
Definition: vk_mem_alloc.h:2867
@ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
Definition: vk_mem_alloc.h:2820
@ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
Definition: vk_mem_alloc.h:2846
@ 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:2787
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
Definition: vk_mem_alloc.h:2861
@ VMA_ALLOCATION_CREATE_STRATEGY_MASK
Definition: vk_mem_alloc.h:2871
@ VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
Definition: vk_mem_alloc.h:2826
@ VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
Definition: vk_mem_alloc.h:2841
@ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
Definition: vk_mem_alloc.h:2850
@ VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2876
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:2396
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.