Vulkan Memory Allocator
vk_mem_alloc.h
Go to the documentation of this file.
1 //
2 // Copyright (c) 2017-2018 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 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
1635 /*
1636 Define this macro to 0/1 to disable/enable support for recording functionality,
1637 available through VmaAllocatorCreateInfo::pRecordSettings.
1638 */
1639 #ifndef VMA_RECORDING_ENABLED
1640  #ifdef _WIN32
1641  #define VMA_RECORDING_ENABLED 1
1642  #else
1643  #define VMA_RECORDING_ENABLED 0
1644  #endif
1645 #endif
1646 
1647 #ifndef NOMINMAX
1648  #define NOMINMAX // For windows.h
1649 #endif
1650 
1651 #ifndef VULKAN_H_
1652  #include <vulkan/vulkan.h>
1653 #endif
1654 
1655 #if VMA_RECORDING_ENABLED
1656  #include <windows.h>
1657 #endif
1658 
1659 #if !defined(VMA_DEDICATED_ALLOCATION)
1660  #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1661  #define VMA_DEDICATED_ALLOCATION 1
1662  #else
1663  #define VMA_DEDICATED_ALLOCATION 0
1664  #endif
1665 #endif
1666 
1676 VK_DEFINE_HANDLE(VmaAllocator)
1677 
1678 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1680  VmaAllocator allocator,
1681  uint32_t memoryType,
1682  VkDeviceMemory memory,
1683  VkDeviceSize size);
1685 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1686  VmaAllocator allocator,
1687  uint32_t memoryType,
1688  VkDeviceMemory memory,
1689  VkDeviceSize size);
1690 
1704 
1734 
1737 typedef VkFlags VmaAllocatorCreateFlags;
1738 
1743 typedef struct VmaVulkanFunctions {
1744  PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1745  PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1746  PFN_vkAllocateMemory vkAllocateMemory;
1747  PFN_vkFreeMemory vkFreeMemory;
1748  PFN_vkMapMemory vkMapMemory;
1749  PFN_vkUnmapMemory vkUnmapMemory;
1750  PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1751  PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1752  PFN_vkBindBufferMemory vkBindBufferMemory;
1753  PFN_vkBindImageMemory vkBindImageMemory;
1754  PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1755  PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1756  PFN_vkCreateBuffer vkCreateBuffer;
1757  PFN_vkDestroyBuffer vkDestroyBuffer;
1758  PFN_vkCreateImage vkCreateImage;
1759  PFN_vkDestroyImage vkDestroyImage;
1760  PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1761 #if VMA_DEDICATED_ALLOCATION
1762  PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1763  PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1764 #endif
1766 
1768 typedef enum VmaRecordFlagBits {
1775 
1778 typedef VkFlags VmaRecordFlags;
1779 
1781 typedef struct VmaRecordSettings
1782 {
1792  const char* pFilePath;
1794 
1797 {
1801 
1802  VkPhysicalDevice physicalDevice;
1804 
1805  VkDevice device;
1807 
1810 
1811  const VkAllocationCallbacks* pAllocationCallbacks;
1813 
1853  const VkDeviceSize* pHeapSizeLimit;
1874 
1876 VkResult vmaCreateAllocator(
1877  const VmaAllocatorCreateInfo* pCreateInfo,
1878  VmaAllocator* pAllocator);
1879 
1881 void vmaDestroyAllocator(
1882  VmaAllocator allocator);
1883 
1889  VmaAllocator allocator,
1890  const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1891 
1897  VmaAllocator allocator,
1898  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1899 
1907  VmaAllocator allocator,
1908  uint32_t memoryTypeIndex,
1909  VkMemoryPropertyFlags* pFlags);
1910 
1920  VmaAllocator allocator,
1921  uint32_t frameIndex);
1922 
1925 typedef struct VmaStatInfo
1926 {
1928  uint32_t blockCount;
1934  VkDeviceSize usedBytes;
1936  VkDeviceSize unusedBytes;
1939 } VmaStatInfo;
1940 
1942 typedef struct VmaStats
1943 {
1944  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1945  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1947 } VmaStats;
1948 
1950 void vmaCalculateStats(
1951  VmaAllocator allocator,
1952  VmaStats* pStats);
1953 
1954 #define VMA_STATS_STRING_ENABLED 1
1955 
1956 #if VMA_STATS_STRING_ENABLED
1957 
1959 
1961 void vmaBuildStatsString(
1962  VmaAllocator allocator,
1963  char** ppStatsString,
1964  VkBool32 detailedMap);
1965 
1966 void vmaFreeStatsString(
1967  VmaAllocator allocator,
1968  char* pStatsString);
1969 
1970 #endif // #if VMA_STATS_STRING_ENABLED
1971 
1980 VK_DEFINE_HANDLE(VmaPool)
1981 
1982 typedef enum VmaMemoryUsage
1983 {
2032 } VmaMemoryUsage;
2033 
2043 
2098 
2114 
2124 
2131 
2135 
2137 {
2150  VkMemoryPropertyFlags requiredFlags;
2155  VkMemoryPropertyFlags preferredFlags;
2163  uint32_t memoryTypeBits;
2176  void* pUserData;
2178 
2195 VkResult vmaFindMemoryTypeIndex(
2196  VmaAllocator allocator,
2197  uint32_t memoryTypeBits,
2198  const VmaAllocationCreateInfo* pAllocationCreateInfo,
2199  uint32_t* pMemoryTypeIndex);
2200 
2214  VmaAllocator allocator,
2215  const VkBufferCreateInfo* pBufferCreateInfo,
2216  const VmaAllocationCreateInfo* pAllocationCreateInfo,
2217  uint32_t* pMemoryTypeIndex);
2218 
2232  VmaAllocator allocator,
2233  const VkImageCreateInfo* pImageCreateInfo,
2234  const VmaAllocationCreateInfo* pAllocationCreateInfo,
2235  uint32_t* pMemoryTypeIndex);
2236 
2257 
2274 
2285 
2291 
2294 typedef VkFlags VmaPoolCreateFlags;
2295 
2298 typedef struct VmaPoolCreateInfo {
2313  VkDeviceSize blockSize;
2342 
2345 typedef struct VmaPoolStats {
2348  VkDeviceSize size;
2351  VkDeviceSize unusedSize;
2364  VkDeviceSize unusedRangeSizeMax;
2367  size_t blockCount;
2368 } VmaPoolStats;
2369 
2376 VkResult vmaCreatePool(
2377  VmaAllocator allocator,
2378  const VmaPoolCreateInfo* pCreateInfo,
2379  VmaPool* pPool);
2380 
2383 void vmaDestroyPool(
2384  VmaAllocator allocator,
2385  VmaPool pool);
2386 
2393 void vmaGetPoolStats(
2394  VmaAllocator allocator,
2395  VmaPool pool,
2396  VmaPoolStats* pPoolStats);
2397 
2405  VmaAllocator allocator,
2406  VmaPool pool,
2407  size_t* pLostAllocationCount);
2408 
2423 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2424 
2449 VK_DEFINE_HANDLE(VmaAllocation)
2450 
2451 
2453 typedef struct VmaAllocationInfo {
2458  uint32_t memoryType;
2467  VkDeviceMemory deviceMemory;
2472  VkDeviceSize offset;
2477  VkDeviceSize size;
2491  void* pUserData;
2493 
2504 VkResult vmaAllocateMemory(
2505  VmaAllocator allocator,
2506  const VkMemoryRequirements* pVkMemoryRequirements,
2507  const VmaAllocationCreateInfo* pCreateInfo,
2508  VmaAllocation* pAllocation,
2509  VmaAllocationInfo* pAllocationInfo);
2510 
2530 VkResult vmaAllocateMemoryPages(
2531  VmaAllocator allocator,
2532  const VkMemoryRequirements* pVkMemoryRequirements,
2533  const VmaAllocationCreateInfo* pCreateInfo,
2534  size_t allocationCount,
2535  VmaAllocation* pAllocations,
2536  VmaAllocationInfo* pAllocationInfo);
2537 
2545  VmaAllocator allocator,
2546  VkBuffer buffer,
2547  const VmaAllocationCreateInfo* pCreateInfo,
2548  VmaAllocation* pAllocation,
2549  VmaAllocationInfo* pAllocationInfo);
2550 
2552 VkResult vmaAllocateMemoryForImage(
2553  VmaAllocator allocator,
2554  VkImage image,
2555  const VmaAllocationCreateInfo* pCreateInfo,
2556  VmaAllocation* pAllocation,
2557  VmaAllocationInfo* pAllocationInfo);
2558 
2563 void vmaFreeMemory(
2564  VmaAllocator allocator,
2565  VmaAllocation allocation);
2566 
2577 void vmaFreeMemoryPages(
2578  VmaAllocator allocator,
2579  size_t allocationCount,
2580  VmaAllocation* pAllocations);
2581 
2602 VkResult vmaResizeAllocation(
2603  VmaAllocator allocator,
2604  VmaAllocation allocation,
2605  VkDeviceSize newSize);
2606 
2624  VmaAllocator allocator,
2625  VmaAllocation allocation,
2626  VmaAllocationInfo* pAllocationInfo);
2627 
2642 VkBool32 vmaTouchAllocation(
2643  VmaAllocator allocator,
2644  VmaAllocation allocation);
2645 
2660  VmaAllocator allocator,
2661  VmaAllocation allocation,
2662  void* pUserData);
2663 
2675  VmaAllocator allocator,
2676  VmaAllocation* pAllocation);
2677 
2712 VkResult vmaMapMemory(
2713  VmaAllocator allocator,
2714  VmaAllocation allocation,
2715  void** ppData);
2716 
2721 void vmaUnmapMemory(
2722  VmaAllocator allocator,
2723  VmaAllocation allocation);
2724 
2737 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2738 
2751 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2752 
2769 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2770 
2777 VK_DEFINE_HANDLE(VmaDefragmentationContext)
2778 
2779 typedef enum VmaDefragmentationFlagBits {
2783 typedef VkFlags VmaDefragmentationFlags;
2784 
2789 typedef struct VmaDefragmentationInfo2 {
2813  uint32_t poolCount;
2834  VkDeviceSize maxCpuBytesToMove;
2844  VkDeviceSize maxGpuBytesToMove;
2858  VkCommandBuffer commandBuffer;
2860 
2865 typedef struct VmaDefragmentationInfo {
2870  VkDeviceSize maxBytesToMove;
2877 
2879 typedef struct VmaDefragmentationStats {
2881  VkDeviceSize bytesMoved;
2883  VkDeviceSize bytesFreed;
2889 
2916 VkResult vmaDefragmentationBegin(
2917  VmaAllocator allocator,
2918  const VmaDefragmentationInfo2* pInfo,
2919  VmaDefragmentationStats* pStats,
2920  VmaDefragmentationContext *pContext);
2921 
2927 VkResult vmaDefragmentationEnd(
2928  VmaAllocator allocator,
2929  VmaDefragmentationContext context);
2930 
2971 VkResult vmaDefragment(
2972  VmaAllocator allocator,
2973  VmaAllocation* pAllocations,
2974  size_t allocationCount,
2975  VkBool32* pAllocationsChanged,
2976  const VmaDefragmentationInfo *pDefragmentationInfo,
2977  VmaDefragmentationStats* pDefragmentationStats);
2978 
2991 VkResult vmaBindBufferMemory(
2992  VmaAllocator allocator,
2993  VmaAllocation allocation,
2994  VkBuffer buffer);
2995 
3008 VkResult vmaBindImageMemory(
3009  VmaAllocator allocator,
3010  VmaAllocation allocation,
3011  VkImage image);
3012 
3039 VkResult vmaCreateBuffer(
3040  VmaAllocator allocator,
3041  const VkBufferCreateInfo* pBufferCreateInfo,
3042  const VmaAllocationCreateInfo* pAllocationCreateInfo,
3043  VkBuffer* pBuffer,
3044  VmaAllocation* pAllocation,
3045  VmaAllocationInfo* pAllocationInfo);
3046 
3058 void vmaDestroyBuffer(
3059  VmaAllocator allocator,
3060  VkBuffer buffer,
3061  VmaAllocation allocation);
3062 
3064 VkResult vmaCreateImage(
3065  VmaAllocator allocator,
3066  const VkImageCreateInfo* pImageCreateInfo,
3067  const VmaAllocationCreateInfo* pAllocationCreateInfo,
3068  VkImage* pImage,
3069  VmaAllocation* pAllocation,
3070  VmaAllocationInfo* pAllocationInfo);
3071 
3083 void vmaDestroyImage(
3084  VmaAllocator allocator,
3085  VkImage image,
3086  VmaAllocation allocation);
3087 
3088 #ifdef __cplusplus
3089 }
3090 #endif
3091 
3092 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3093 
3094 // For Visual Studio IntelliSense.
3095 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3096 #define VMA_IMPLEMENTATION
3097 #endif
3098 
3099 #ifdef VMA_IMPLEMENTATION
3100 #undef VMA_IMPLEMENTATION
3101 
3102 #include <cstdint>
3103 #include <cstdlib>
3104 #include <cstring>
3105 
3106 /*******************************************************************************
3107 CONFIGURATION SECTION
3108 
3109 Define some of these macros before each #include of this header or change them
3110 here if you need other then default behavior depending on your environment.
3111 */
3112 
3113 /*
3114 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3115 internally, like:
3116 
3117  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3118 
3119 Define to 0 if you are going to provide you own pointers to Vulkan functions via
3120 VmaAllocatorCreateInfo::pVulkanFunctions.
3121 */
3122 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3123 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3124 #endif
3125 
3126 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3127 //#define VMA_USE_STL_CONTAINERS 1
3128 
3129 /* Set this macro to 1 to make the library including and using STL containers:
3130 std::pair, std::vector, std::list, std::unordered_map.
3131 
3132 Set it to 0 or undefined to make the library using its own implementation of
3133 the containers.
3134 */
3135 #if VMA_USE_STL_CONTAINERS
3136  #define VMA_USE_STL_VECTOR 1
3137  #define VMA_USE_STL_UNORDERED_MAP 1
3138  #define VMA_USE_STL_LIST 1
3139 #endif
3140 
3141 #ifndef VMA_USE_STL_SHARED_MUTEX
3142  // Minimum Visual Studio 2015 Update 2
3143  #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3144  #define VMA_USE_STL_SHARED_MUTEX 1
3145  #endif
3146 #endif
3147 
3148 #if VMA_USE_STL_VECTOR
3149  #include <vector>
3150 #endif
3151 
3152 #if VMA_USE_STL_UNORDERED_MAP
3153  #include <unordered_map>
3154 #endif
3155 
3156 #if VMA_USE_STL_LIST
3157  #include <list>
3158 #endif
3159 
3160 /*
3161 Following headers are used in this CONFIGURATION section only, so feel free to
3162 remove them if not needed.
3163 */
3164 #include <cassert> // for assert
3165 #include <algorithm> // for min, max
3166 #include <mutex>
3167 #include <atomic> // for std::atomic
3168 
3169 #ifndef VMA_NULL
3170  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3171  #define VMA_NULL nullptr
3172 #endif
3173 
3174 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3175 #include <cstdlib>
3176 void *aligned_alloc(size_t alignment, size_t size)
3177 {
3178  // alignment must be >= sizeof(void*)
3179  if(alignment < sizeof(void*))
3180  {
3181  alignment = sizeof(void*);
3182  }
3183 
3184  return memalign(alignment, size);
3185 }
3186 #elif defined(__APPLE__) || defined(__ANDROID__)
3187 #include <cstdlib>
3188 void *aligned_alloc(size_t alignment, size_t size)
3189 {
3190  // alignment must be >= sizeof(void*)
3191  if(alignment < sizeof(void*))
3192  {
3193  alignment = sizeof(void*);
3194  }
3195 
3196  void *pointer;
3197  if(posix_memalign(&pointer, alignment, size) == 0)
3198  return pointer;
3199  return VMA_NULL;
3200 }
3201 #endif
3202 
3203 // If your compiler is not compatible with C++11 and definition of
3204 // aligned_alloc() function is missing, uncommeting following line may help:
3205 
3206 //#include <malloc.h>
3207 
3208 // Normal assert to check for programmer's errors, especially in Debug configuration.
3209 #ifndef VMA_ASSERT
3210  #ifdef _DEBUG
3211  #define VMA_ASSERT(expr) assert(expr)
3212  #else
3213  #define VMA_ASSERT(expr)
3214  #endif
3215 #endif
3216 
3217 // Assert that will be called very often, like inside data structures e.g. operator[].
3218 // Making it non-empty can make program slow.
3219 #ifndef VMA_HEAVY_ASSERT
3220  #ifdef _DEBUG
3221  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3222  #else
3223  #define VMA_HEAVY_ASSERT(expr)
3224  #endif
3225 #endif
3226 
3227 #ifndef VMA_ALIGN_OF
3228  #define VMA_ALIGN_OF(type) (__alignof(type))
3229 #endif
3230 
3231 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3232  #if defined(_WIN32)
3233  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3234  #else
3235  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3236  #endif
3237 #endif
3238 
3239 #ifndef VMA_SYSTEM_FREE
3240  #if defined(_WIN32)
3241  #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3242  #else
3243  #define VMA_SYSTEM_FREE(ptr) free(ptr)
3244  #endif
3245 #endif
3246 
3247 #ifndef VMA_MIN
3248  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3249 #endif
3250 
3251 #ifndef VMA_MAX
3252  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3253 #endif
3254 
3255 #ifndef VMA_SWAP
3256  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3257 #endif
3258 
3259 #ifndef VMA_SORT
3260  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3261 #endif
3262 
3263 #ifndef VMA_DEBUG_LOG
3264  #define VMA_DEBUG_LOG(format, ...)
3265  /*
3266  #define VMA_DEBUG_LOG(format, ...) do { \
3267  printf(format, __VA_ARGS__); \
3268  printf("\n"); \
3269  } while(false)
3270  */
3271 #endif
3272 
3273 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3274 #if VMA_STATS_STRING_ENABLED
3275  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3276  {
3277  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3278  }
3279  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3280  {
3281  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3282  }
3283  static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3284  {
3285  snprintf(outStr, strLen, "%p", ptr);
3286  }
3287 #endif
3288 
3289 #ifndef VMA_MUTEX
3290  class VmaMutex
3291  {
3292  public:
3293  void Lock() { m_Mutex.lock(); }
3294  void Unlock() { m_Mutex.unlock(); }
3295  private:
3296  std::mutex m_Mutex;
3297  };
3298  #define VMA_MUTEX VmaMutex
3299 #endif
3300 
3301 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
3302 #ifndef VMA_RW_MUTEX
3303  #if VMA_USE_STL_SHARED_MUTEX
3304  // Use std::shared_mutex from C++17.
3305  #include <shared_mutex>
3306  class VmaRWMutex
3307  {
3308  public:
3309  void LockRead() { m_Mutex.lock_shared(); }
3310  void UnlockRead() { m_Mutex.unlock_shared(); }
3311  void LockWrite() { m_Mutex.lock(); }
3312  void UnlockWrite() { m_Mutex.unlock(); }
3313  private:
3314  std::shared_mutex m_Mutex;
3315  };
3316  #define VMA_RW_MUTEX VmaRWMutex
3317  #elif defined(_WIN32)
3318  // Use SRWLOCK from WinAPI.
3319  class VmaRWMutex
3320  {
3321  public:
3322  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3323  void LockRead() { AcquireSRWLockShared(&m_Lock); }
3324  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3325  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3326  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3327  private:
3328  SRWLOCK m_Lock;
3329  };
3330  #define VMA_RW_MUTEX VmaRWMutex
3331  #else
3332  // Less efficient fallback: Use normal mutex.
3333  class VmaRWMutex
3334  {
3335  public:
3336  void LockRead() { m_Mutex.Lock(); }
3337  void UnlockRead() { m_Mutex.Unlock(); }
3338  void LockWrite() { m_Mutex.Lock(); }
3339  void UnlockWrite() { m_Mutex.Unlock(); }
3340  private:
3341  VMA_MUTEX m_Mutex;
3342  };
3343  #define VMA_RW_MUTEX VmaRWMutex
3344  #endif // #if VMA_USE_STL_SHARED_MUTEX
3345 #endif // #ifndef VMA_RW_MUTEX
3346 
3347 /*
3348 If providing your own implementation, you need to implement a subset of std::atomic:
3349 
3350 - Constructor(uint32_t desired)
3351 - uint32_t load() const
3352 - void store(uint32_t desired)
3353 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3354 */
3355 #ifndef VMA_ATOMIC_UINT32
3356  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3357 #endif
3358 
3359 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3360 
3364  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3365 #endif
3366 
3367 #ifndef VMA_DEBUG_ALIGNMENT
3368 
3372  #define VMA_DEBUG_ALIGNMENT (1)
3373 #endif
3374 
3375 #ifndef VMA_DEBUG_MARGIN
3376 
3380  #define VMA_DEBUG_MARGIN (0)
3381 #endif
3382 
3383 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3384 
3388  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3389 #endif
3390 
3391 #ifndef VMA_DEBUG_DETECT_CORRUPTION
3392 
3397  #define VMA_DEBUG_DETECT_CORRUPTION (0)
3398 #endif
3399 
3400 #ifndef VMA_DEBUG_GLOBAL_MUTEX
3401 
3405  #define VMA_DEBUG_GLOBAL_MUTEX (0)
3406 #endif
3407 
3408 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3409 
3413  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3414 #endif
3415 
3416 #ifndef VMA_SMALL_HEAP_MAX_SIZE
3417  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3419 #endif
3420 
3421 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3422  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3424 #endif
3425 
3426 #ifndef VMA_CLASS_NO_COPY
3427  #define VMA_CLASS_NO_COPY(className) \
3428  private: \
3429  className(const className&) = delete; \
3430  className& operator=(const className&) = delete;
3431 #endif
3432 
3433 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3434 
3435 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3436 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3437 
3438 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3439 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3440 
3441 /*******************************************************************************
3442 END OF CONFIGURATION
3443 */
3444 
3445 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3446 
3447 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3448  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3449 
3450 // Returns number of bits set to 1 in (v).
3451 static inline uint32_t VmaCountBitsSet(uint32_t v)
3452 {
3453  uint32_t c = v - ((v >> 1) & 0x55555555);
3454  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3455  c = ((c >> 4) + c) & 0x0F0F0F0F;
3456  c = ((c >> 8) + c) & 0x00FF00FF;
3457  c = ((c >> 16) + c) & 0x0000FFFF;
3458  return c;
3459 }
3460 
3461 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3462 // Use types like uint32_t, uint64_t as T.
3463 template <typename T>
3464 static inline T VmaAlignUp(T val, T align)
3465 {
3466  return (val + align - 1) / align * align;
3467 }
3468 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3469 // Use types like uint32_t, uint64_t as T.
3470 template <typename T>
3471 static inline T VmaAlignDown(T val, T align)
3472 {
3473  return val / align * align;
3474 }
3475 
3476 // Division with mathematical rounding to nearest number.
3477 template <typename T>
3478 static inline T VmaRoundDiv(T x, T y)
3479 {
3480  return (x + (y / (T)2)) / y;
3481 }
3482 
3483 /*
3484 Returns true if given number is a power of two.
3485 T must be unsigned integer number or signed integer but always nonnegative.
3486 For 0 returns true.
3487 */
3488 template <typename T>
3489 inline bool VmaIsPow2(T x)
3490 {
3491  return (x & (x-1)) == 0;
3492 }
3493 
3494 // Returns smallest power of 2 greater or equal to v.
3495 static inline uint32_t VmaNextPow2(uint32_t v)
3496 {
3497  v--;
3498  v |= v >> 1;
3499  v |= v >> 2;
3500  v |= v >> 4;
3501  v |= v >> 8;
3502  v |= v >> 16;
3503  v++;
3504  return v;
3505 }
3506 static inline uint64_t VmaNextPow2(uint64_t v)
3507 {
3508  v--;
3509  v |= v >> 1;
3510  v |= v >> 2;
3511  v |= v >> 4;
3512  v |= v >> 8;
3513  v |= v >> 16;
3514  v |= v >> 32;
3515  v++;
3516  return v;
3517 }
3518 
3519 // Returns largest power of 2 less or equal to v.
3520 static inline uint32_t VmaPrevPow2(uint32_t v)
3521 {
3522  v |= v >> 1;
3523  v |= v >> 2;
3524  v |= v >> 4;
3525  v |= v >> 8;
3526  v |= v >> 16;
3527  v = v ^ (v >> 1);
3528  return v;
3529 }
3530 static inline uint64_t VmaPrevPow2(uint64_t v)
3531 {
3532  v |= v >> 1;
3533  v |= v >> 2;
3534  v |= v >> 4;
3535  v |= v >> 8;
3536  v |= v >> 16;
3537  v |= v >> 32;
3538  v = v ^ (v >> 1);
3539  return v;
3540 }
3541 
3542 static inline bool VmaStrIsEmpty(const char* pStr)
3543 {
3544  return pStr == VMA_NULL || *pStr == '\0';
3545 }
3546 
3547 static const char* VmaAlgorithmToStr(uint32_t algorithm)
3548 {
3549  switch(algorithm)
3550  {
3552  return "Linear";
3554  return "Buddy";
3555  case 0:
3556  return "Default";
3557  default:
3558  VMA_ASSERT(0);
3559  return "";
3560  }
3561 }
3562 
3563 #ifndef VMA_SORT
3564 
3565 template<typename Iterator, typename Compare>
3566 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3567 {
3568  Iterator centerValue = end; --centerValue;
3569  Iterator insertIndex = beg;
3570  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3571  {
3572  if(cmp(*memTypeIndex, *centerValue))
3573  {
3574  if(insertIndex != memTypeIndex)
3575  {
3576  VMA_SWAP(*memTypeIndex, *insertIndex);
3577  }
3578  ++insertIndex;
3579  }
3580  }
3581  if(insertIndex != centerValue)
3582  {
3583  VMA_SWAP(*insertIndex, *centerValue);
3584  }
3585  return insertIndex;
3586 }
3587 
3588 template<typename Iterator, typename Compare>
3589 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3590 {
3591  if(beg < end)
3592  {
3593  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3594  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3595  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3596  }
3597 }
3598 
3599 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3600 
3601 #endif // #ifndef VMA_SORT
3602 
3603 /*
3604 Returns true if two memory blocks occupy overlapping pages.
3605 ResourceA must be in less memory offset than ResourceB.
3606 
3607 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3608 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3609 */
3610 static inline bool VmaBlocksOnSamePage(
3611  VkDeviceSize resourceAOffset,
3612  VkDeviceSize resourceASize,
3613  VkDeviceSize resourceBOffset,
3614  VkDeviceSize pageSize)
3615 {
3616  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3617  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3618  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3619  VkDeviceSize resourceBStart = resourceBOffset;
3620  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3621  return resourceAEndPage == resourceBStartPage;
3622 }
3623 
3624 enum VmaSuballocationType
3625 {
3626  VMA_SUBALLOCATION_TYPE_FREE = 0,
3627  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3628  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3629  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3630  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3631  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3632  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3633 };
3634 
3635 /*
3636 Returns true if given suballocation types could conflict and must respect
3637 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3638 or linear image and another one is optimal image. If type is unknown, behave
3639 conservatively.
3640 */
3641 static inline bool VmaIsBufferImageGranularityConflict(
3642  VmaSuballocationType suballocType1,
3643  VmaSuballocationType suballocType2)
3644 {
3645  if(suballocType1 > suballocType2)
3646  {
3647  VMA_SWAP(suballocType1, suballocType2);
3648  }
3649 
3650  switch(suballocType1)
3651  {
3652  case VMA_SUBALLOCATION_TYPE_FREE:
3653  return false;
3654  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3655  return true;
3656  case VMA_SUBALLOCATION_TYPE_BUFFER:
3657  return
3658  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3659  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3660  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3661  return
3662  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3663  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3664  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3665  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3666  return
3667  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3668  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3669  return false;
3670  default:
3671  VMA_ASSERT(0);
3672  return true;
3673  }
3674 }
3675 
3676 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3677 {
3678  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3679  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3680  for(size_t i = 0; i < numberCount; ++i, ++pDst)
3681  {
3682  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3683  }
3684 }
3685 
3686 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3687 {
3688  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3689  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3690  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3691  {
3692  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3693  {
3694  return false;
3695  }
3696  }
3697  return true;
3698 }
3699 
3700 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3701 struct VmaMutexLock
3702 {
3703  VMA_CLASS_NO_COPY(VmaMutexLock)
3704 public:
3705  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3706  m_pMutex(useMutex ? &mutex : VMA_NULL)
3707  { if(m_pMutex) { m_pMutex->Lock(); } }
3708  ~VmaMutexLock()
3709  { if(m_pMutex) { m_pMutex->Unlock(); } }
3710 private:
3711  VMA_MUTEX* m_pMutex;
3712 };
3713 
3714 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3715 struct VmaMutexLockRead
3716 {
3717  VMA_CLASS_NO_COPY(VmaMutexLockRead)
3718 public:
3719  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3720  m_pMutex(useMutex ? &mutex : VMA_NULL)
3721  { if(m_pMutex) { m_pMutex->LockRead(); } }
3722  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3723 private:
3724  VMA_RW_MUTEX* m_pMutex;
3725 };
3726 
3727 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3728 struct VmaMutexLockWrite
3729 {
3730  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3731 public:
3732  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3733  m_pMutex(useMutex ? &mutex : VMA_NULL)
3734  { if(m_pMutex) { m_pMutex->LockWrite(); } }
3735  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3736 private:
3737  VMA_RW_MUTEX* m_pMutex;
3738 };
3739 
3740 #if VMA_DEBUG_GLOBAL_MUTEX
3741  static VMA_MUTEX gDebugGlobalMutex;
3742  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3743 #else
3744  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3745 #endif
3746 
3747 // Minimum size of a free suballocation to register it in the free suballocation collection.
3748 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3749 
3750 /*
3751 Performs binary search and returns iterator to first element that is greater or
3752 equal to (key), according to comparison (cmp).
3753 
3754 Cmp should return true if first argument is less than second argument.
3755 
3756 Returned value is the found element, if present in the collection or place where
3757 new element with value (key) should be inserted.
3758 */
3759 template <typename CmpLess, typename IterT, typename KeyT>
3760 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3761 {
3762  size_t down = 0, up = (end - beg);
3763  while(down < up)
3764  {
3765  const size_t mid = (down + up) / 2;
3766  if(cmp(*(beg+mid), key))
3767  {
3768  down = mid + 1;
3769  }
3770  else
3771  {
3772  up = mid;
3773  }
3774  }
3775  return beg + down;
3776 }
3777 
3778 /*
3779 Returns true if all pointers in the array are not-null and unique.
3780 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3781 T must be pointer type, e.g. VmaAllocation, VmaPool.
3782 */
3783 template<typename T>
3784 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3785 {
3786  for(uint32_t i = 0; i < count; ++i)
3787  {
3788  const T iPtr = arr[i];
3789  if(iPtr == VMA_NULL)
3790  {
3791  return false;
3792  }
3793  for(uint32_t j = i + 1; j < count; ++j)
3794  {
3795  if(iPtr == arr[j])
3796  {
3797  return false;
3798  }
3799  }
3800  }
3801  return true;
3802 }
3803 
3805 // Memory allocation
3806 
3807 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3808 {
3809  if((pAllocationCallbacks != VMA_NULL) &&
3810  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3811  {
3812  return (*pAllocationCallbacks->pfnAllocation)(
3813  pAllocationCallbacks->pUserData,
3814  size,
3815  alignment,
3816  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3817  }
3818  else
3819  {
3820  return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3821  }
3822 }
3823 
3824 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3825 {
3826  if((pAllocationCallbacks != VMA_NULL) &&
3827  (pAllocationCallbacks->pfnFree != VMA_NULL))
3828  {
3829  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3830  }
3831  else
3832  {
3833  VMA_SYSTEM_FREE(ptr);
3834  }
3835 }
3836 
3837 template<typename T>
3838 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3839 {
3840  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3841 }
3842 
3843 template<typename T>
3844 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3845 {
3846  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3847 }
3848 
3849 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3850 
3851 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3852 
3853 template<typename T>
3854 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3855 {
3856  ptr->~T();
3857  VmaFree(pAllocationCallbacks, ptr);
3858 }
3859 
3860 template<typename T>
3861 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3862 {
3863  if(ptr != VMA_NULL)
3864  {
3865  for(size_t i = count; i--; )
3866  {
3867  ptr[i].~T();
3868  }
3869  VmaFree(pAllocationCallbacks, ptr);
3870  }
3871 }
3872 
3873 // STL-compatible allocator.
3874 template<typename T>
3875 class VmaStlAllocator
3876 {
3877 public:
3878  const VkAllocationCallbacks* const m_pCallbacks;
3879  typedef T value_type;
3880 
3881  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3882  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3883 
3884  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3885  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3886 
3887  template<typename U>
3888  bool operator==(const VmaStlAllocator<U>& rhs) const
3889  {
3890  return m_pCallbacks == rhs.m_pCallbacks;
3891  }
3892  template<typename U>
3893  bool operator!=(const VmaStlAllocator<U>& rhs) const
3894  {
3895  return m_pCallbacks != rhs.m_pCallbacks;
3896  }
3897 
3898  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3899 };
3900 
3901 #if VMA_USE_STL_VECTOR
3902 
3903 #define VmaVector std::vector
3904 
3905 template<typename T, typename allocatorT>
3906 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3907 {
3908  vec.insert(vec.begin() + index, item);
3909 }
3910 
3911 template<typename T, typename allocatorT>
3912 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3913 {
3914  vec.erase(vec.begin() + index);
3915 }
3916 
3917 #else // #if VMA_USE_STL_VECTOR
3918 
3919 /* Class with interface compatible with subset of std::vector.
3920 T must be POD because constructors and destructors are not called and memcpy is
3921 used for these objects. */
3922 template<typename T, typename AllocatorT>
3923 class VmaVector
3924 {
3925 public:
3926  typedef T value_type;
3927 
3928  VmaVector(const AllocatorT& allocator) :
3929  m_Allocator(allocator),
3930  m_pArray(VMA_NULL),
3931  m_Count(0),
3932  m_Capacity(0)
3933  {
3934  }
3935 
3936  VmaVector(size_t count, const AllocatorT& allocator) :
3937  m_Allocator(allocator),
3938  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3939  m_Count(count),
3940  m_Capacity(count)
3941  {
3942  }
3943 
3944  VmaVector(const VmaVector<T, AllocatorT>& src) :
3945  m_Allocator(src.m_Allocator),
3946  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3947  m_Count(src.m_Count),
3948  m_Capacity(src.m_Count)
3949  {
3950  if(m_Count != 0)
3951  {
3952  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3953  }
3954  }
3955 
3956  ~VmaVector()
3957  {
3958  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3959  }
3960 
3961  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3962  {
3963  if(&rhs != this)
3964  {
3965  resize(rhs.m_Count);
3966  if(m_Count != 0)
3967  {
3968  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3969  }
3970  }
3971  return *this;
3972  }
3973 
3974  bool empty() const { return m_Count == 0; }
3975  size_t size() const { return m_Count; }
3976  T* data() { return m_pArray; }
3977  const T* data() const { return m_pArray; }
3978 
3979  T& operator[](size_t index)
3980  {
3981  VMA_HEAVY_ASSERT(index < m_Count);
3982  return m_pArray[index];
3983  }
3984  const T& operator[](size_t index) const
3985  {
3986  VMA_HEAVY_ASSERT(index < m_Count);
3987  return m_pArray[index];
3988  }
3989 
3990  T& front()
3991  {
3992  VMA_HEAVY_ASSERT(m_Count > 0);
3993  return m_pArray[0];
3994  }
3995  const T& front() const
3996  {
3997  VMA_HEAVY_ASSERT(m_Count > 0);
3998  return m_pArray[0];
3999  }
4000  T& back()
4001  {
4002  VMA_HEAVY_ASSERT(m_Count > 0);
4003  return m_pArray[m_Count - 1];
4004  }
4005  const T& back() const
4006  {
4007  VMA_HEAVY_ASSERT(m_Count > 0);
4008  return m_pArray[m_Count - 1];
4009  }
4010 
4011  void reserve(size_t newCapacity, bool freeMemory = false)
4012  {
4013  newCapacity = VMA_MAX(newCapacity, m_Count);
4014 
4015  if((newCapacity < m_Capacity) && !freeMemory)
4016  {
4017  newCapacity = m_Capacity;
4018  }
4019 
4020  if(newCapacity != m_Capacity)
4021  {
4022  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4023  if(m_Count != 0)
4024  {
4025  memcpy(newArray, m_pArray, m_Count * sizeof(T));
4026  }
4027  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4028  m_Capacity = newCapacity;
4029  m_pArray = newArray;
4030  }
4031  }
4032 
4033  void resize(size_t newCount, bool freeMemory = false)
4034  {
4035  size_t newCapacity = m_Capacity;
4036  if(newCount > m_Capacity)
4037  {
4038  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4039  }
4040  else if(freeMemory)
4041  {
4042  newCapacity = newCount;
4043  }
4044 
4045  if(newCapacity != m_Capacity)
4046  {
4047  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4048  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4049  if(elementsToCopy != 0)
4050  {
4051  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4052  }
4053  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4054  m_Capacity = newCapacity;
4055  m_pArray = newArray;
4056  }
4057 
4058  m_Count = newCount;
4059  }
4060 
4061  void clear(bool freeMemory = false)
4062  {
4063  resize(0, freeMemory);
4064  }
4065 
4066  void insert(size_t index, const T& src)
4067  {
4068  VMA_HEAVY_ASSERT(index <= m_Count);
4069  const size_t oldCount = size();
4070  resize(oldCount + 1);
4071  if(index < oldCount)
4072  {
4073  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4074  }
4075  m_pArray[index] = src;
4076  }
4077 
4078  void remove(size_t index)
4079  {
4080  VMA_HEAVY_ASSERT(index < m_Count);
4081  const size_t oldCount = size();
4082  if(index < oldCount - 1)
4083  {
4084  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4085  }
4086  resize(oldCount - 1);
4087  }
4088 
4089  void push_back(const T& src)
4090  {
4091  const size_t newIndex = size();
4092  resize(newIndex + 1);
4093  m_pArray[newIndex] = src;
4094  }
4095 
4096  void pop_back()
4097  {
4098  VMA_HEAVY_ASSERT(m_Count > 0);
4099  resize(size() - 1);
4100  }
4101 
4102  void push_front(const T& src)
4103  {
4104  insert(0, src);
4105  }
4106 
4107  void pop_front()
4108  {
4109  VMA_HEAVY_ASSERT(m_Count > 0);
4110  remove(0);
4111  }
4112 
4113  typedef T* iterator;
4114 
4115  iterator begin() { return m_pArray; }
4116  iterator end() { return m_pArray + m_Count; }
4117 
4118 private:
4119  AllocatorT m_Allocator;
4120  T* m_pArray;
4121  size_t m_Count;
4122  size_t m_Capacity;
4123 };
4124 
4125 template<typename T, typename allocatorT>
4126 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4127 {
4128  vec.insert(index, item);
4129 }
4130 
4131 template<typename T, typename allocatorT>
4132 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4133 {
4134  vec.remove(index);
4135 }
4136 
4137 #endif // #if VMA_USE_STL_VECTOR
4138 
4139 template<typename CmpLess, typename VectorT>
4140 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4141 {
4142  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4143  vector.data(),
4144  vector.data() + vector.size(),
4145  value,
4146  CmpLess()) - vector.data();
4147  VmaVectorInsert(vector, indexToInsert, value);
4148  return indexToInsert;
4149 }
4150 
4151 template<typename CmpLess, typename VectorT>
4152 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4153 {
4154  CmpLess comparator;
4155  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4156  vector.begin(),
4157  vector.end(),
4158  value,
4159  comparator);
4160  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4161  {
4162  size_t indexToRemove = it - vector.begin();
4163  VmaVectorRemove(vector, indexToRemove);
4164  return true;
4165  }
4166  return false;
4167 }
4168 
4169 template<typename CmpLess, typename IterT, typename KeyT>
4170 IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4171 {
4172  CmpLess comparator;
4173  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4174  beg, end, value, comparator);
4175  if(it == end ||
4176  (!comparator(*it, value) && !comparator(value, *it)))
4177  {
4178  return it;
4179  }
4180  return end;
4181 }
4182 
4184 // class VmaPoolAllocator
4185 
4186 /*
4187 Allocator for objects of type T using a list of arrays (pools) to speed up
4188 allocation. Number of elements that can be allocated is not bounded because
4189 allocator can create multiple blocks.
4190 */
4191 template<typename T>
4192 class VmaPoolAllocator
4193 {
4194  VMA_CLASS_NO_COPY(VmaPoolAllocator)
4195 public:
4196  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4197  ~VmaPoolAllocator();
4198  void Clear();
4199  T* Alloc();
4200  void Free(T* ptr);
4201 
4202 private:
4203  union Item
4204  {
4205  uint32_t NextFreeIndex;
4206  T Value;
4207  };
4208 
4209  struct ItemBlock
4210  {
4211  Item* pItems;
4212  uint32_t FirstFreeIndex;
4213  };
4214 
4215  const VkAllocationCallbacks* m_pAllocationCallbacks;
4216  size_t m_ItemsPerBlock;
4217  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4218 
4219  ItemBlock& CreateNewBlock();
4220 };
4221 
4222 template<typename T>
4223 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4224  m_pAllocationCallbacks(pAllocationCallbacks),
4225  m_ItemsPerBlock(itemsPerBlock),
4226  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4227 {
4228  VMA_ASSERT(itemsPerBlock > 0);
4229 }
4230 
4231 template<typename T>
4232 VmaPoolAllocator<T>::~VmaPoolAllocator()
4233 {
4234  Clear();
4235 }
4236 
4237 template<typename T>
4238 void VmaPoolAllocator<T>::Clear()
4239 {
4240  for(size_t i = m_ItemBlocks.size(); i--; )
4241  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4242  m_ItemBlocks.clear();
4243 }
4244 
4245 template<typename T>
4246 T* VmaPoolAllocator<T>::Alloc()
4247 {
4248  for(size_t i = m_ItemBlocks.size(); i--; )
4249  {
4250  ItemBlock& block = m_ItemBlocks[i];
4251  // This block has some free items: Use first one.
4252  if(block.FirstFreeIndex != UINT32_MAX)
4253  {
4254  Item* const pItem = &block.pItems[block.FirstFreeIndex];
4255  block.FirstFreeIndex = pItem->NextFreeIndex;
4256  return &pItem->Value;
4257  }
4258  }
4259 
4260  // No block has free item: Create new one and use it.
4261  ItemBlock& newBlock = CreateNewBlock();
4262  Item* const pItem = &newBlock.pItems[0];
4263  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4264  return &pItem->Value;
4265 }
4266 
4267 template<typename T>
4268 void VmaPoolAllocator<T>::Free(T* ptr)
4269 {
4270  // Search all memory blocks to find ptr.
4271  for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4272  {
4273  ItemBlock& block = m_ItemBlocks[i];
4274 
4275  // Casting to union.
4276  Item* pItemPtr;
4277  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4278 
4279  // Check if pItemPtr is in address range of this block.
4280  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4281  {
4282  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4283  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4284  block.FirstFreeIndex = index;
4285  return;
4286  }
4287  }
4288  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4289 }
4290 
4291 template<typename T>
4292 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4293 {
4294  ItemBlock newBlock = {
4295  vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4296 
4297  m_ItemBlocks.push_back(newBlock);
4298 
4299  // Setup singly-linked list of all free items in this block.
4300  for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4301  newBlock.pItems[i].NextFreeIndex = i + 1;
4302  newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4303  return m_ItemBlocks.back();
4304 }
4305 
4307 // class VmaRawList, VmaList
4308 
4309 #if VMA_USE_STL_LIST
4310 
4311 #define VmaList std::list
4312 
4313 #else // #if VMA_USE_STL_LIST
4314 
4315 template<typename T>
4316 struct VmaListItem
4317 {
4318  VmaListItem* pPrev;
4319  VmaListItem* pNext;
4320  T Value;
4321 };
4322 
4323 // Doubly linked list.
4324 template<typename T>
4325 class VmaRawList
4326 {
4327  VMA_CLASS_NO_COPY(VmaRawList)
4328 public:
4329  typedef VmaListItem<T> ItemType;
4330 
4331  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4332  ~VmaRawList();
4333  void Clear();
4334 
4335  size_t GetCount() const { return m_Count; }
4336  bool IsEmpty() const { return m_Count == 0; }
4337 
4338  ItemType* Front() { return m_pFront; }
4339  const ItemType* Front() const { return m_pFront; }
4340  ItemType* Back() { return m_pBack; }
4341  const ItemType* Back() const { return m_pBack; }
4342 
4343  ItemType* PushBack();
4344  ItemType* PushFront();
4345  ItemType* PushBack(const T& value);
4346  ItemType* PushFront(const T& value);
4347  void PopBack();
4348  void PopFront();
4349 
4350  // Item can be null - it means PushBack.
4351  ItemType* InsertBefore(ItemType* pItem);
4352  // Item can be null - it means PushFront.
4353  ItemType* InsertAfter(ItemType* pItem);
4354 
4355  ItemType* InsertBefore(ItemType* pItem, const T& value);
4356  ItemType* InsertAfter(ItemType* pItem, const T& value);
4357 
4358  void Remove(ItemType* pItem);
4359 
4360 private:
4361  const VkAllocationCallbacks* const m_pAllocationCallbacks;
4362  VmaPoolAllocator<ItemType> m_ItemAllocator;
4363  ItemType* m_pFront;
4364  ItemType* m_pBack;
4365  size_t m_Count;
4366 };
4367 
4368 template<typename T>
4369 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4370  m_pAllocationCallbacks(pAllocationCallbacks),
4371  m_ItemAllocator(pAllocationCallbacks, 128),
4372  m_pFront(VMA_NULL),
4373  m_pBack(VMA_NULL),
4374  m_Count(0)
4375 {
4376 }
4377 
4378 template<typename T>
4379 VmaRawList<T>::~VmaRawList()
4380 {
4381  // Intentionally not calling Clear, because that would be unnecessary
4382  // computations to return all items to m_ItemAllocator as free.
4383 }
4384 
4385 template<typename T>
4386 void VmaRawList<T>::Clear()
4387 {
4388  if(IsEmpty() == false)
4389  {
4390  ItemType* pItem = m_pBack;
4391  while(pItem != VMA_NULL)
4392  {
4393  ItemType* const pPrevItem = pItem->pPrev;
4394  m_ItemAllocator.Free(pItem);
4395  pItem = pPrevItem;
4396  }
4397  m_pFront = VMA_NULL;
4398  m_pBack = VMA_NULL;
4399  m_Count = 0;
4400  }
4401 }
4402 
4403 template<typename T>
4404 VmaListItem<T>* VmaRawList<T>::PushBack()
4405 {
4406  ItemType* const pNewItem = m_ItemAllocator.Alloc();
4407  pNewItem->pNext = VMA_NULL;
4408  if(IsEmpty())
4409  {
4410  pNewItem->pPrev = VMA_NULL;
4411  m_pFront = pNewItem;
4412  m_pBack = pNewItem;
4413  m_Count = 1;
4414  }
4415  else
4416  {
4417  pNewItem->pPrev = m_pBack;
4418  m_pBack->pNext = pNewItem;
4419  m_pBack = pNewItem;
4420  ++m_Count;
4421  }
4422  return pNewItem;
4423 }
4424 
4425 template<typename T>
4426 VmaListItem<T>* VmaRawList<T>::PushFront()
4427 {
4428  ItemType* const pNewItem = m_ItemAllocator.Alloc();
4429  pNewItem->pPrev = VMA_NULL;
4430  if(IsEmpty())
4431  {
4432  pNewItem->pNext = VMA_NULL;
4433  m_pFront = pNewItem;
4434  m_pBack = pNewItem;
4435  m_Count = 1;
4436  }
4437  else
4438  {
4439  pNewItem->pNext = m_pFront;
4440  m_pFront->pPrev = pNewItem;
4441  m_pFront = pNewItem;
4442  ++m_Count;
4443  }
4444  return pNewItem;
4445 }
4446 
4447 template<typename T>
4448 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4449 {
4450  ItemType* const pNewItem = PushBack();
4451  pNewItem->Value = value;
4452  return pNewItem;
4453 }
4454 
4455 template<typename T>
4456 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4457 {
4458  ItemType* const pNewItem = PushFront();
4459  pNewItem->Value = value;
4460  return pNewItem;
4461 }
4462 
4463 template<typename T>
4464 void VmaRawList<T>::PopBack()
4465 {
4466  VMA_HEAVY_ASSERT(m_Count > 0);
4467  ItemType* const pBackItem = m_pBack;
4468  ItemType* const pPrevItem = pBackItem->pPrev;
4469  if(pPrevItem != VMA_NULL)
4470  {
4471  pPrevItem->pNext = VMA_NULL;
4472  }
4473  m_pBack = pPrevItem;
4474  m_ItemAllocator.Free(pBackItem);
4475  --m_Count;
4476 }
4477 
4478 template<typename T>
4479 void VmaRawList<T>::PopFront()
4480 {
4481  VMA_HEAVY_ASSERT(m_Count > 0);
4482  ItemType* const pFrontItem = m_pFront;
4483  ItemType* const pNextItem = pFrontItem->pNext;
4484  if(pNextItem != VMA_NULL)
4485  {
4486  pNextItem->pPrev = VMA_NULL;
4487  }
4488  m_pFront = pNextItem;
4489  m_ItemAllocator.Free(pFrontItem);
4490  --m_Count;
4491 }
4492 
4493 template<typename T>
4494 void VmaRawList<T>::Remove(ItemType* pItem)
4495 {
4496  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4497  VMA_HEAVY_ASSERT(m_Count > 0);
4498 
4499  if(pItem->pPrev != VMA_NULL)
4500  {
4501  pItem->pPrev->pNext = pItem->pNext;
4502  }
4503  else
4504  {
4505  VMA_HEAVY_ASSERT(m_pFront == pItem);
4506  m_pFront = pItem->pNext;
4507  }
4508 
4509  if(pItem->pNext != VMA_NULL)
4510  {
4511  pItem->pNext->pPrev = pItem->pPrev;
4512  }
4513  else
4514  {
4515  VMA_HEAVY_ASSERT(m_pBack == pItem);
4516  m_pBack = pItem->pPrev;
4517  }
4518 
4519  m_ItemAllocator.Free(pItem);
4520  --m_Count;
4521 }
4522 
4523 template<typename T>
4524 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4525 {
4526  if(pItem != VMA_NULL)
4527  {
4528  ItemType* const prevItem = pItem->pPrev;
4529  ItemType* const newItem = m_ItemAllocator.Alloc();
4530  newItem->pPrev = prevItem;
4531  newItem->pNext = pItem;
4532  pItem->pPrev = newItem;
4533  if(prevItem != VMA_NULL)
4534  {
4535  prevItem->pNext = newItem;
4536  }
4537  else
4538  {
4539  VMA_HEAVY_ASSERT(m_pFront == pItem);
4540  m_pFront = newItem;
4541  }
4542  ++m_Count;
4543  return newItem;
4544  }
4545  else
4546  return PushBack();
4547 }
4548 
4549 template<typename T>
4550 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4551 {
4552  if(pItem != VMA_NULL)
4553  {
4554  ItemType* const nextItem = pItem->pNext;
4555  ItemType* const newItem = m_ItemAllocator.Alloc();
4556  newItem->pNext = nextItem;
4557  newItem->pPrev = pItem;
4558  pItem->pNext = newItem;
4559  if(nextItem != VMA_NULL)
4560  {
4561  nextItem->pPrev = newItem;
4562  }
4563  else
4564  {
4565  VMA_HEAVY_ASSERT(m_pBack == pItem);
4566  m_pBack = newItem;
4567  }
4568  ++m_Count;
4569  return newItem;
4570  }
4571  else
4572  return PushFront();
4573 }
4574 
4575 template<typename T>
4576 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4577 {
4578  ItemType* const newItem = InsertBefore(pItem);
4579  newItem->Value = value;
4580  return newItem;
4581 }
4582 
4583 template<typename T>
4584 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4585 {
4586  ItemType* const newItem = InsertAfter(pItem);
4587  newItem->Value = value;
4588  return newItem;
4589 }
4590 
4591 template<typename T, typename AllocatorT>
4592 class VmaList
4593 {
4594  VMA_CLASS_NO_COPY(VmaList)
4595 public:
4596  class iterator
4597  {
4598  public:
4599  iterator() :
4600  m_pList(VMA_NULL),
4601  m_pItem(VMA_NULL)
4602  {
4603  }
4604 
4605  T& operator*() const
4606  {
4607  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4608  return m_pItem->Value;
4609  }
4610  T* operator->() const
4611  {
4612  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4613  return &m_pItem->Value;
4614  }
4615 
4616  iterator& operator++()
4617  {
4618  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4619  m_pItem = m_pItem->pNext;
4620  return *this;
4621  }
4622  iterator& operator--()
4623  {
4624  if(m_pItem != VMA_NULL)
4625  {
4626  m_pItem = m_pItem->pPrev;
4627  }
4628  else
4629  {
4630  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4631  m_pItem = m_pList->Back();
4632  }
4633  return *this;
4634  }
4635 
4636  iterator operator++(int)
4637  {
4638  iterator result = *this;
4639  ++*this;
4640  return result;
4641  }
4642  iterator operator--(int)
4643  {
4644  iterator result = *this;
4645  --*this;
4646  return result;
4647  }
4648 
4649  bool operator==(const iterator& rhs) const
4650  {
4651  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4652  return m_pItem == rhs.m_pItem;
4653  }
4654  bool operator!=(const iterator& rhs) const
4655  {
4656  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4657  return m_pItem != rhs.m_pItem;
4658  }
4659 
4660  private:
4661  VmaRawList<T>* m_pList;
4662  VmaListItem<T>* m_pItem;
4663 
4664  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4665  m_pList(pList),
4666  m_pItem(pItem)
4667  {
4668  }
4669 
4670  friend class VmaList<T, AllocatorT>;
4671  };
4672 
4673  class const_iterator
4674  {
4675  public:
4676  const_iterator() :
4677  m_pList(VMA_NULL),
4678  m_pItem(VMA_NULL)
4679  {
4680  }
4681 
4682  const_iterator(const iterator& src) :
4683  m_pList(src.m_pList),
4684  m_pItem(src.m_pItem)
4685  {
4686  }
4687 
4688  const T& operator*() const
4689  {
4690  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4691  return m_pItem->Value;
4692  }
4693  const T* operator->() const
4694  {
4695  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4696  return &m_pItem->Value;
4697  }
4698 
4699  const_iterator& operator++()
4700  {
4701  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4702  m_pItem = m_pItem->pNext;
4703  return *this;
4704  }
4705  const_iterator& operator--()
4706  {
4707  if(m_pItem != VMA_NULL)
4708  {
4709  m_pItem = m_pItem->pPrev;
4710  }
4711  else
4712  {
4713  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4714  m_pItem = m_pList->Back();
4715  }
4716  return *this;
4717  }
4718 
4719  const_iterator operator++(int)
4720  {
4721  const_iterator result = *this;
4722  ++*this;
4723  return result;
4724  }
4725  const_iterator operator--(int)
4726  {
4727  const_iterator result = *this;
4728  --*this;
4729  return result;
4730  }
4731 
4732  bool operator==(const const_iterator& rhs) const
4733  {
4734  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4735  return m_pItem == rhs.m_pItem;
4736  }
4737  bool operator!=(const const_iterator& rhs) const
4738  {
4739  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4740  return m_pItem != rhs.m_pItem;
4741  }
4742 
4743  private:
4744  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4745  m_pList(pList),
4746  m_pItem(pItem)
4747  {
4748  }
4749 
4750  const VmaRawList<T>* m_pList;
4751  const VmaListItem<T>* m_pItem;
4752 
4753  friend class VmaList<T, AllocatorT>;
4754  };
4755 
4756  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4757 
4758  bool empty() const { return m_RawList.IsEmpty(); }
4759  size_t size() const { return m_RawList.GetCount(); }
4760 
4761  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4762  iterator end() { return iterator(&m_RawList, VMA_NULL); }
4763 
4764  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4765  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4766 
4767  void clear() { m_RawList.Clear(); }
4768  void push_back(const T& value) { m_RawList.PushBack(value); }
4769  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4770  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4771 
4772 private:
4773  VmaRawList<T> m_RawList;
4774 };
4775 
4776 #endif // #if VMA_USE_STL_LIST
4777 
4779 // class VmaMap
4780 
4781 // Unused in this version.
4782 #if 0
4783 
4784 #if VMA_USE_STL_UNORDERED_MAP
4785 
4786 #define VmaPair std::pair
4787 
4788 #define VMA_MAP_TYPE(KeyT, ValueT) \
4789  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4790 
4791 #else // #if VMA_USE_STL_UNORDERED_MAP
4792 
4793 template<typename T1, typename T2>
4794 struct VmaPair
4795 {
4796  T1 first;
4797  T2 second;
4798 
4799  VmaPair() : first(), second() { }
4800  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4801 };
4802 
4803 /* Class compatible with subset of interface of std::unordered_map.
4804 KeyT, ValueT must be POD because they will be stored in VmaVector.
4805 */
4806 template<typename KeyT, typename ValueT>
4807 class VmaMap
4808 {
4809 public:
4810  typedef VmaPair<KeyT, ValueT> PairType;
4811  typedef PairType* iterator;
4812 
4813  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4814 
4815  iterator begin() { return m_Vector.begin(); }
4816  iterator end() { return m_Vector.end(); }
4817 
4818  void insert(const PairType& pair);
4819  iterator find(const KeyT& key);
4820  void erase(iterator it);
4821 
4822 private:
4823  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4824 };
4825 
4826 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4827 
4828 template<typename FirstT, typename SecondT>
4829 struct VmaPairFirstLess
4830 {
4831  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4832  {
4833  return lhs.first < rhs.first;
4834  }
4835  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4836  {
4837  return lhs.first < rhsFirst;
4838  }
4839 };
4840 
4841 template<typename KeyT, typename ValueT>
4842 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4843 {
4844  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4845  m_Vector.data(),
4846  m_Vector.data() + m_Vector.size(),
4847  pair,
4848  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4849  VmaVectorInsert(m_Vector, indexToInsert, pair);
4850 }
4851 
4852 template<typename KeyT, typename ValueT>
4853 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4854 {
4855  PairType* it = VmaBinaryFindFirstNotLess(
4856  m_Vector.data(),
4857  m_Vector.data() + m_Vector.size(),
4858  key,
4859  VmaPairFirstLess<KeyT, ValueT>());
4860  if((it != m_Vector.end()) && (it->first == key))
4861  {
4862  return it;
4863  }
4864  else
4865  {
4866  return m_Vector.end();
4867  }
4868 }
4869 
4870 template<typename KeyT, typename ValueT>
4871 void VmaMap<KeyT, ValueT>::erase(iterator it)
4872 {
4873  VmaVectorRemove(m_Vector, it - m_Vector.begin());
4874 }
4875 
4876 #endif // #if VMA_USE_STL_UNORDERED_MAP
4877 
4878 #endif // #if 0
4879 
4881 
4882 class VmaDeviceMemoryBlock;
4883 
4884 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4885 
4886 struct VmaAllocation_T
4887 {
4888  VMA_CLASS_NO_COPY(VmaAllocation_T)
4889 private:
4890  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4891 
4892  enum FLAGS
4893  {
4894  FLAG_USER_DATA_STRING = 0x01,
4895  };
4896 
4897 public:
4898  enum ALLOCATION_TYPE
4899  {
4900  ALLOCATION_TYPE_NONE,
4901  ALLOCATION_TYPE_BLOCK,
4902  ALLOCATION_TYPE_DEDICATED,
4903  };
4904 
4905  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4906  m_Alignment(1),
4907  m_Size(0),
4908  m_pUserData(VMA_NULL),
4909  m_LastUseFrameIndex(currentFrameIndex),
4910  m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4911  m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4912  m_MapCount(0),
4913  m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4914  {
4915 #if VMA_STATS_STRING_ENABLED
4916  m_CreationFrameIndex = currentFrameIndex;
4917  m_BufferImageUsage = 0;
4918 #endif
4919  }
4920 
4921  ~VmaAllocation_T()
4922  {
4923  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4924 
4925  // Check if owned string was freed.
4926  VMA_ASSERT(m_pUserData == VMA_NULL);
4927  }
4928 
4929  void InitBlockAllocation(
4930  VmaPool hPool,
4931  VmaDeviceMemoryBlock* block,
4932  VkDeviceSize offset,
4933  VkDeviceSize alignment,
4934  VkDeviceSize size,
4935  VmaSuballocationType suballocationType,
4936  bool mapped,
4937  bool canBecomeLost)
4938  {
4939  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4940  VMA_ASSERT(block != VMA_NULL);
4941  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4942  m_Alignment = alignment;
4943  m_Size = size;
4944  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4945  m_SuballocationType = (uint8_t)suballocationType;
4946  m_BlockAllocation.m_hPool = hPool;
4947  m_BlockAllocation.m_Block = block;
4948  m_BlockAllocation.m_Offset = offset;
4949  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4950  }
4951 
4952  void InitLost()
4953  {
4954  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4955  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4956  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4957  m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4958  m_BlockAllocation.m_Block = VMA_NULL;
4959  m_BlockAllocation.m_Offset = 0;
4960  m_BlockAllocation.m_CanBecomeLost = true;
4961  }
4962 
4963  void ChangeBlockAllocation(
4964  VmaAllocator hAllocator,
4965  VmaDeviceMemoryBlock* block,
4966  VkDeviceSize offset);
4967 
4968  void ChangeSize(VkDeviceSize newSize);
4969  void ChangeOffset(VkDeviceSize newOffset);
4970 
4971  // pMappedData not null means allocation is created with MAPPED flag.
4972  void InitDedicatedAllocation(
4973  uint32_t memoryTypeIndex,
4974  VkDeviceMemory hMemory,
4975  VmaSuballocationType suballocationType,
4976  void* pMappedData,
4977  VkDeviceSize size)
4978  {
4979  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4980  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4981  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4982  m_Alignment = 0;
4983  m_Size = size;
4984  m_SuballocationType = (uint8_t)suballocationType;
4985  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4986  m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4987  m_DedicatedAllocation.m_hMemory = hMemory;
4988  m_DedicatedAllocation.m_pMappedData = pMappedData;
4989  }
4990 
4991  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
4992  VkDeviceSize GetAlignment() const { return m_Alignment; }
4993  VkDeviceSize GetSize() const { return m_Size; }
4994  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
4995  void* GetUserData() const { return m_pUserData; }
4996  void SetUserData(VmaAllocator hAllocator, void* pUserData);
4997  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
4998 
4999  VmaDeviceMemoryBlock* GetBlock() const
5000  {
5001  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5002  return m_BlockAllocation.m_Block;
5003  }
5004  VkDeviceSize GetOffset() const;
5005  VkDeviceMemory GetMemory() const;
5006  uint32_t GetMemoryTypeIndex() const;
5007  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5008  void* GetMappedData() const;
5009  bool CanBecomeLost() const;
5010  VmaPool GetPool() const;
5011 
5012  uint32_t GetLastUseFrameIndex() const
5013  {
5014  return m_LastUseFrameIndex.load();
5015  }
5016  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5017  {
5018  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5019  }
5020  /*
5021  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5022  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5023  - Else, returns false.
5024 
5025  If hAllocation is already lost, assert - you should not call it then.
5026  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5027  */
5028  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5029 
5030  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5031  {
5032  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5033  outInfo.blockCount = 1;
5034  outInfo.allocationCount = 1;
5035  outInfo.unusedRangeCount = 0;
5036  outInfo.usedBytes = m_Size;
5037  outInfo.unusedBytes = 0;
5038  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5039  outInfo.unusedRangeSizeMin = UINT64_MAX;
5040  outInfo.unusedRangeSizeMax = 0;
5041  }
5042 
5043  void BlockAllocMap();
5044  void BlockAllocUnmap();
5045  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5046  void DedicatedAllocUnmap(VmaAllocator hAllocator);
5047 
5048 #if VMA_STATS_STRING_ENABLED
5049  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5050  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5051 
5052  void InitBufferImageUsage(uint32_t bufferImageUsage)
5053  {
5054  VMA_ASSERT(m_BufferImageUsage == 0);
5055  m_BufferImageUsage = bufferImageUsage;
5056  }
5057 
5058  void PrintParameters(class VmaJsonWriter& json) const;
5059 #endif
5060 
5061 private:
5062  VkDeviceSize m_Alignment;
5063  VkDeviceSize m_Size;
5064  void* m_pUserData;
5065  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5066  uint8_t m_Type; // ALLOCATION_TYPE
5067  uint8_t m_SuballocationType; // VmaSuballocationType
5068  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5069  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5070  uint8_t m_MapCount;
5071  uint8_t m_Flags; // enum FLAGS
5072 
5073  // Allocation out of VmaDeviceMemoryBlock.
5074  struct BlockAllocation
5075  {
5076  VmaPool m_hPool; // Null if belongs to general memory.
5077  VmaDeviceMemoryBlock* m_Block;
5078  VkDeviceSize m_Offset;
5079  bool m_CanBecomeLost;
5080  };
5081 
5082  // Allocation for an object that has its own private VkDeviceMemory.
5083  struct DedicatedAllocation
5084  {
5085  uint32_t m_MemoryTypeIndex;
5086  VkDeviceMemory m_hMemory;
5087  void* m_pMappedData; // Not null means memory is mapped.
5088  };
5089 
5090  union
5091  {
5092  // Allocation out of VmaDeviceMemoryBlock.
5093  BlockAllocation m_BlockAllocation;
5094  // Allocation for an object that has its own private VkDeviceMemory.
5095  DedicatedAllocation m_DedicatedAllocation;
5096  };
5097 
5098 #if VMA_STATS_STRING_ENABLED
5099  uint32_t m_CreationFrameIndex;
5100  uint32_t m_BufferImageUsage; // 0 if unknown.
5101 #endif
5102 
5103  void FreeUserDataString(VmaAllocator hAllocator);
5104 };
5105 
5106 /*
5107 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5108 allocated memory block or free.
5109 */
5110 struct VmaSuballocation
5111 {
5112  VkDeviceSize offset;
5113  VkDeviceSize size;
5114  VmaAllocation hAllocation;
5115  VmaSuballocationType type;
5116 };
5117 
5118 // Comparator for offsets.
5119 struct VmaSuballocationOffsetLess
5120 {
5121  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5122  {
5123  return lhs.offset < rhs.offset;
5124  }
5125 };
5126 struct VmaSuballocationOffsetGreater
5127 {
5128  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5129  {
5130  return lhs.offset > rhs.offset;
5131  }
5132 };
5133 
5134 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5135 
5136 // Cost of one additional allocation lost, as equivalent in bytes.
5137 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5138 
5139 /*
5140 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5141 
5142 If canMakeOtherLost was false:
5143 - item points to a FREE suballocation.
5144 - itemsToMakeLostCount is 0.
5145 
5146 If canMakeOtherLost was true:
5147 - item points to first of sequence of suballocations, which are either FREE,
5148  or point to VmaAllocations that can become lost.
5149 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5150  the requested allocation to succeed.
5151 */
5152 struct VmaAllocationRequest
5153 {
5154  VkDeviceSize offset;
5155  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5156  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5157  VmaSuballocationList::iterator item;
5158  size_t itemsToMakeLostCount;
5159  void* customData;
5160 
5161  VkDeviceSize CalcCost() const
5162  {
5163  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5164  }
5165 };
5166 
5167 /*
5168 Data structure used for bookkeeping of allocations and unused ranges of memory
5169 in a single VkDeviceMemory block.
5170 */
5171 class VmaBlockMetadata
5172 {
5173 public:
5174  VmaBlockMetadata(VmaAllocator hAllocator);
5175  virtual ~VmaBlockMetadata() { }
5176  virtual void Init(VkDeviceSize size) { m_Size = size; }
5177 
5178  // Validates all data structures inside this object. If not valid, returns false.
5179  virtual bool Validate() const = 0;
5180  VkDeviceSize GetSize() const { return m_Size; }
5181  virtual size_t GetAllocationCount() const = 0;
5182  virtual VkDeviceSize GetSumFreeSize() const = 0;
5183  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5184  // Returns true if this block is empty - contains only single free suballocation.
5185  virtual bool IsEmpty() const = 0;
5186 
5187  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5188  // Shouldn't modify blockCount.
5189  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5190 
5191 #if VMA_STATS_STRING_ENABLED
5192  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5193 #endif
5194 
5195  // Tries to find a place for suballocation with given parameters inside this block.
5196  // If succeeded, fills pAllocationRequest and returns true.
5197  // If failed, returns false.
5198  virtual bool CreateAllocationRequest(
5199  uint32_t currentFrameIndex,
5200  uint32_t frameInUseCount,
5201  VkDeviceSize bufferImageGranularity,
5202  VkDeviceSize allocSize,
5203  VkDeviceSize allocAlignment,
5204  bool upperAddress,
5205  VmaSuballocationType allocType,
5206  bool canMakeOtherLost,
5207  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5208  uint32_t strategy,
5209  VmaAllocationRequest* pAllocationRequest) = 0;
5210 
5211  virtual bool MakeRequestedAllocationsLost(
5212  uint32_t currentFrameIndex,
5213  uint32_t frameInUseCount,
5214  VmaAllocationRequest* pAllocationRequest) = 0;
5215 
5216  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5217 
5218  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5219 
5220  // Makes actual allocation based on request. Request must already be checked and valid.
5221  virtual void Alloc(
5222  const VmaAllocationRequest& request,
5223  VmaSuballocationType type,
5224  VkDeviceSize allocSize,
5225  bool upperAddress,
5226  VmaAllocation hAllocation) = 0;
5227 
5228  // Frees suballocation assigned to given memory region.
5229  virtual void Free(const VmaAllocation allocation) = 0;
5230  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5231 
5232  // Tries to resize (grow or shrink) space for given allocation, in place.
5233  virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5234 
5235 protected:
5236  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5237 
5238 #if VMA_STATS_STRING_ENABLED
5239  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5240  VkDeviceSize unusedBytes,
5241  size_t allocationCount,
5242  size_t unusedRangeCount) const;
5243  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5244  VkDeviceSize offset,
5245  VmaAllocation hAllocation) const;
5246  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5247  VkDeviceSize offset,
5248  VkDeviceSize size) const;
5249  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5250 #endif
5251 
5252 private:
5253  VkDeviceSize m_Size;
5254  const VkAllocationCallbacks* m_pAllocationCallbacks;
5255 };
5256 
5257 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5258  VMA_ASSERT(0 && "Validation failed: " #cond); \
5259  return false; \
5260  } } while(false)
5261 
5262 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5263 {
5264  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5265 public:
5266  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5267  virtual ~VmaBlockMetadata_Generic();
5268  virtual void Init(VkDeviceSize size);
5269 
5270  virtual bool Validate() const;
5271  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5272  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5273  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5274  virtual bool IsEmpty() const;
5275 
5276  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5277  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5278 
5279 #if VMA_STATS_STRING_ENABLED
5280  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5281 #endif
5282 
5283  virtual bool CreateAllocationRequest(
5284  uint32_t currentFrameIndex,
5285  uint32_t frameInUseCount,
5286  VkDeviceSize bufferImageGranularity,
5287  VkDeviceSize allocSize,
5288  VkDeviceSize allocAlignment,
5289  bool upperAddress,
5290  VmaSuballocationType allocType,
5291  bool canMakeOtherLost,
5292  uint32_t strategy,
5293  VmaAllocationRequest* pAllocationRequest);
5294 
5295  virtual bool MakeRequestedAllocationsLost(
5296  uint32_t currentFrameIndex,
5297  uint32_t frameInUseCount,
5298  VmaAllocationRequest* pAllocationRequest);
5299 
5300  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5301 
5302  virtual VkResult CheckCorruption(const void* pBlockData);
5303 
5304  virtual void Alloc(
5305  const VmaAllocationRequest& request,
5306  VmaSuballocationType type,
5307  VkDeviceSize allocSize,
5308  bool upperAddress,
5309  VmaAllocation hAllocation);
5310 
5311  virtual void Free(const VmaAllocation allocation);
5312  virtual void FreeAtOffset(VkDeviceSize offset);
5313 
5314  virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5315 
5317  // For defragmentation
5318 
5319  bool IsBufferImageGranularityConflictPossible(
5320  VkDeviceSize bufferImageGranularity,
5321  VmaSuballocationType& inOutPrevSuballocType) const;
5322 
5323 private:
5324  friend class VmaDefragmentationAlgorithm_Generic;
5325  friend class VmaDefragmentationAlgorithm_Fast;
5326 
5327  uint32_t m_FreeCount;
5328  VkDeviceSize m_SumFreeSize;
5329  VmaSuballocationList m_Suballocations;
5330  // Suballocations that are free and have size greater than certain threshold.
5331  // Sorted by size, ascending.
5332  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5333 
5334  bool ValidateFreeSuballocationList() const;
5335 
5336  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5337  // If yes, fills pOffset and returns true. If no, returns false.
5338  bool CheckAllocation(
5339  uint32_t currentFrameIndex,
5340  uint32_t frameInUseCount,
5341  VkDeviceSize bufferImageGranularity,
5342  VkDeviceSize allocSize,
5343  VkDeviceSize allocAlignment,
5344  VmaSuballocationType allocType,
5345  VmaSuballocationList::const_iterator suballocItem,
5346  bool canMakeOtherLost,
5347  VkDeviceSize* pOffset,
5348  size_t* itemsToMakeLostCount,
5349  VkDeviceSize* pSumFreeSize,
5350  VkDeviceSize* pSumItemSize) const;
5351  // Given free suballocation, it merges it with following one, which must also be free.
5352  void MergeFreeWithNext(VmaSuballocationList::iterator item);
5353  // Releases given suballocation, making it free.
5354  // Merges it with adjacent free suballocations if applicable.
5355  // Returns iterator to new free suballocation at this place.
5356  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5357  // Given free suballocation, it inserts it into sorted list of
5358  // m_FreeSuballocationsBySize if it's suitable.
5359  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5360  // Given free suballocation, it removes it from sorted list of
5361  // m_FreeSuballocationsBySize if it's suitable.
5362  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5363 };
5364 
5365 /*
5366 Allocations and their references in internal data structure look like this:
5367 
5368 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5369 
5370  0 +-------+
5371  | |
5372  | |
5373  | |
5374  +-------+
5375  | Alloc | 1st[m_1stNullItemsBeginCount]
5376  +-------+
5377  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5378  +-------+
5379  | ... |
5380  +-------+
5381  | Alloc | 1st[1st.size() - 1]
5382  +-------+
5383  | |
5384  | |
5385  | |
5386 GetSize() +-------+
5387 
5388 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5389 
5390  0 +-------+
5391  | Alloc | 2nd[0]
5392  +-------+
5393  | Alloc | 2nd[1]
5394  +-------+
5395  | ... |
5396  +-------+
5397  | Alloc | 2nd[2nd.size() - 1]
5398  +-------+
5399  | |
5400  | |
5401  | |
5402  +-------+
5403  | Alloc | 1st[m_1stNullItemsBeginCount]
5404  +-------+
5405  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5406  +-------+
5407  | ... |
5408  +-------+
5409  | Alloc | 1st[1st.size() - 1]
5410  +-------+
5411  | |
5412 GetSize() +-------+
5413 
5414 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5415 
5416  0 +-------+
5417  | |
5418  | |
5419  | |
5420  +-------+
5421  | Alloc | 1st[m_1stNullItemsBeginCount]
5422  +-------+
5423  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5424  +-------+
5425  | ... |
5426  +-------+
5427  | Alloc | 1st[1st.size() - 1]
5428  +-------+
5429  | |
5430  | |
5431  | |
5432  +-------+
5433  | Alloc | 2nd[2nd.size() - 1]
5434  +-------+
5435  | ... |
5436  +-------+
5437  | Alloc | 2nd[1]
5438  +-------+
5439  | Alloc | 2nd[0]
5440 GetSize() +-------+
5441 
5442 */
5443 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5444 {
5445  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5446 public:
5447  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5448  virtual ~VmaBlockMetadata_Linear();
5449  virtual void Init(VkDeviceSize size);
5450 
5451  virtual bool Validate() const;
5452  virtual size_t GetAllocationCount() const;
5453  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5454  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5455  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5456 
5457  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5458  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5459 
5460 #if VMA_STATS_STRING_ENABLED
5461  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5462 #endif
5463 
5464  virtual bool CreateAllocationRequest(
5465  uint32_t currentFrameIndex,
5466  uint32_t frameInUseCount,
5467  VkDeviceSize bufferImageGranularity,
5468  VkDeviceSize allocSize,
5469  VkDeviceSize allocAlignment,
5470  bool upperAddress,
5471  VmaSuballocationType allocType,
5472  bool canMakeOtherLost,
5473  uint32_t strategy,
5474  VmaAllocationRequest* pAllocationRequest);
5475 
5476  virtual bool MakeRequestedAllocationsLost(
5477  uint32_t currentFrameIndex,
5478  uint32_t frameInUseCount,
5479  VmaAllocationRequest* pAllocationRequest);
5480 
5481  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5482 
5483  virtual VkResult CheckCorruption(const void* pBlockData);
5484 
5485  virtual void Alloc(
5486  const VmaAllocationRequest& request,
5487  VmaSuballocationType type,
5488  VkDeviceSize allocSize,
5489  bool upperAddress,
5490  VmaAllocation hAllocation);
5491 
5492  virtual void Free(const VmaAllocation allocation);
5493  virtual void FreeAtOffset(VkDeviceSize offset);
5494 
5495 private:
5496  /*
5497  There are two suballocation vectors, used in ping-pong way.
5498  The one with index m_1stVectorIndex is called 1st.
5499  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5500  2nd can be non-empty only when 1st is not empty.
5501  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5502  */
5503  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5504 
5505  enum SECOND_VECTOR_MODE
5506  {
5507  SECOND_VECTOR_EMPTY,
5508  /*
5509  Suballocations in 2nd vector are created later than the ones in 1st, but they
5510  all have smaller offset.
5511  */
5512  SECOND_VECTOR_RING_BUFFER,
5513  /*
5514  Suballocations in 2nd vector are upper side of double stack.
5515  They all have offsets higher than those in 1st vector.
5516  Top of this stack means smaller offsets, but higher indices in this vector.
5517  */
5518  SECOND_VECTOR_DOUBLE_STACK,
5519  };
5520 
5521  VkDeviceSize m_SumFreeSize;
5522  SuballocationVectorType m_Suballocations0, m_Suballocations1;
5523  uint32_t m_1stVectorIndex;
5524  SECOND_VECTOR_MODE m_2ndVectorMode;
5525 
5526  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5527  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5528  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5529  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5530 
5531  // Number of items in 1st vector with hAllocation = null at the beginning.
5532  size_t m_1stNullItemsBeginCount;
5533  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5534  size_t m_1stNullItemsMiddleCount;
5535  // Number of items in 2nd vector with hAllocation = null.
5536  size_t m_2ndNullItemsCount;
5537 
5538  bool ShouldCompact1st() const;
5539  void CleanupAfterFree();
5540 };
5541 
5542 /*
5543 - GetSize() is the original size of allocated memory block.
5544 - m_UsableSize is this size aligned down to a power of two.
5545  All allocations and calculations happen relative to m_UsableSize.
5546 - GetUnusableSize() is the difference between them.
5547  It is repoted as separate, unused range, not available for allocations.
5548 
5549 Node at level 0 has size = m_UsableSize.
5550 Each next level contains nodes with size 2 times smaller than current level.
5551 m_LevelCount is the maximum number of levels to use in the current object.
5552 */
5553 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5554 {
5555  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5556 public:
5557  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5558  virtual ~VmaBlockMetadata_Buddy();
5559  virtual void Init(VkDeviceSize size);
5560 
5561  virtual bool Validate() const;
5562  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5563  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5564  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5565  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5566 
5567  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5568  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5569 
5570 #if VMA_STATS_STRING_ENABLED
5571  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5572 #endif
5573 
5574  virtual bool CreateAllocationRequest(
5575  uint32_t currentFrameIndex,
5576  uint32_t frameInUseCount,
5577  VkDeviceSize bufferImageGranularity,
5578  VkDeviceSize allocSize,
5579  VkDeviceSize allocAlignment,
5580  bool upperAddress,
5581  VmaSuballocationType allocType,
5582  bool canMakeOtherLost,
5583  uint32_t strategy,
5584  VmaAllocationRequest* pAllocationRequest);
5585 
5586  virtual bool MakeRequestedAllocationsLost(
5587  uint32_t currentFrameIndex,
5588  uint32_t frameInUseCount,
5589  VmaAllocationRequest* pAllocationRequest);
5590 
5591  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5592 
5593  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5594 
5595  virtual void Alloc(
5596  const VmaAllocationRequest& request,
5597  VmaSuballocationType type,
5598  VkDeviceSize allocSize,
5599  bool upperAddress,
5600  VmaAllocation hAllocation);
5601 
5602  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
5603  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5604 
5605 private:
5606  static const VkDeviceSize MIN_NODE_SIZE = 32;
5607  static const size_t MAX_LEVELS = 30;
5608 
5609  struct ValidationContext
5610  {
5611  size_t calculatedAllocationCount;
5612  size_t calculatedFreeCount;
5613  VkDeviceSize calculatedSumFreeSize;
5614 
5615  ValidationContext() :
5616  calculatedAllocationCount(0),
5617  calculatedFreeCount(0),
5618  calculatedSumFreeSize(0) { }
5619  };
5620 
5621  struct Node
5622  {
5623  VkDeviceSize offset;
5624  enum TYPE
5625  {
5626  TYPE_FREE,
5627  TYPE_ALLOCATION,
5628  TYPE_SPLIT,
5629  TYPE_COUNT
5630  } type;
5631  Node* parent;
5632  Node* buddy;
5633 
5634  union
5635  {
5636  struct
5637  {
5638  Node* prev;
5639  Node* next;
5640  } free;
5641  struct
5642  {
5643  VmaAllocation alloc;
5644  } allocation;
5645  struct
5646  {
5647  Node* leftChild;
5648  } split;
5649  };
5650  };
5651 
5652  // Size of the memory block aligned down to a power of two.
5653  VkDeviceSize m_UsableSize;
5654  uint32_t m_LevelCount;
5655 
5656  Node* m_Root;
5657  struct {
5658  Node* front;
5659  Node* back;
5660  } m_FreeList[MAX_LEVELS];
5661  // Number of nodes in the tree with type == TYPE_ALLOCATION.
5662  size_t m_AllocationCount;
5663  // Number of nodes in the tree with type == TYPE_FREE.
5664  size_t m_FreeCount;
5665  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5666  VkDeviceSize m_SumFreeSize;
5667 
5668  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5669  void DeleteNode(Node* node);
5670  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5671  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5672  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5673  // Alloc passed just for validation. Can be null.
5674  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5675  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5676  // Adds node to the front of FreeList at given level.
5677  // node->type must be FREE.
5678  // node->free.prev, next can be undefined.
5679  void AddToFreeListFront(uint32_t level, Node* node);
5680  // Removes node from FreeList at given level.
5681  // node->type must be FREE.
5682  // node->free.prev, next stay untouched.
5683  void RemoveFromFreeList(uint32_t level, Node* node);
5684 
5685 #if VMA_STATS_STRING_ENABLED
5686  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5687 #endif
5688 };
5689 
5690 /*
5691 Represents a single block of device memory (`VkDeviceMemory`) with all the
5692 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5693 
5694 Thread-safety: This class must be externally synchronized.
5695 */
5696 class VmaDeviceMemoryBlock
5697 {
5698  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5699 public:
5700  VmaBlockMetadata* m_pMetadata;
5701 
5702  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5703 
5704  ~VmaDeviceMemoryBlock()
5705  {
5706  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5707  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5708  }
5709 
5710  // Always call after construction.
5711  void Init(
5712  VmaAllocator hAllocator,
5713  uint32_t newMemoryTypeIndex,
5714  VkDeviceMemory newMemory,
5715  VkDeviceSize newSize,
5716  uint32_t id,
5717  uint32_t algorithm);
5718  // Always call before destruction.
5719  void Destroy(VmaAllocator allocator);
5720 
5721  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5722  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5723  uint32_t GetId() const { return m_Id; }
5724  void* GetMappedData() const { return m_pMappedData; }
5725 
5726  // Validates all data structures inside this object. If not valid, returns false.
5727  bool Validate() const;
5728 
5729  VkResult CheckCorruption(VmaAllocator hAllocator);
5730 
5731  // ppData can be null.
5732  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5733  void Unmap(VmaAllocator hAllocator, uint32_t count);
5734 
5735  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5736  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5737 
5738  VkResult BindBufferMemory(
5739  const VmaAllocator hAllocator,
5740  const VmaAllocation hAllocation,
5741  VkBuffer hBuffer);
5742  VkResult BindImageMemory(
5743  const VmaAllocator hAllocator,
5744  const VmaAllocation hAllocation,
5745  VkImage hImage);
5746 
5747 private:
5748  uint32_t m_MemoryTypeIndex;
5749  uint32_t m_Id;
5750  VkDeviceMemory m_hMemory;
5751 
5752  /*
5753  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5754  Also protects m_MapCount, m_pMappedData.
5755  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5756  */
5757  VMA_MUTEX m_Mutex;
5758  uint32_t m_MapCount;
5759  void* m_pMappedData;
5760 };
5761 
5762 struct VmaPointerLess
5763 {
5764  bool operator()(const void* lhs, const void* rhs) const
5765  {
5766  return lhs < rhs;
5767  }
5768 };
5769 
5770 struct VmaDefragmentationMove
5771 {
5772  size_t srcBlockIndex;
5773  size_t dstBlockIndex;
5774  VkDeviceSize srcOffset;
5775  VkDeviceSize dstOffset;
5776  VkDeviceSize size;
5777 };
5778 
5779 class VmaDefragmentationAlgorithm;
5780 
5781 /*
5782 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5783 Vulkan memory type.
5784 
5785 Synchronized internally with a mutex.
5786 */
5787 struct VmaBlockVector
5788 {
5789  VMA_CLASS_NO_COPY(VmaBlockVector)
5790 public:
5791  VmaBlockVector(
5792  VmaAllocator hAllocator,
5793  uint32_t memoryTypeIndex,
5794  VkDeviceSize preferredBlockSize,
5795  size_t minBlockCount,
5796  size_t maxBlockCount,
5797  VkDeviceSize bufferImageGranularity,
5798  uint32_t frameInUseCount,
5799  bool isCustomPool,
5800  bool explicitBlockSize,
5801  uint32_t algorithm);
5802  ~VmaBlockVector();
5803 
5804  VkResult CreateMinBlocks();
5805 
5806  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5807  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5808  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5809  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5810  uint32_t GetAlgorithm() const { return m_Algorithm; }
5811 
5812  void GetPoolStats(VmaPoolStats* pStats);
5813 
5814  bool IsEmpty() const { return m_Blocks.empty(); }
5815  bool IsCorruptionDetectionEnabled() const;
5816 
5817  VkResult Allocate(
5818  VmaPool hCurrentPool,
5819  uint32_t currentFrameIndex,
5820  VkDeviceSize size,
5821  VkDeviceSize alignment,
5822  const VmaAllocationCreateInfo& createInfo,
5823  VmaSuballocationType suballocType,
5824  size_t allocationCount,
5825  VmaAllocation* pAllocations);
5826 
5827  void Free(
5828  VmaAllocation hAllocation);
5829 
5830  // Adds statistics of this BlockVector to pStats.
5831  void AddStats(VmaStats* pStats);
5832 
5833 #if VMA_STATS_STRING_ENABLED
5834  void PrintDetailedMap(class VmaJsonWriter& json);
5835 #endif
5836 
5837  void MakePoolAllocationsLost(
5838  uint32_t currentFrameIndex,
5839  size_t* pLostAllocationCount);
5840  VkResult CheckCorruption();
5841 
5842  // Saves results in pCtx->res.
5843  void Defragment(
5844  class VmaBlockVectorDefragmentationContext* pCtx,
5845  VmaDefragmentationStats* pStats,
5846  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5847  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5848  VkCommandBuffer commandBuffer);
5849  void DefragmentationEnd(
5850  class VmaBlockVectorDefragmentationContext* pCtx,
5851  VmaDefragmentationStats* pStats);
5852 
5854  // To be used only while the m_Mutex is locked. Used during defragmentation.
5855 
5856  size_t GetBlockCount() const { return m_Blocks.size(); }
5857  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5858  size_t CalcAllocationCount() const;
5859  bool IsBufferImageGranularityConflictPossible() const;
5860 
5861 private:
5862  friend class VmaDefragmentationAlgorithm_Generic;
5863 
5864  const VmaAllocator m_hAllocator;
5865  const uint32_t m_MemoryTypeIndex;
5866  const VkDeviceSize m_PreferredBlockSize;
5867  const size_t m_MinBlockCount;
5868  const size_t m_MaxBlockCount;
5869  const VkDeviceSize m_BufferImageGranularity;
5870  const uint32_t m_FrameInUseCount;
5871  const bool m_IsCustomPool;
5872  const bool m_ExplicitBlockSize;
5873  const uint32_t m_Algorithm;
5874  /* There can be at most one allocation that is completely empty - a
5875  hysteresis to avoid pessimistic case of alternating creation and destruction
5876  of a VkDeviceMemory. */
5877  bool m_HasEmptyBlock;
5878  VMA_RW_MUTEX m_Mutex;
5879  // Incrementally sorted by sumFreeSize, ascending.
5880  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5881  uint32_t m_NextBlockId;
5882 
5883  VkDeviceSize CalcMaxBlockSize() const;
5884 
5885  // Finds and removes given block from vector.
5886  void Remove(VmaDeviceMemoryBlock* pBlock);
5887 
5888  // Performs single step in sorting m_Blocks. They may not be fully sorted
5889  // after this call.
5890  void IncrementallySortBlocks();
5891 
5892  VkResult AllocatePage(
5893  VmaPool hCurrentPool,
5894  uint32_t currentFrameIndex,
5895  VkDeviceSize size,
5896  VkDeviceSize alignment,
5897  const VmaAllocationCreateInfo& createInfo,
5898  VmaSuballocationType suballocType,
5899  VmaAllocation* pAllocation);
5900 
5901  // To be used only without CAN_MAKE_OTHER_LOST flag.
5902  VkResult AllocateFromBlock(
5903  VmaDeviceMemoryBlock* pBlock,
5904  VmaPool hCurrentPool,
5905  uint32_t currentFrameIndex,
5906  VkDeviceSize size,
5907  VkDeviceSize alignment,
5908  VmaAllocationCreateFlags allocFlags,
5909  void* pUserData,
5910  VmaSuballocationType suballocType,
5911  uint32_t strategy,
5912  VmaAllocation* pAllocation);
5913 
5914  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5915 
5916  // Saves result to pCtx->res.
5917  void ApplyDefragmentationMovesCpu(
5918  class VmaBlockVectorDefragmentationContext* pDefragCtx,
5919  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5920  // Saves result to pCtx->res.
5921  void ApplyDefragmentationMovesGpu(
5922  class VmaBlockVectorDefragmentationContext* pDefragCtx,
5923  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5924  VkCommandBuffer commandBuffer);
5925 
5926  /*
5927  Used during defragmentation. pDefragmentationStats is optional. It's in/out
5928  - updated with new data.
5929  */
5930  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5931 };
5932 
5933 struct VmaPool_T
5934 {
5935  VMA_CLASS_NO_COPY(VmaPool_T)
5936 public:
5937  VmaBlockVector m_BlockVector;
5938 
5939  VmaPool_T(
5940  VmaAllocator hAllocator,
5941  const VmaPoolCreateInfo& createInfo,
5942  VkDeviceSize preferredBlockSize);
5943  ~VmaPool_T();
5944 
5945  uint32_t GetId() const { return m_Id; }
5946  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5947 
5948 #if VMA_STATS_STRING_ENABLED
5949  //void PrintDetailedMap(class VmaStringBuilder& sb);
5950 #endif
5951 
5952 private:
5953  uint32_t m_Id;
5954 };
5955 
5956 /*
5957 Performs defragmentation:
5958 
5959 - Updates `pBlockVector->m_pMetadata`.
5960 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5961 - Does not move actual data, only returns requested moves as `moves`.
5962 */
5963 class VmaDefragmentationAlgorithm
5964 {
5965  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5966 public:
5967  VmaDefragmentationAlgorithm(
5968  VmaAllocator hAllocator,
5969  VmaBlockVector* pBlockVector,
5970  uint32_t currentFrameIndex) :
5971  m_hAllocator(hAllocator),
5972  m_pBlockVector(pBlockVector),
5973  m_CurrentFrameIndex(currentFrameIndex)
5974  {
5975  }
5976  virtual ~VmaDefragmentationAlgorithm()
5977  {
5978  }
5979 
5980  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5981  virtual void AddAll() = 0;
5982 
5983  virtual VkResult Defragment(
5984  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5985  VkDeviceSize maxBytesToMove,
5986  uint32_t maxAllocationsToMove) = 0;
5987 
5988  virtual VkDeviceSize GetBytesMoved() const = 0;
5989  virtual uint32_t GetAllocationsMoved() const = 0;
5990 
5991 protected:
5992  VmaAllocator const m_hAllocator;
5993  VmaBlockVector* const m_pBlockVector;
5994  const uint32_t m_CurrentFrameIndex;
5995 
5996  struct AllocationInfo
5997  {
5998  VmaAllocation m_hAllocation;
5999  VkBool32* m_pChanged;
6000 
6001  AllocationInfo() :
6002  m_hAllocation(VK_NULL_HANDLE),
6003  m_pChanged(VMA_NULL)
6004  {
6005  }
6006  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6007  m_hAllocation(hAlloc),
6008  m_pChanged(pChanged)
6009  {
6010  }
6011  };
6012 };
6013 
6014 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6015 {
6016  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6017 public:
6018  VmaDefragmentationAlgorithm_Generic(
6019  VmaAllocator hAllocator,
6020  VmaBlockVector* pBlockVector,
6021  uint32_t currentFrameIndex,
6022  bool overlappingMoveSupported);
6023  virtual ~VmaDefragmentationAlgorithm_Generic();
6024 
6025  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6026  virtual void AddAll() { m_AllAllocations = true; }
6027 
6028  virtual VkResult Defragment(
6029  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6030  VkDeviceSize maxBytesToMove,
6031  uint32_t maxAllocationsToMove);
6032 
6033  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6034  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6035 
6036 private:
6037  uint32_t m_AllocationCount;
6038  bool m_AllAllocations;
6039 
6040  VkDeviceSize m_BytesMoved;
6041  uint32_t m_AllocationsMoved;
6042 
6043  struct AllocationInfoSizeGreater
6044  {
6045  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6046  {
6047  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6048  }
6049  };
6050 
6051  struct AllocationInfoOffsetGreater
6052  {
6053  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6054  {
6055  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6056  }
6057  };
6058 
6059  struct BlockInfo
6060  {
6061  size_t m_OriginalBlockIndex;
6062  VmaDeviceMemoryBlock* m_pBlock;
6063  bool m_HasNonMovableAllocations;
6064  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6065 
6066  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6067  m_OriginalBlockIndex(SIZE_MAX),
6068  m_pBlock(VMA_NULL),
6069  m_HasNonMovableAllocations(true),
6070  m_Allocations(pAllocationCallbacks)
6071  {
6072  }
6073 
6074  void CalcHasNonMovableAllocations()
6075  {
6076  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6077  const size_t defragmentAllocCount = m_Allocations.size();
6078  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6079  }
6080 
6081  void SortAllocationsBySizeDescending()
6082  {
6083  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6084  }
6085 
6086  void SortAllocationsByOffsetDescending()
6087  {
6088  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6089  }
6090  };
6091 
6092  struct BlockPointerLess
6093  {
6094  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6095  {
6096  return pLhsBlockInfo->m_pBlock < pRhsBlock;
6097  }
6098  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6099  {
6100  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6101  }
6102  };
6103 
6104  // 1. Blocks with some non-movable allocations go first.
6105  // 2. Blocks with smaller sumFreeSize go first.
6106  struct BlockInfoCompareMoveDestination
6107  {
6108  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6109  {
6110  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6111  {
6112  return true;
6113  }
6114  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6115  {
6116  return false;
6117  }
6118  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6119  {
6120  return true;
6121  }
6122  return false;
6123  }
6124  };
6125 
6126  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6127  BlockInfoVector m_Blocks;
6128 
6129  VkResult DefragmentRound(
6130  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6131  VkDeviceSize maxBytesToMove,
6132  uint32_t maxAllocationsToMove);
6133 
6134  size_t CalcBlocksWithNonMovableCount() const;
6135 
6136  static bool MoveMakesSense(
6137  size_t dstBlockIndex, VkDeviceSize dstOffset,
6138  size_t srcBlockIndex, VkDeviceSize srcOffset);
6139 };
6140 
6141 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6142 {
6143  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6144 public:
6145  VmaDefragmentationAlgorithm_Fast(
6146  VmaAllocator hAllocator,
6147  VmaBlockVector* pBlockVector,
6148  uint32_t currentFrameIndex,
6149  bool overlappingMoveSupported);
6150  virtual ~VmaDefragmentationAlgorithm_Fast();
6151 
6152  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
6153  virtual void AddAll() { m_AllAllocations = true; }
6154 
6155  virtual VkResult Defragment(
6156  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6157  VkDeviceSize maxBytesToMove,
6158  uint32_t maxAllocationsToMove);
6159 
6160  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6161  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6162 
6163 private:
6164  struct BlockInfo
6165  {
6166  size_t origBlockIndex;
6167  };
6168 
6169  class FreeSpaceDatabase
6170  {
6171  public:
6172  FreeSpaceDatabase()
6173  {
6174  FreeSpace s = {};
6175  s.blockInfoIndex = SIZE_MAX;
6176  for(size_t i = 0; i < MAX_COUNT; ++i)
6177  {
6178  m_FreeSpaces[i] = s;
6179  }
6180  }
6181 
6182  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6183  {
6184  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6185  {
6186  return;
6187  }
6188 
6189  // Find first invalid or the smallest structure.
6190  size_t bestIndex = SIZE_MAX;
6191  for(size_t i = 0; i < MAX_COUNT; ++i)
6192  {
6193  // Empty structure.
6194  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6195  {
6196  bestIndex = i;
6197  break;
6198  }
6199  if(m_FreeSpaces[i].size < size &&
6200  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6201  {
6202  bestIndex = i;
6203  }
6204  }
6205 
6206  if(bestIndex != SIZE_MAX)
6207  {
6208  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6209  m_FreeSpaces[bestIndex].offset = offset;
6210  m_FreeSpaces[bestIndex].size = size;
6211  }
6212  }
6213 
6214  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6215  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6216  {
6217  size_t bestIndex = SIZE_MAX;
6218  VkDeviceSize bestFreeSpaceAfter = 0;
6219  for(size_t i = 0; i < MAX_COUNT; ++i)
6220  {
6221  // Structure is valid.
6222  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6223  {
6224  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6225  // Allocation fits into this structure.
6226  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6227  {
6228  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6229  (dstOffset + size);
6230  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6231  {
6232  bestIndex = i;
6233  bestFreeSpaceAfter = freeSpaceAfter;
6234  }
6235  }
6236  }
6237  }
6238 
6239  if(bestIndex != SIZE_MAX)
6240  {
6241  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6242  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6243 
6244  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6245  {
6246  // Leave this structure for remaining empty space.
6247  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6248  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6249  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6250  }
6251  else
6252  {
6253  // This structure becomes invalid.
6254  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6255  }
6256 
6257  return true;
6258  }
6259 
6260  return false;
6261  }
6262 
6263  private:
6264  static const size_t MAX_COUNT = 4;
6265 
6266  struct FreeSpace
6267  {
6268  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6269  VkDeviceSize offset;
6270  VkDeviceSize size;
6271  } m_FreeSpaces[MAX_COUNT];
6272  };
6273 
6274  const bool m_OverlappingMoveSupported;
6275 
6276  uint32_t m_AllocationCount;
6277  bool m_AllAllocations;
6278 
6279  VkDeviceSize m_BytesMoved;
6280  uint32_t m_AllocationsMoved;
6281 
6282  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6283 
6284  void PreprocessMetadata();
6285  void PostprocessMetadata();
6286  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6287 };
6288 
6289 struct VmaBlockDefragmentationContext
6290 {
6291  enum BLOCK_FLAG
6292  {
6293  BLOCK_FLAG_USED = 0x00000001,
6294  };
6295  uint32_t flags;
6296  VkBuffer hBuffer;
6297 
6298  VmaBlockDefragmentationContext() :
6299  flags(0),
6300  hBuffer(VK_NULL_HANDLE)
6301  {
6302  }
6303 };
6304 
6305 class VmaBlockVectorDefragmentationContext
6306 {
6307  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6308 public:
6309  VkResult res;
6310  bool mutexLocked;
6311  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6312 
6313  VmaBlockVectorDefragmentationContext(
6314  VmaAllocator hAllocator,
6315  VmaPool hCustomPool, // Optional.
6316  VmaBlockVector* pBlockVector,
6317  uint32_t currFrameIndex,
6318  uint32_t flags);
6319  ~VmaBlockVectorDefragmentationContext();
6320 
6321  VmaPool GetCustomPool() const { return m_hCustomPool; }
6322  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6323  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6324 
6325  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6326  void AddAll() { m_AllAllocations = true; }
6327 
6328  void Begin(bool overlappingMoveSupported);
6329 
6330 private:
6331  const VmaAllocator m_hAllocator;
6332  // Null if not from custom pool.
6333  const VmaPool m_hCustomPool;
6334  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6335  VmaBlockVector* const m_pBlockVector;
6336  const uint32_t m_CurrFrameIndex;
6337  const uint32_t m_AlgorithmFlags;
6338  // Owner of this object.
6339  VmaDefragmentationAlgorithm* m_pAlgorithm;
6340 
6341  struct AllocInfo
6342  {
6343  VmaAllocation hAlloc;
6344  VkBool32* pChanged;
6345  };
6346  // Used between constructor and Begin.
6347  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6348  bool m_AllAllocations;
6349 };
6350 
6351 struct VmaDefragmentationContext_T
6352 {
6353 private:
6354  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6355 public:
6356  VmaDefragmentationContext_T(
6357  VmaAllocator hAllocator,
6358  uint32_t currFrameIndex,
6359  uint32_t flags,
6360  VmaDefragmentationStats* pStats);
6361  ~VmaDefragmentationContext_T();
6362 
6363  void AddPools(uint32_t poolCount, VmaPool* pPools);
6364  void AddAllocations(
6365  uint32_t allocationCount,
6366  VmaAllocation* pAllocations,
6367  VkBool32* pAllocationsChanged);
6368 
6369  /*
6370  Returns:
6371  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6372  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6373  - Negative value if error occured and object can be destroyed immediately.
6374  */
6375  VkResult Defragment(
6376  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6377  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6378  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6379 
6380 private:
6381  const VmaAllocator m_hAllocator;
6382  const uint32_t m_CurrFrameIndex;
6383  const uint32_t m_Flags;
6384  VmaDefragmentationStats* const m_pStats;
6385  // Owner of these objects.
6386  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6387  // Owner of these objects.
6388  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6389 };
6390 
6391 #if VMA_RECORDING_ENABLED
6392 
6393 class VmaRecorder
6394 {
6395 public:
6396  VmaRecorder();
6397  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6398  void WriteConfiguration(
6399  const VkPhysicalDeviceProperties& devProps,
6400  const VkPhysicalDeviceMemoryProperties& memProps,
6401  bool dedicatedAllocationExtensionEnabled);
6402  ~VmaRecorder();
6403 
6404  void RecordCreateAllocator(uint32_t frameIndex);
6405  void RecordDestroyAllocator(uint32_t frameIndex);
6406  void RecordCreatePool(uint32_t frameIndex,
6407  const VmaPoolCreateInfo& createInfo,
6408  VmaPool pool);
6409  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6410  void RecordAllocateMemory(uint32_t frameIndex,
6411  const VkMemoryRequirements& vkMemReq,
6412  const VmaAllocationCreateInfo& createInfo,
6413  VmaAllocation allocation);
6414  void RecordAllocateMemoryPages(uint32_t frameIndex,
6415  const VkMemoryRequirements& vkMemReq,
6416  const VmaAllocationCreateInfo& createInfo,
6417  uint64_t allocationCount,
6418  const VmaAllocation* pAllocations);
6419  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6420  const VkMemoryRequirements& vkMemReq,
6421  bool requiresDedicatedAllocation,
6422  bool prefersDedicatedAllocation,
6423  const VmaAllocationCreateInfo& createInfo,
6424  VmaAllocation allocation);
6425  void RecordAllocateMemoryForImage(uint32_t frameIndex,
6426  const VkMemoryRequirements& vkMemReq,
6427  bool requiresDedicatedAllocation,
6428  bool prefersDedicatedAllocation,
6429  const VmaAllocationCreateInfo& createInfo,
6430  VmaAllocation allocation);
6431  void RecordFreeMemory(uint32_t frameIndex,
6432  VmaAllocation allocation);
6433  void RecordFreeMemoryPages(uint32_t frameIndex,
6434  uint64_t allocationCount,
6435  const VmaAllocation* pAllocations);
6436  void RecordResizeAllocation(
6437  uint32_t frameIndex,
6438  VmaAllocation allocation,
6439  VkDeviceSize newSize);
6440  void RecordSetAllocationUserData(uint32_t frameIndex,
6441  VmaAllocation allocation,
6442  const void* pUserData);
6443  void RecordCreateLostAllocation(uint32_t frameIndex,
6444  VmaAllocation allocation);
6445  void RecordMapMemory(uint32_t frameIndex,
6446  VmaAllocation allocation);
6447  void RecordUnmapMemory(uint32_t frameIndex,
6448  VmaAllocation allocation);
6449  void RecordFlushAllocation(uint32_t frameIndex,
6450  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6451  void RecordInvalidateAllocation(uint32_t frameIndex,
6452  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6453  void RecordCreateBuffer(uint32_t frameIndex,
6454  const VkBufferCreateInfo& bufCreateInfo,
6455  const VmaAllocationCreateInfo& allocCreateInfo,
6456  VmaAllocation allocation);
6457  void RecordCreateImage(uint32_t frameIndex,
6458  const VkImageCreateInfo& imageCreateInfo,
6459  const VmaAllocationCreateInfo& allocCreateInfo,
6460  VmaAllocation allocation);
6461  void RecordDestroyBuffer(uint32_t frameIndex,
6462  VmaAllocation allocation);
6463  void RecordDestroyImage(uint32_t frameIndex,
6464  VmaAllocation allocation);
6465  void RecordTouchAllocation(uint32_t frameIndex,
6466  VmaAllocation allocation);
6467  void RecordGetAllocationInfo(uint32_t frameIndex,
6468  VmaAllocation allocation);
6469  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6470  VmaPool pool);
6471  void RecordDefragmentationBegin(uint32_t frameIndex,
6472  const VmaDefragmentationInfo2& info,
6474  void RecordDefragmentationEnd(uint32_t frameIndex,
6476 
6477 private:
6478  struct CallParams
6479  {
6480  uint32_t threadId;
6481  double time;
6482  };
6483 
6484  class UserDataString
6485  {
6486  public:
6487  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6488  const char* GetString() const { return m_Str; }
6489 
6490  private:
6491  char m_PtrStr[17];
6492  const char* m_Str;
6493  };
6494 
6495  bool m_UseMutex;
6496  VmaRecordFlags m_Flags;
6497  FILE* m_File;
6498  VMA_MUTEX m_FileMutex;
6499  int64_t m_Freq;
6500  int64_t m_StartCounter;
6501 
6502  void GetBasicParams(CallParams& outParams);
6503 
6504  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6505  template<typename T>
6506  void PrintPointerList(uint64_t count, const T* pItems)
6507  {
6508  if(count)
6509  {
6510  fprintf(m_File, "%p", pItems[0]);
6511  for(uint64_t i = 1; i < count; ++i)
6512  {
6513  fprintf(m_File, " %p", pItems[i]);
6514  }
6515  }
6516  }
6517 
6518  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6519  void Flush();
6520 };
6521 
6522 #endif // #if VMA_RECORDING_ENABLED
6523 
6524 // Main allocator object.
6525 struct VmaAllocator_T
6526 {
6527  VMA_CLASS_NO_COPY(VmaAllocator_T)
6528 public:
6529  bool m_UseMutex;
6530  bool m_UseKhrDedicatedAllocation;
6531  VkDevice m_hDevice;
6532  bool m_AllocationCallbacksSpecified;
6533  VkAllocationCallbacks m_AllocationCallbacks;
6534  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6535 
6536  // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6537  VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6538  VMA_MUTEX m_HeapSizeLimitMutex;
6539 
6540  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6541  VkPhysicalDeviceMemoryProperties m_MemProps;
6542 
6543  // Default pools.
6544  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6545 
6546  // Each vector is sorted by memory (handle value).
6547  typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6548  AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6549  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6550 
6551  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6552  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6553  ~VmaAllocator_T();
6554 
6555  const VkAllocationCallbacks* GetAllocationCallbacks() const
6556  {
6557  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6558  }
6559  const VmaVulkanFunctions& GetVulkanFunctions() const
6560  {
6561  return m_VulkanFunctions;
6562  }
6563 
6564  VkDeviceSize GetBufferImageGranularity() const
6565  {
6566  return VMA_MAX(
6567  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6568  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6569  }
6570 
6571  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6572  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6573 
6574  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6575  {
6576  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6577  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6578  }
6579  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6580  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6581  {
6582  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6583  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6584  }
6585  // Minimum alignment for all allocations in specific memory type.
6586  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6587  {
6588  return IsMemoryTypeNonCoherent(memTypeIndex) ?
6589  VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6590  (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6591  }
6592 
6593  bool IsIntegratedGpu() const
6594  {
6595  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6596  }
6597 
6598 #if VMA_RECORDING_ENABLED
6599  VmaRecorder* GetRecorder() const { return m_pRecorder; }
6600 #endif
6601 
6602  void GetBufferMemoryRequirements(
6603  VkBuffer hBuffer,
6604  VkMemoryRequirements& memReq,
6605  bool& requiresDedicatedAllocation,
6606  bool& prefersDedicatedAllocation) const;
6607  void GetImageMemoryRequirements(
6608  VkImage hImage,
6609  VkMemoryRequirements& memReq,
6610  bool& requiresDedicatedAllocation,
6611  bool& prefersDedicatedAllocation) const;
6612 
6613  // Main allocation function.
6614  VkResult AllocateMemory(
6615  const VkMemoryRequirements& vkMemReq,
6616  bool requiresDedicatedAllocation,
6617  bool prefersDedicatedAllocation,
6618  VkBuffer dedicatedBuffer,
6619  VkImage dedicatedImage,
6620  const VmaAllocationCreateInfo& createInfo,
6621  VmaSuballocationType suballocType,
6622  size_t allocationCount,
6623  VmaAllocation* pAllocations);
6624 
6625  // Main deallocation function.
6626  void FreeMemory(
6627  size_t allocationCount,
6628  const VmaAllocation* pAllocations);
6629 
6630  VkResult ResizeAllocation(
6631  const VmaAllocation alloc,
6632  VkDeviceSize newSize);
6633 
6634  void CalculateStats(VmaStats* pStats);
6635 
6636 #if VMA_STATS_STRING_ENABLED
6637  void PrintDetailedMap(class VmaJsonWriter& json);
6638 #endif
6639 
6640  VkResult DefragmentationBegin(
6641  const VmaDefragmentationInfo2& info,
6642  VmaDefragmentationStats* pStats,
6643  VmaDefragmentationContext* pContext);
6644  VkResult DefragmentationEnd(
6645  VmaDefragmentationContext context);
6646 
6647  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6648  bool TouchAllocation(VmaAllocation hAllocation);
6649 
6650  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6651  void DestroyPool(VmaPool pool);
6652  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6653 
6654  void SetCurrentFrameIndex(uint32_t frameIndex);
6655  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6656 
6657  void MakePoolAllocationsLost(
6658  VmaPool hPool,
6659  size_t* pLostAllocationCount);
6660  VkResult CheckPoolCorruption(VmaPool hPool);
6661  VkResult CheckCorruption(uint32_t memoryTypeBits);
6662 
6663  void CreateLostAllocation(VmaAllocation* pAllocation);
6664 
6665  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6666  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6667 
6668  VkResult Map(VmaAllocation hAllocation, void** ppData);
6669  void Unmap(VmaAllocation hAllocation);
6670 
6671  VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6672  VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6673 
6674  void FlushOrInvalidateAllocation(
6675  VmaAllocation hAllocation,
6676  VkDeviceSize offset, VkDeviceSize size,
6677  VMA_CACHE_OPERATION op);
6678 
6679  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6680 
6681 private:
6682  VkDeviceSize m_PreferredLargeHeapBlockSize;
6683 
6684  VkPhysicalDevice m_PhysicalDevice;
6685  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6686 
6687  VMA_RW_MUTEX m_PoolsMutex;
6688  // Protected by m_PoolsMutex. Sorted by pointer value.
6689  VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6690  uint32_t m_NextPoolId;
6691 
6692  VmaVulkanFunctions m_VulkanFunctions;
6693 
6694 #if VMA_RECORDING_ENABLED
6695  VmaRecorder* m_pRecorder;
6696 #endif
6697 
6698  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6699 
6700  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6701 
6702  VkResult AllocateMemoryOfType(
6703  VkDeviceSize size,
6704  VkDeviceSize alignment,
6705  bool dedicatedAllocation,
6706  VkBuffer dedicatedBuffer,
6707  VkImage dedicatedImage,
6708  const VmaAllocationCreateInfo& createInfo,
6709  uint32_t memTypeIndex,
6710  VmaSuballocationType suballocType,
6711  size_t allocationCount,
6712  VmaAllocation* pAllocations);
6713 
6714  // Helper function only to be used inside AllocateDedicatedMemory.
6715  VkResult AllocateDedicatedMemoryPage(
6716  VkDeviceSize size,
6717  VmaSuballocationType suballocType,
6718  uint32_t memTypeIndex,
6719  const VkMemoryAllocateInfo& allocInfo,
6720  bool map,
6721  bool isUserDataString,
6722  void* pUserData,
6723  VmaAllocation* pAllocation);
6724 
6725  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6726  VkResult AllocateDedicatedMemory(
6727  VkDeviceSize size,
6728  VmaSuballocationType suballocType,
6729  uint32_t memTypeIndex,
6730  bool map,
6731  bool isUserDataString,
6732  void* pUserData,
6733  VkBuffer dedicatedBuffer,
6734  VkImage dedicatedImage,
6735  size_t allocationCount,
6736  VmaAllocation* pAllocations);
6737 
6738  // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6739  void FreeDedicatedMemory(VmaAllocation allocation);
6740 };
6741 
6743 // Memory allocation #2 after VmaAllocator_T definition
6744 
6745 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6746 {
6747  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6748 }
6749 
6750 static void VmaFree(VmaAllocator hAllocator, void* ptr)
6751 {
6752  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6753 }
6754 
6755 template<typename T>
6756 static T* VmaAllocate(VmaAllocator hAllocator)
6757 {
6758  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6759 }
6760 
6761 template<typename T>
6762 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6763 {
6764  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6765 }
6766 
6767 template<typename T>
6768 static void vma_delete(VmaAllocator hAllocator, T* ptr)
6769 {
6770  if(ptr != VMA_NULL)
6771  {
6772  ptr->~T();
6773  VmaFree(hAllocator, ptr);
6774  }
6775 }
6776 
6777 template<typename T>
6778 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6779 {
6780  if(ptr != VMA_NULL)
6781  {
6782  for(size_t i = count; i--; )
6783  ptr[i].~T();
6784  VmaFree(hAllocator, ptr);
6785  }
6786 }
6787 
6789 // VmaStringBuilder
6790 
6791 #if VMA_STATS_STRING_ENABLED
6792 
6793 class VmaStringBuilder
6794 {
6795 public:
6796  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6797  size_t GetLength() const { return m_Data.size(); }
6798  const char* GetData() const { return m_Data.data(); }
6799 
6800  void Add(char ch) { m_Data.push_back(ch); }
6801  void Add(const char* pStr);
6802  void AddNewLine() { Add('\n'); }
6803  void AddNumber(uint32_t num);
6804  void AddNumber(uint64_t num);
6805  void AddPointer(const void* ptr);
6806 
6807 private:
6808  VmaVector< char, VmaStlAllocator<char> > m_Data;
6809 };
6810 
6811 void VmaStringBuilder::Add(const char* pStr)
6812 {
6813  const size_t strLen = strlen(pStr);
6814  if(strLen > 0)
6815  {
6816  const size_t oldCount = m_Data.size();
6817  m_Data.resize(oldCount + strLen);
6818  memcpy(m_Data.data() + oldCount, pStr, strLen);
6819  }
6820 }
6821 
6822 void VmaStringBuilder::AddNumber(uint32_t num)
6823 {
6824  char buf[11];
6825  VmaUint32ToStr(buf, sizeof(buf), num);
6826  Add(buf);
6827 }
6828 
6829 void VmaStringBuilder::AddNumber(uint64_t num)
6830 {
6831  char buf[21];
6832  VmaUint64ToStr(buf, sizeof(buf), num);
6833  Add(buf);
6834 }
6835 
6836 void VmaStringBuilder::AddPointer(const void* ptr)
6837 {
6838  char buf[21];
6839  VmaPtrToStr(buf, sizeof(buf), ptr);
6840  Add(buf);
6841 }
6842 
6843 #endif // #if VMA_STATS_STRING_ENABLED
6844 
6846 // VmaJsonWriter
6847 
6848 #if VMA_STATS_STRING_ENABLED
6849 
6850 class VmaJsonWriter
6851 {
6852  VMA_CLASS_NO_COPY(VmaJsonWriter)
6853 public:
6854  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6855  ~VmaJsonWriter();
6856 
6857  void BeginObject(bool singleLine = false);
6858  void EndObject();
6859 
6860  void BeginArray(bool singleLine = false);
6861  void EndArray();
6862 
6863  void WriteString(const char* pStr);
6864  void BeginString(const char* pStr = VMA_NULL);
6865  void ContinueString(const char* pStr);
6866  void ContinueString(uint32_t n);
6867  void ContinueString(uint64_t n);
6868  void ContinueString_Pointer(const void* ptr);
6869  void EndString(const char* pStr = VMA_NULL);
6870 
6871  void WriteNumber(uint32_t n);
6872  void WriteNumber(uint64_t n);
6873  void WriteBool(bool b);
6874  void WriteNull();
6875 
6876 private:
6877  static const char* const INDENT;
6878 
6879  enum COLLECTION_TYPE
6880  {
6881  COLLECTION_TYPE_OBJECT,
6882  COLLECTION_TYPE_ARRAY,
6883  };
6884  struct StackItem
6885  {
6886  COLLECTION_TYPE type;
6887  uint32_t valueCount;
6888  bool singleLineMode;
6889  };
6890 
6891  VmaStringBuilder& m_SB;
6892  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6893  bool m_InsideString;
6894 
6895  void BeginValue(bool isString);
6896  void WriteIndent(bool oneLess = false);
6897 };
6898 
6899 const char* const VmaJsonWriter::INDENT = " ";
6900 
6901 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6902  m_SB(sb),
6903  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6904  m_InsideString(false)
6905 {
6906 }
6907 
6908 VmaJsonWriter::~VmaJsonWriter()
6909 {
6910  VMA_ASSERT(!m_InsideString);
6911  VMA_ASSERT(m_Stack.empty());
6912 }
6913 
6914 void VmaJsonWriter::BeginObject(bool singleLine)
6915 {
6916  VMA_ASSERT(!m_InsideString);
6917 
6918  BeginValue(false);
6919  m_SB.Add('{');
6920 
6921  StackItem item;
6922  item.type = COLLECTION_TYPE_OBJECT;
6923  item.valueCount = 0;
6924  item.singleLineMode = singleLine;
6925  m_Stack.push_back(item);
6926 }
6927 
6928 void VmaJsonWriter::EndObject()
6929 {
6930  VMA_ASSERT(!m_InsideString);
6931 
6932  WriteIndent(true);
6933  m_SB.Add('}');
6934 
6935  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6936  m_Stack.pop_back();
6937 }
6938 
6939 void VmaJsonWriter::BeginArray(bool singleLine)
6940 {
6941  VMA_ASSERT(!m_InsideString);
6942 
6943  BeginValue(false);
6944  m_SB.Add('[');
6945 
6946  StackItem item;
6947  item.type = COLLECTION_TYPE_ARRAY;
6948  item.valueCount = 0;
6949  item.singleLineMode = singleLine;
6950  m_Stack.push_back(item);
6951 }
6952 
6953 void VmaJsonWriter::EndArray()
6954 {
6955  VMA_ASSERT(!m_InsideString);
6956 
6957  WriteIndent(true);
6958  m_SB.Add(']');
6959 
6960  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6961  m_Stack.pop_back();
6962 }
6963 
6964 void VmaJsonWriter::WriteString(const char* pStr)
6965 {
6966  BeginString(pStr);
6967  EndString();
6968 }
6969 
6970 void VmaJsonWriter::BeginString(const char* pStr)
6971 {
6972  VMA_ASSERT(!m_InsideString);
6973 
6974  BeginValue(true);
6975  m_SB.Add('"');
6976  m_InsideString = true;
6977  if(pStr != VMA_NULL && pStr[0] != '\0')
6978  {
6979  ContinueString(pStr);
6980  }
6981 }
6982 
6983 void VmaJsonWriter::ContinueString(const char* pStr)
6984 {
6985  VMA_ASSERT(m_InsideString);
6986 
6987  const size_t strLen = strlen(pStr);
6988  for(size_t i = 0; i < strLen; ++i)
6989  {
6990  char ch = pStr[i];
6991  if(ch == '\\')
6992  {
6993  m_SB.Add("\\\\");
6994  }
6995  else if(ch == '"')
6996  {
6997  m_SB.Add("\\\"");
6998  }
6999  else if(ch >= 32)
7000  {
7001  m_SB.Add(ch);
7002  }
7003  else switch(ch)
7004  {
7005  case '\b':
7006  m_SB.Add("\\b");
7007  break;
7008  case '\f':
7009  m_SB.Add("\\f");
7010  break;
7011  case '\n':
7012  m_SB.Add("\\n");
7013  break;
7014  case '\r':
7015  m_SB.Add("\\r");
7016  break;
7017  case '\t':
7018  m_SB.Add("\\t");
7019  break;
7020  default:
7021  VMA_ASSERT(0 && "Character not currently supported.");
7022  break;
7023  }
7024  }
7025 }
7026 
7027 void VmaJsonWriter::ContinueString(uint32_t n)
7028 {
7029  VMA_ASSERT(m_InsideString);
7030  m_SB.AddNumber(n);
7031 }
7032 
7033 void VmaJsonWriter::ContinueString(uint64_t n)
7034 {
7035  VMA_ASSERT(m_InsideString);
7036  m_SB.AddNumber(n);
7037 }
7038 
7039 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7040 {
7041  VMA_ASSERT(m_InsideString);
7042  m_SB.AddPointer(ptr);
7043 }
7044 
7045 void VmaJsonWriter::EndString(const char* pStr)
7046 {
7047  VMA_ASSERT(m_InsideString);
7048  if(pStr != VMA_NULL && pStr[0] != '\0')
7049  {
7050  ContinueString(pStr);
7051  }
7052  m_SB.Add('"');
7053  m_InsideString = false;
7054 }
7055 
7056 void VmaJsonWriter::WriteNumber(uint32_t n)
7057 {
7058  VMA_ASSERT(!m_InsideString);
7059  BeginValue(false);
7060  m_SB.AddNumber(n);
7061 }
7062 
7063 void VmaJsonWriter::WriteNumber(uint64_t n)
7064 {
7065  VMA_ASSERT(!m_InsideString);
7066  BeginValue(false);
7067  m_SB.AddNumber(n);
7068 }
7069 
7070 void VmaJsonWriter::WriteBool(bool b)
7071 {
7072  VMA_ASSERT(!m_InsideString);
7073  BeginValue(false);
7074  m_SB.Add(b ? "true" : "false");
7075 }
7076 
7077 void VmaJsonWriter::WriteNull()
7078 {
7079  VMA_ASSERT(!m_InsideString);
7080  BeginValue(false);
7081  m_SB.Add("null");
7082 }
7083 
7084 void VmaJsonWriter::BeginValue(bool isString)
7085 {
7086  if(!m_Stack.empty())
7087  {
7088  StackItem& currItem = m_Stack.back();
7089  if(currItem.type == COLLECTION_TYPE_OBJECT &&
7090  currItem.valueCount % 2 == 0)
7091  {
7092  VMA_ASSERT(isString);
7093  }
7094 
7095  if(currItem.type == COLLECTION_TYPE_OBJECT &&
7096  currItem.valueCount % 2 != 0)
7097  {
7098  m_SB.Add(": ");
7099  }
7100  else if(currItem.valueCount > 0)
7101  {
7102  m_SB.Add(", ");
7103  WriteIndent();
7104  }
7105  else
7106  {
7107  WriteIndent();
7108  }
7109  ++currItem.valueCount;
7110  }
7111 }
7112 
7113 void VmaJsonWriter::WriteIndent(bool oneLess)
7114 {
7115  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7116  {
7117  m_SB.AddNewLine();
7118 
7119  size_t count = m_Stack.size();
7120  if(count > 0 && oneLess)
7121  {
7122  --count;
7123  }
7124  for(size_t i = 0; i < count; ++i)
7125  {
7126  m_SB.Add(INDENT);
7127  }
7128  }
7129 }
7130 
7131 #endif // #if VMA_STATS_STRING_ENABLED
7132 
7134 
7135 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7136 {
7137  if(IsUserDataString())
7138  {
7139  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7140 
7141  FreeUserDataString(hAllocator);
7142 
7143  if(pUserData != VMA_NULL)
7144  {
7145  const char* const newStrSrc = (char*)pUserData;
7146  const size_t newStrLen = strlen(newStrSrc);
7147  char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7148  memcpy(newStrDst, newStrSrc, newStrLen + 1);
7149  m_pUserData = newStrDst;
7150  }
7151  }
7152  else
7153  {
7154  m_pUserData = pUserData;
7155  }
7156 }
7157 
7158 void VmaAllocation_T::ChangeBlockAllocation(
7159  VmaAllocator hAllocator,
7160  VmaDeviceMemoryBlock* block,
7161  VkDeviceSize offset)
7162 {
7163  VMA_ASSERT(block != VMA_NULL);
7164  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7165 
7166  // Move mapping reference counter from old block to new block.
7167  if(block != m_BlockAllocation.m_Block)
7168  {
7169  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7170  if(IsPersistentMap())
7171  ++mapRefCount;
7172  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7173  block->Map(hAllocator, mapRefCount, VMA_NULL);
7174  }
7175 
7176  m_BlockAllocation.m_Block = block;
7177  m_BlockAllocation.m_Offset = offset;
7178 }
7179 
7180 void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7181 {
7182  VMA_ASSERT(newSize > 0);
7183  m_Size = newSize;
7184 }
7185 
7186 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7187 {
7188  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7189  m_BlockAllocation.m_Offset = newOffset;
7190 }
7191 
7192 VkDeviceSize VmaAllocation_T::GetOffset() const
7193 {
7194  switch(m_Type)
7195  {
7196  case ALLOCATION_TYPE_BLOCK:
7197  return m_BlockAllocation.m_Offset;
7198  case ALLOCATION_TYPE_DEDICATED:
7199  return 0;
7200  default:
7201  VMA_ASSERT(0);
7202  return 0;
7203  }
7204 }
7205 
7206 VkDeviceMemory VmaAllocation_T::GetMemory() const
7207 {
7208  switch(m_Type)
7209  {
7210  case ALLOCATION_TYPE_BLOCK:
7211  return m_BlockAllocation.m_Block->GetDeviceMemory();
7212  case ALLOCATION_TYPE_DEDICATED:
7213  return m_DedicatedAllocation.m_hMemory;
7214  default:
7215  VMA_ASSERT(0);
7216  return VK_NULL_HANDLE;
7217  }
7218 }
7219 
7220 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7221 {
7222  switch(m_Type)
7223  {
7224  case ALLOCATION_TYPE_BLOCK:
7225  return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7226  case ALLOCATION_TYPE_DEDICATED:
7227  return m_DedicatedAllocation.m_MemoryTypeIndex;
7228  default:
7229  VMA_ASSERT(0);
7230  return UINT32_MAX;
7231  }
7232 }
7233 
7234 void* VmaAllocation_T::GetMappedData() const
7235 {
7236  switch(m_Type)
7237  {
7238  case ALLOCATION_TYPE_BLOCK:
7239  if(m_MapCount != 0)
7240  {
7241  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7242  VMA_ASSERT(pBlockData != VMA_NULL);
7243  return (char*)pBlockData + m_BlockAllocation.m_Offset;
7244  }
7245  else
7246  {
7247  return VMA_NULL;
7248  }
7249  break;
7250  case ALLOCATION_TYPE_DEDICATED:
7251  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7252  return m_DedicatedAllocation.m_pMappedData;
7253  default:
7254  VMA_ASSERT(0);
7255  return VMA_NULL;
7256  }
7257 }
7258 
7259 bool VmaAllocation_T::CanBecomeLost() const
7260 {
7261  switch(m_Type)
7262  {
7263  case ALLOCATION_TYPE_BLOCK:
7264  return m_BlockAllocation.m_CanBecomeLost;
7265  case ALLOCATION_TYPE_DEDICATED:
7266  return false;
7267  default:
7268  VMA_ASSERT(0);
7269  return false;
7270  }
7271 }
7272 
7273 VmaPool VmaAllocation_T::GetPool() const
7274 {
7275  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7276  return m_BlockAllocation.m_hPool;
7277 }
7278 
7279 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7280 {
7281  VMA_ASSERT(CanBecomeLost());
7282 
7283  /*
7284  Warning: This is a carefully designed algorithm.
7285  Do not modify unless you really know what you're doing :)
7286  */
7287  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7288  for(;;)
7289  {
7290  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7291  {
7292  VMA_ASSERT(0);
7293  return false;
7294  }
7295  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7296  {
7297  return false;
7298  }
7299  else // Last use time earlier than current time.
7300  {
7301  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7302  {
7303  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7304  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7305  return true;
7306  }
7307  }
7308  }
7309 }
7310 
7311 #if VMA_STATS_STRING_ENABLED
7312 
7313 // Correspond to values of enum VmaSuballocationType.
7314 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7315  "FREE",
7316  "UNKNOWN",
7317  "BUFFER",
7318  "IMAGE_UNKNOWN",
7319  "IMAGE_LINEAR",
7320  "IMAGE_OPTIMAL",
7321 };
7322 
7323 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7324 {
7325  json.WriteString("Type");
7326  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7327 
7328  json.WriteString("Size");
7329  json.WriteNumber(m_Size);
7330 
7331  if(m_pUserData != VMA_NULL)
7332  {
7333  json.WriteString("UserData");
7334  if(IsUserDataString())
7335  {
7336  json.WriteString((const char*)m_pUserData);
7337  }
7338  else
7339  {
7340  json.BeginString();
7341  json.ContinueString_Pointer(m_pUserData);
7342  json.EndString();
7343  }
7344  }
7345 
7346  json.WriteString("CreationFrameIndex");
7347  json.WriteNumber(m_CreationFrameIndex);
7348 
7349  json.WriteString("LastUseFrameIndex");
7350  json.WriteNumber(GetLastUseFrameIndex());
7351 
7352  if(m_BufferImageUsage != 0)
7353  {
7354  json.WriteString("Usage");
7355  json.WriteNumber(m_BufferImageUsage);
7356  }
7357 }
7358 
7359 #endif
7360 
7361 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7362 {
7363  VMA_ASSERT(IsUserDataString());
7364  if(m_pUserData != VMA_NULL)
7365  {
7366  char* const oldStr = (char*)m_pUserData;
7367  const size_t oldStrLen = strlen(oldStr);
7368  vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7369  m_pUserData = VMA_NULL;
7370  }
7371 }
7372 
7373 void VmaAllocation_T::BlockAllocMap()
7374 {
7375  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7376 
7377  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7378  {
7379  ++m_MapCount;
7380  }
7381  else
7382  {
7383  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7384  }
7385 }
7386 
7387 void VmaAllocation_T::BlockAllocUnmap()
7388 {
7389  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7390 
7391  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7392  {
7393  --m_MapCount;
7394  }
7395  else
7396  {
7397  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7398  }
7399 }
7400 
7401 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7402 {
7403  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7404 
7405  if(m_MapCount != 0)
7406  {
7407  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7408  {
7409  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7410  *ppData = m_DedicatedAllocation.m_pMappedData;
7411  ++m_MapCount;
7412  return VK_SUCCESS;
7413  }
7414  else
7415  {
7416  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7417  return VK_ERROR_MEMORY_MAP_FAILED;
7418  }
7419  }
7420  else
7421  {
7422  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7423  hAllocator->m_hDevice,
7424  m_DedicatedAllocation.m_hMemory,
7425  0, // offset
7426  VK_WHOLE_SIZE,
7427  0, // flags
7428  ppData);
7429  if(result == VK_SUCCESS)
7430  {
7431  m_DedicatedAllocation.m_pMappedData = *ppData;
7432  m_MapCount = 1;
7433  }
7434  return result;
7435  }
7436 }
7437 
7438 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7439 {
7440  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7441 
7442  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7443  {
7444  --m_MapCount;
7445  if(m_MapCount == 0)
7446  {
7447  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7448  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7449  hAllocator->m_hDevice,
7450  m_DedicatedAllocation.m_hMemory);
7451  }
7452  }
7453  else
7454  {
7455  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7456  }
7457 }
7458 
7459 #if VMA_STATS_STRING_ENABLED
7460 
7461 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7462 {
7463  json.BeginObject();
7464 
7465  json.WriteString("Blocks");
7466  json.WriteNumber(stat.blockCount);
7467 
7468  json.WriteString("Allocations");
7469  json.WriteNumber(stat.allocationCount);
7470 
7471  json.WriteString("UnusedRanges");
7472  json.WriteNumber(stat.unusedRangeCount);
7473 
7474  json.WriteString("UsedBytes");
7475  json.WriteNumber(stat.usedBytes);
7476 
7477  json.WriteString("UnusedBytes");
7478  json.WriteNumber(stat.unusedBytes);
7479 
7480  if(stat.allocationCount > 1)
7481  {
7482  json.WriteString("AllocationSize");
7483  json.BeginObject(true);
7484  json.WriteString("Min");
7485  json.WriteNumber(stat.allocationSizeMin);
7486  json.WriteString("Avg");
7487  json.WriteNumber(stat.allocationSizeAvg);
7488  json.WriteString("Max");
7489  json.WriteNumber(stat.allocationSizeMax);
7490  json.EndObject();
7491  }
7492 
7493  if(stat.unusedRangeCount > 1)
7494  {
7495  json.WriteString("UnusedRangeSize");
7496  json.BeginObject(true);
7497  json.WriteString("Min");
7498  json.WriteNumber(stat.unusedRangeSizeMin);
7499  json.WriteString("Avg");
7500  json.WriteNumber(stat.unusedRangeSizeAvg);
7501  json.WriteString("Max");
7502  json.WriteNumber(stat.unusedRangeSizeMax);
7503  json.EndObject();
7504  }
7505 
7506  json.EndObject();
7507 }
7508 
7509 #endif // #if VMA_STATS_STRING_ENABLED
7510 
7511 struct VmaSuballocationItemSizeLess
7512 {
7513  bool operator()(
7514  const VmaSuballocationList::iterator lhs,
7515  const VmaSuballocationList::iterator rhs) const
7516  {
7517  return lhs->size < rhs->size;
7518  }
7519  bool operator()(
7520  const VmaSuballocationList::iterator lhs,
7521  VkDeviceSize rhsSize) const
7522  {
7523  return lhs->size < rhsSize;
7524  }
7525 };
7526 
7527 
7529 // class VmaBlockMetadata
7530 
7531 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7532  m_Size(0),
7533  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7534 {
7535 }
7536 
7537 #if VMA_STATS_STRING_ENABLED
7538 
7539 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7540  VkDeviceSize unusedBytes,
7541  size_t allocationCount,
7542  size_t unusedRangeCount) const
7543 {
7544  json.BeginObject();
7545 
7546  json.WriteString("TotalBytes");
7547  json.WriteNumber(GetSize());
7548 
7549  json.WriteString("UnusedBytes");
7550  json.WriteNumber(unusedBytes);
7551 
7552  json.WriteString("Allocations");
7553  json.WriteNumber((uint64_t)allocationCount);
7554 
7555  json.WriteString("UnusedRanges");
7556  json.WriteNumber((uint64_t)unusedRangeCount);
7557 
7558  json.WriteString("Suballocations");
7559  json.BeginArray();
7560 }
7561 
7562 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7563  VkDeviceSize offset,
7564  VmaAllocation hAllocation) const
7565 {
7566  json.BeginObject(true);
7567 
7568  json.WriteString("Offset");
7569  json.WriteNumber(offset);
7570 
7571  hAllocation->PrintParameters(json);
7572 
7573  json.EndObject();
7574 }
7575 
7576 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7577  VkDeviceSize offset,
7578  VkDeviceSize size) const
7579 {
7580  json.BeginObject(true);
7581 
7582  json.WriteString("Offset");
7583  json.WriteNumber(offset);
7584 
7585  json.WriteString("Type");
7586  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7587 
7588  json.WriteString("Size");
7589  json.WriteNumber(size);
7590 
7591  json.EndObject();
7592 }
7593 
7594 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7595 {
7596  json.EndArray();
7597  json.EndObject();
7598 }
7599 
7600 #endif // #if VMA_STATS_STRING_ENABLED
7601 
7603 // class VmaBlockMetadata_Generic
7604 
7605 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7606  VmaBlockMetadata(hAllocator),
7607  m_FreeCount(0),
7608  m_SumFreeSize(0),
7609  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7610  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7611 {
7612 }
7613 
7614 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7615 {
7616 }
7617 
7618 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7619 {
7620  VmaBlockMetadata::Init(size);
7621 
7622  m_FreeCount = 1;
7623  m_SumFreeSize = size;
7624 
7625  VmaSuballocation suballoc = {};
7626  suballoc.offset = 0;
7627  suballoc.size = size;
7628  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7629  suballoc.hAllocation = VK_NULL_HANDLE;
7630 
7631  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7632  m_Suballocations.push_back(suballoc);
7633  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7634  --suballocItem;
7635  m_FreeSuballocationsBySize.push_back(suballocItem);
7636 }
7637 
7638 bool VmaBlockMetadata_Generic::Validate() const
7639 {
7640  VMA_VALIDATE(!m_Suballocations.empty());
7641 
7642  // Expected offset of new suballocation as calculated from previous ones.
7643  VkDeviceSize calculatedOffset = 0;
7644  // Expected number of free suballocations as calculated from traversing their list.
7645  uint32_t calculatedFreeCount = 0;
7646  // Expected sum size of free suballocations as calculated from traversing their list.
7647  VkDeviceSize calculatedSumFreeSize = 0;
7648  // Expected number of free suballocations that should be registered in
7649  // m_FreeSuballocationsBySize calculated from traversing their list.
7650  size_t freeSuballocationsToRegister = 0;
7651  // True if previous visited suballocation was free.
7652  bool prevFree = false;
7653 
7654  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7655  suballocItem != m_Suballocations.cend();
7656  ++suballocItem)
7657  {
7658  const VmaSuballocation& subAlloc = *suballocItem;
7659 
7660  // Actual offset of this suballocation doesn't match expected one.
7661  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7662 
7663  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7664  // Two adjacent free suballocations are invalid. They should be merged.
7665  VMA_VALIDATE(!prevFree || !currFree);
7666 
7667  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7668 
7669  if(currFree)
7670  {
7671  calculatedSumFreeSize += subAlloc.size;
7672  ++calculatedFreeCount;
7673  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7674  {
7675  ++freeSuballocationsToRegister;
7676  }
7677 
7678  // Margin required between allocations - every free space must be at least that large.
7679  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7680  }
7681  else
7682  {
7683  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7684  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7685 
7686  // Margin required between allocations - previous allocation must be free.
7687  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7688  }
7689 
7690  calculatedOffset += subAlloc.size;
7691  prevFree = currFree;
7692  }
7693 
7694  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7695  // match expected one.
7696  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7697 
7698  VkDeviceSize lastSize = 0;
7699  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7700  {
7701  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7702 
7703  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7704  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7705  // They must be sorted by size ascending.
7706  VMA_VALIDATE(suballocItem->size >= lastSize);
7707 
7708  lastSize = suballocItem->size;
7709  }
7710 
7711  // Check if totals match calculacted values.
7712  VMA_VALIDATE(ValidateFreeSuballocationList());
7713  VMA_VALIDATE(calculatedOffset == GetSize());
7714  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7715  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7716 
7717  return true;
7718 }
7719 
7720 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7721 {
7722  if(!m_FreeSuballocationsBySize.empty())
7723  {
7724  return m_FreeSuballocationsBySize.back()->size;
7725  }
7726  else
7727  {
7728  return 0;
7729  }
7730 }
7731 
7732 bool VmaBlockMetadata_Generic::IsEmpty() const
7733 {
7734  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7735 }
7736 
7737 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7738 {
7739  outInfo.blockCount = 1;
7740 
7741  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7742  outInfo.allocationCount = rangeCount - m_FreeCount;
7743  outInfo.unusedRangeCount = m_FreeCount;
7744 
7745  outInfo.unusedBytes = m_SumFreeSize;
7746  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7747 
7748  outInfo.allocationSizeMin = UINT64_MAX;
7749  outInfo.allocationSizeMax = 0;
7750  outInfo.unusedRangeSizeMin = UINT64_MAX;
7751  outInfo.unusedRangeSizeMax = 0;
7752 
7753  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7754  suballocItem != m_Suballocations.cend();
7755  ++suballocItem)
7756  {
7757  const VmaSuballocation& suballoc = *suballocItem;
7758  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7759  {
7760  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7761  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7762  }
7763  else
7764  {
7765  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7766  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7767  }
7768  }
7769 }
7770 
7771 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7772 {
7773  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7774 
7775  inoutStats.size += GetSize();
7776  inoutStats.unusedSize += m_SumFreeSize;
7777  inoutStats.allocationCount += rangeCount - m_FreeCount;
7778  inoutStats.unusedRangeCount += m_FreeCount;
7779  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7780 }
7781 
7782 #if VMA_STATS_STRING_ENABLED
7783 
7784 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7785 {
7786  PrintDetailedMap_Begin(json,
7787  m_SumFreeSize, // unusedBytes
7788  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7789  m_FreeCount); // unusedRangeCount
7790 
7791  size_t i = 0;
7792  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7793  suballocItem != m_Suballocations.cend();
7794  ++suballocItem, ++i)
7795  {
7796  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7797  {
7798  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7799  }
7800  else
7801  {
7802  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7803  }
7804  }
7805 
7806  PrintDetailedMap_End(json);
7807 }
7808 
7809 #endif // #if VMA_STATS_STRING_ENABLED
7810 
7811 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7812  uint32_t currentFrameIndex,
7813  uint32_t frameInUseCount,
7814  VkDeviceSize bufferImageGranularity,
7815  VkDeviceSize allocSize,
7816  VkDeviceSize allocAlignment,
7817  bool upperAddress,
7818  VmaSuballocationType allocType,
7819  bool canMakeOtherLost,
7820  uint32_t strategy,
7821  VmaAllocationRequest* pAllocationRequest)
7822 {
7823  VMA_ASSERT(allocSize > 0);
7824  VMA_ASSERT(!upperAddress);
7825  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7826  VMA_ASSERT(pAllocationRequest != VMA_NULL);
7827  VMA_HEAVY_ASSERT(Validate());
7828 
7829  // There is not enough total free space in this block to fullfill the request: Early return.
7830  if(canMakeOtherLost == false &&
7831  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7832  {
7833  return false;
7834  }
7835 
7836  // New algorithm, efficiently searching freeSuballocationsBySize.
7837  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7838  if(freeSuballocCount > 0)
7839  {
7841  {
7842  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7843  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7844  m_FreeSuballocationsBySize.data(),
7845  m_FreeSuballocationsBySize.data() + freeSuballocCount,
7846  allocSize + 2 * VMA_DEBUG_MARGIN,
7847  VmaSuballocationItemSizeLess());
7848  size_t index = it - m_FreeSuballocationsBySize.data();
7849  for(; index < freeSuballocCount; ++index)
7850  {
7851  if(CheckAllocation(
7852  currentFrameIndex,
7853  frameInUseCount,
7854  bufferImageGranularity,
7855  allocSize,
7856  allocAlignment,
7857  allocType,
7858  m_FreeSuballocationsBySize[index],
7859  false, // canMakeOtherLost
7860  &pAllocationRequest->offset,
7861  &pAllocationRequest->itemsToMakeLostCount,
7862  &pAllocationRequest->sumFreeSize,
7863  &pAllocationRequest->sumItemSize))
7864  {
7865  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7866  return true;
7867  }
7868  }
7869  }
7870  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7871  {
7872  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7873  it != m_Suballocations.end();
7874  ++it)
7875  {
7876  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7877  currentFrameIndex,
7878  frameInUseCount,
7879  bufferImageGranularity,
7880  allocSize,
7881  allocAlignment,
7882  allocType,
7883  it,
7884  false, // canMakeOtherLost
7885  &pAllocationRequest->offset,
7886  &pAllocationRequest->itemsToMakeLostCount,
7887  &pAllocationRequest->sumFreeSize,
7888  &pAllocationRequest->sumItemSize))
7889  {
7890  pAllocationRequest->item = it;
7891  return true;
7892  }
7893  }
7894  }
7895  else // WORST_FIT, FIRST_FIT
7896  {
7897  // Search staring from biggest suballocations.
7898  for(size_t index = freeSuballocCount; index--; )
7899  {
7900  if(CheckAllocation(
7901  currentFrameIndex,
7902  frameInUseCount,
7903  bufferImageGranularity,
7904  allocSize,
7905  allocAlignment,
7906  allocType,
7907  m_FreeSuballocationsBySize[index],
7908  false, // canMakeOtherLost
7909  &pAllocationRequest->offset,
7910  &pAllocationRequest->itemsToMakeLostCount,
7911  &pAllocationRequest->sumFreeSize,
7912  &pAllocationRequest->sumItemSize))
7913  {
7914  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7915  return true;
7916  }
7917  }
7918  }
7919  }
7920 
7921  if(canMakeOtherLost)
7922  {
7923  // Brute-force algorithm. TODO: Come up with something better.
7924 
7925  pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7926  pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7927 
7928  VmaAllocationRequest tmpAllocRequest = {};
7929  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7930  suballocIt != m_Suballocations.end();
7931  ++suballocIt)
7932  {
7933  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7934  suballocIt->hAllocation->CanBecomeLost())
7935  {
7936  if(CheckAllocation(
7937  currentFrameIndex,
7938  frameInUseCount,
7939  bufferImageGranularity,
7940  allocSize,
7941  allocAlignment,
7942  allocType,
7943  suballocIt,
7944  canMakeOtherLost,
7945  &tmpAllocRequest.offset,
7946  &tmpAllocRequest.itemsToMakeLostCount,
7947  &tmpAllocRequest.sumFreeSize,
7948  &tmpAllocRequest.sumItemSize))
7949  {
7950  tmpAllocRequest.item = suballocIt;
7951 
7952  if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7954  {
7955  *pAllocationRequest = tmpAllocRequest;
7956  }
7957  }
7958  }
7959  }
7960 
7961  if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7962  {
7963  return true;
7964  }
7965  }
7966 
7967  return false;
7968 }
7969 
7970 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7971  uint32_t currentFrameIndex,
7972  uint32_t frameInUseCount,
7973  VmaAllocationRequest* pAllocationRequest)
7974 {
7975  while(pAllocationRequest->itemsToMakeLostCount > 0)
7976  {
7977  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7978  {
7979  ++pAllocationRequest->item;
7980  }
7981  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7982  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7983  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7984  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7985  {
7986  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
7987  --pAllocationRequest->itemsToMakeLostCount;
7988  }
7989  else
7990  {
7991  return false;
7992  }
7993  }
7994 
7995  VMA_HEAVY_ASSERT(Validate());
7996  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7997  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
7998 
7999  return true;
8000 }
8001 
8002 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8003 {
8004  uint32_t lostAllocationCount = 0;
8005  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8006  it != m_Suballocations.end();
8007  ++it)
8008  {
8009  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8010  it->hAllocation->CanBecomeLost() &&
8011  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8012  {
8013  it = FreeSuballocation(it);
8014  ++lostAllocationCount;
8015  }
8016  }
8017  return lostAllocationCount;
8018 }
8019 
8020 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8021 {
8022  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8023  it != m_Suballocations.end();
8024  ++it)
8025  {
8026  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8027  {
8028  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8029  {
8030  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8031  return VK_ERROR_VALIDATION_FAILED_EXT;
8032  }
8033  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8034  {
8035  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8036  return VK_ERROR_VALIDATION_FAILED_EXT;
8037  }
8038  }
8039  }
8040 
8041  return VK_SUCCESS;
8042 }
8043 
8044 void VmaBlockMetadata_Generic::Alloc(
8045  const VmaAllocationRequest& request,
8046  VmaSuballocationType type,
8047  VkDeviceSize allocSize,
8048  bool upperAddress,
8049  VmaAllocation hAllocation)
8050 {
8051  VMA_ASSERT(!upperAddress);
8052  VMA_ASSERT(request.item != m_Suballocations.end());
8053  VmaSuballocation& suballoc = *request.item;
8054  // Given suballocation is a free block.
8055  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8056  // Given offset is inside this suballocation.
8057  VMA_ASSERT(request.offset >= suballoc.offset);
8058  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8059  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8060  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8061 
8062  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8063  // it to become used.
8064  UnregisterFreeSuballocation(request.item);
8065 
8066  suballoc.offset = request.offset;
8067  suballoc.size = allocSize;
8068  suballoc.type = type;
8069  suballoc.hAllocation = hAllocation;
8070 
8071  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8072  if(paddingEnd)
8073  {
8074  VmaSuballocation paddingSuballoc = {};
8075  paddingSuballoc.offset = request.offset + allocSize;
8076  paddingSuballoc.size = paddingEnd;
8077  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8078  VmaSuballocationList::iterator next = request.item;
8079  ++next;
8080  const VmaSuballocationList::iterator paddingEndItem =
8081  m_Suballocations.insert(next, paddingSuballoc);
8082  RegisterFreeSuballocation(paddingEndItem);
8083  }
8084 
8085  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8086  if(paddingBegin)
8087  {
8088  VmaSuballocation paddingSuballoc = {};
8089  paddingSuballoc.offset = request.offset - paddingBegin;
8090  paddingSuballoc.size = paddingBegin;
8091  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8092  const VmaSuballocationList::iterator paddingBeginItem =
8093  m_Suballocations.insert(request.item, paddingSuballoc);
8094  RegisterFreeSuballocation(paddingBeginItem);
8095  }
8096 
8097  // Update totals.
8098  m_FreeCount = m_FreeCount - 1;
8099  if(paddingBegin > 0)
8100  {
8101  ++m_FreeCount;
8102  }
8103  if(paddingEnd > 0)
8104  {
8105  ++m_FreeCount;
8106  }
8107  m_SumFreeSize -= allocSize;
8108 }
8109 
8110 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8111 {
8112  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8113  suballocItem != m_Suballocations.end();
8114  ++suballocItem)
8115  {
8116  VmaSuballocation& suballoc = *suballocItem;
8117  if(suballoc.hAllocation == allocation)
8118  {
8119  FreeSuballocation(suballocItem);
8120  VMA_HEAVY_ASSERT(Validate());
8121  return;
8122  }
8123  }
8124  VMA_ASSERT(0 && "Not found!");
8125 }
8126 
8127 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8128 {
8129  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8130  suballocItem != m_Suballocations.end();
8131  ++suballocItem)
8132  {
8133  VmaSuballocation& suballoc = *suballocItem;
8134  if(suballoc.offset == offset)
8135  {
8136  FreeSuballocation(suballocItem);
8137  return;
8138  }
8139  }
8140  VMA_ASSERT(0 && "Not found!");
8141 }
8142 
8143 bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8144 {
8145  typedef VmaSuballocationList::iterator iter_type;
8146  for(iter_type suballocItem = m_Suballocations.begin();
8147  suballocItem != m_Suballocations.end();
8148  ++suballocItem)
8149  {
8150  VmaSuballocation& suballoc = *suballocItem;
8151  if(suballoc.hAllocation == alloc)
8152  {
8153  iter_type nextItem = suballocItem;
8154  ++nextItem;
8155 
8156  // Should have been ensured on higher level.
8157  VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8158 
8159  // Shrinking.
8160  if(newSize < alloc->GetSize())
8161  {
8162  const VkDeviceSize sizeDiff = suballoc.size - newSize;
8163 
8164  // There is next item.
8165  if(nextItem != m_Suballocations.end())
8166  {
8167  // Next item is free.
8168  if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8169  {
8170  // Grow this next item backward.
8171  UnregisterFreeSuballocation(nextItem);
8172  nextItem->offset -= sizeDiff;
8173  nextItem->size += sizeDiff;
8174  RegisterFreeSuballocation(nextItem);
8175  }
8176  // Next item is not free.
8177  else
8178  {
8179  // Create free item after current one.
8180  VmaSuballocation newFreeSuballoc;
8181  newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8182  newFreeSuballoc.offset = suballoc.offset + newSize;
8183  newFreeSuballoc.size = sizeDiff;
8184  newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8185  iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8186  RegisterFreeSuballocation(newFreeSuballocIt);
8187 
8188  ++m_FreeCount;
8189  }
8190  }
8191  // This is the last item.
8192  else
8193  {
8194  // Create free item at the end.
8195  VmaSuballocation newFreeSuballoc;
8196  newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8197  newFreeSuballoc.offset = suballoc.offset + newSize;
8198  newFreeSuballoc.size = sizeDiff;
8199  newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8200  m_Suballocations.push_back(newFreeSuballoc);
8201 
8202  iter_type newFreeSuballocIt = m_Suballocations.end();
8203  RegisterFreeSuballocation(--newFreeSuballocIt);
8204 
8205  ++m_FreeCount;
8206  }
8207 
8208  suballoc.size = newSize;
8209  m_SumFreeSize += sizeDiff;
8210  }
8211  // Growing.
8212  else
8213  {
8214  const VkDeviceSize sizeDiff = newSize - suballoc.size;
8215 
8216  // There is next item.
8217  if(nextItem != m_Suballocations.end())
8218  {
8219  // Next item is free.
8220  if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8221  {
8222  // There is not enough free space, including margin.
8223  if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8224  {
8225  return false;
8226  }
8227 
8228  // There is more free space than required.
8229  if(nextItem->size > sizeDiff)
8230  {
8231  // Move and shrink this next item.
8232  UnregisterFreeSuballocation(nextItem);
8233  nextItem->offset += sizeDiff;
8234  nextItem->size -= sizeDiff;
8235  RegisterFreeSuballocation(nextItem);
8236  }
8237  // There is exactly the amount of free space required.
8238  else
8239  {
8240  // Remove this next free item.
8241  UnregisterFreeSuballocation(nextItem);
8242  m_Suballocations.erase(nextItem);
8243  --m_FreeCount;
8244  }
8245  }
8246  // Next item is not free - there is no space to grow.
8247  else
8248  {
8249  return false;
8250  }
8251  }
8252  // This is the last item - there is no space to grow.
8253  else
8254  {
8255  return false;
8256  }
8257 
8258  suballoc.size = newSize;
8259  m_SumFreeSize -= sizeDiff;
8260  }
8261 
8262  // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8263  return true;
8264  }
8265  }
8266  VMA_ASSERT(0 && "Not found!");
8267  return false;
8268 }
8269 
8270 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8271 {
8272  VkDeviceSize lastSize = 0;
8273  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8274  {
8275  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8276 
8277  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8278  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8279  VMA_VALIDATE(it->size >= lastSize);
8280  lastSize = it->size;
8281  }
8282  return true;
8283 }
8284 
8285 bool VmaBlockMetadata_Generic::CheckAllocation(
8286  uint32_t currentFrameIndex,
8287  uint32_t frameInUseCount,
8288  VkDeviceSize bufferImageGranularity,
8289  VkDeviceSize allocSize,
8290  VkDeviceSize allocAlignment,
8291  VmaSuballocationType allocType,
8292  VmaSuballocationList::const_iterator suballocItem,
8293  bool canMakeOtherLost,
8294  VkDeviceSize* pOffset,
8295  size_t* itemsToMakeLostCount,
8296  VkDeviceSize* pSumFreeSize,
8297  VkDeviceSize* pSumItemSize) const
8298 {
8299  VMA_ASSERT(allocSize > 0);
8300  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8301  VMA_ASSERT(suballocItem != m_Suballocations.cend());
8302  VMA_ASSERT(pOffset != VMA_NULL);
8303 
8304  *itemsToMakeLostCount = 0;
8305  *pSumFreeSize = 0;
8306  *pSumItemSize = 0;
8307 
8308  if(canMakeOtherLost)
8309  {
8310  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8311  {
8312  *pSumFreeSize = suballocItem->size;
8313  }
8314  else
8315  {
8316  if(suballocItem->hAllocation->CanBecomeLost() &&
8317  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8318  {
8319  ++*itemsToMakeLostCount;
8320  *pSumItemSize = suballocItem->size;
8321  }
8322  else
8323  {
8324  return false;
8325  }
8326  }
8327 
8328  // Remaining size is too small for this request: Early return.
8329  if(GetSize() - suballocItem->offset < allocSize)
8330  {
8331  return false;
8332  }
8333 
8334  // Start from offset equal to beginning of this suballocation.
8335  *pOffset = suballocItem->offset;
8336 
8337  // Apply VMA_DEBUG_MARGIN at the beginning.
8338  if(VMA_DEBUG_MARGIN > 0)
8339  {
8340  *pOffset += VMA_DEBUG_MARGIN;
8341  }
8342 
8343  // Apply alignment.
8344  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8345 
8346  // Check previous suballocations for BufferImageGranularity conflicts.
8347  // Make bigger alignment if necessary.
8348  if(bufferImageGranularity > 1)
8349  {
8350  bool bufferImageGranularityConflict = false;
8351  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8352  while(prevSuballocItem != m_Suballocations.cbegin())
8353  {
8354  --prevSuballocItem;
8355  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8356  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8357  {
8358  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8359  {
8360  bufferImageGranularityConflict = true;
8361  break;
8362  }
8363  }
8364  else
8365  // Already on previous page.
8366  break;
8367  }
8368  if(bufferImageGranularityConflict)
8369  {
8370  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8371  }
8372  }
8373 
8374  // Now that we have final *pOffset, check if we are past suballocItem.
8375  // If yes, return false - this function should be called for another suballocItem as starting point.
8376  if(*pOffset >= suballocItem->offset + suballocItem->size)
8377  {
8378  return false;
8379  }
8380 
8381  // Calculate padding at the beginning based on current offset.
8382  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8383 
8384  // Calculate required margin at the end.
8385  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8386 
8387  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8388  // Another early return check.
8389  if(suballocItem->offset + totalSize > GetSize())
8390  {
8391  return false;
8392  }
8393 
8394  // Advance lastSuballocItem until desired size is reached.
8395  // Update itemsToMakeLostCount.
8396  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8397  if(totalSize > suballocItem->size)
8398  {
8399  VkDeviceSize remainingSize = totalSize - suballocItem->size;
8400  while(remainingSize > 0)
8401  {
8402  ++lastSuballocItem;
8403  if(lastSuballocItem == m_Suballocations.cend())
8404  {
8405  return false;
8406  }
8407  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8408  {
8409  *pSumFreeSize += lastSuballocItem->size;
8410  }
8411  else
8412  {
8413  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8414  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8415  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8416  {
8417  ++*itemsToMakeLostCount;
8418  *pSumItemSize += lastSuballocItem->size;
8419  }
8420  else
8421  {
8422  return false;
8423  }
8424  }
8425  remainingSize = (lastSuballocItem->size < remainingSize) ?
8426  remainingSize - lastSuballocItem->size : 0;
8427  }
8428  }
8429 
8430  // Check next suballocations for BufferImageGranularity conflicts.
8431  // If conflict exists, we must mark more allocations lost or fail.
8432  if(bufferImageGranularity > 1)
8433  {
8434  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8435  ++nextSuballocItem;
8436  while(nextSuballocItem != m_Suballocations.cend())
8437  {
8438  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8439  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8440  {
8441  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8442  {
8443  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8444  if(nextSuballoc.hAllocation->CanBecomeLost() &&
8445  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8446  {
8447  ++*itemsToMakeLostCount;
8448  }
8449  else
8450  {
8451  return false;
8452  }
8453  }
8454  }
8455  else
8456  {
8457  // Already on next page.
8458  break;
8459  }
8460  ++nextSuballocItem;
8461  }
8462  }
8463  }
8464  else
8465  {
8466  const VmaSuballocation& suballoc = *suballocItem;
8467  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8468 
8469  *pSumFreeSize = suballoc.size;
8470 
8471  // Size of this suballocation is too small for this request: Early return.
8472  if(suballoc.size < allocSize)
8473  {
8474  return false;
8475  }
8476 
8477  // Start from offset equal to beginning of this suballocation.
8478  *pOffset = suballoc.offset;
8479 
8480  // Apply VMA_DEBUG_MARGIN at the beginning.
8481  if(VMA_DEBUG_MARGIN > 0)
8482  {
8483  *pOffset += VMA_DEBUG_MARGIN;
8484  }
8485 
8486  // Apply alignment.
8487  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8488 
8489  // Check previous suballocations for BufferImageGranularity conflicts.
8490  // Make bigger alignment if necessary.
8491  if(bufferImageGranularity > 1)
8492  {
8493  bool bufferImageGranularityConflict = false;
8494  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8495  while(prevSuballocItem != m_Suballocations.cbegin())
8496  {
8497  --prevSuballocItem;
8498  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8499  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8500  {
8501  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8502  {
8503  bufferImageGranularityConflict = true;
8504  break;
8505  }
8506  }
8507  else
8508  // Already on previous page.
8509  break;
8510  }
8511  if(bufferImageGranularityConflict)
8512  {
8513  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8514  }
8515  }
8516 
8517  // Calculate padding at the beginning based on current offset.
8518  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8519 
8520  // Calculate required margin at the end.
8521  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8522 
8523  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8524  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8525  {
8526  return false;
8527  }
8528 
8529  // Check next suballocations for BufferImageGranularity conflicts.
8530  // If conflict exists, allocation cannot be made here.
8531  if(bufferImageGranularity > 1)
8532  {
8533  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8534  ++nextSuballocItem;
8535  while(nextSuballocItem != m_Suballocations.cend())
8536  {
8537  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8538  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8539  {
8540  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8541  {
8542  return false;
8543  }
8544  }
8545  else
8546  {
8547  // Already on next page.
8548  break;
8549  }
8550  ++nextSuballocItem;
8551  }
8552  }
8553  }
8554 
8555  // All tests passed: Success. pOffset is already filled.
8556  return true;
8557 }
8558 
8559 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8560 {
8561  VMA_ASSERT(item != m_Suballocations.end());
8562  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8563 
8564  VmaSuballocationList::iterator nextItem = item;
8565  ++nextItem;
8566  VMA_ASSERT(nextItem != m_Suballocations.end());
8567  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8568 
8569  item->size += nextItem->size;
8570  --m_FreeCount;
8571  m_Suballocations.erase(nextItem);
8572 }
8573 
8574 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8575 {
8576  // Change this suballocation to be marked as free.
8577  VmaSuballocation& suballoc = *suballocItem;
8578  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8579  suballoc.hAllocation = VK_NULL_HANDLE;
8580 
8581  // Update totals.
8582  ++m_FreeCount;
8583  m_SumFreeSize += suballoc.size;
8584 
8585  // Merge with previous and/or next suballocation if it's also free.
8586  bool mergeWithNext = false;
8587  bool mergeWithPrev = false;
8588 
8589  VmaSuballocationList::iterator nextItem = suballocItem;
8590  ++nextItem;
8591  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8592  {
8593  mergeWithNext = true;
8594  }
8595 
8596  VmaSuballocationList::iterator prevItem = suballocItem;
8597  if(suballocItem != m_Suballocations.begin())
8598  {
8599  --prevItem;
8600  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8601  {
8602  mergeWithPrev = true;
8603  }
8604  }
8605 
8606  if(mergeWithNext)
8607  {
8608  UnregisterFreeSuballocation(nextItem);
8609  MergeFreeWithNext(suballocItem);
8610  }
8611 
8612  if(mergeWithPrev)
8613  {
8614  UnregisterFreeSuballocation(prevItem);
8615  MergeFreeWithNext(prevItem);
8616  RegisterFreeSuballocation(prevItem);
8617  return prevItem;
8618  }
8619  else
8620  {
8621  RegisterFreeSuballocation(suballocItem);
8622  return suballocItem;
8623  }
8624 }
8625 
8626 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8627 {
8628  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8629  VMA_ASSERT(item->size > 0);
8630 
8631  // You may want to enable this validation at the beginning or at the end of
8632  // this function, depending on what do you want to check.
8633  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8634 
8635  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8636  {
8637  if(m_FreeSuballocationsBySize.empty())
8638  {
8639  m_FreeSuballocationsBySize.push_back(item);
8640  }
8641  else
8642  {
8643  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8644  }
8645  }
8646 
8647  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8648 }
8649 
8650 
8651 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8652 {
8653  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8654  VMA_ASSERT(item->size > 0);
8655 
8656  // You may want to enable this validation at the beginning or at the end of
8657  // this function, depending on what do you want to check.
8658  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8659 
8660  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8661  {
8662  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8663  m_FreeSuballocationsBySize.data(),
8664  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8665  item,
8666  VmaSuballocationItemSizeLess());
8667  for(size_t index = it - m_FreeSuballocationsBySize.data();
8668  index < m_FreeSuballocationsBySize.size();
8669  ++index)
8670  {
8671  if(m_FreeSuballocationsBySize[index] == item)
8672  {
8673  VmaVectorRemove(m_FreeSuballocationsBySize, index);
8674  return;
8675  }
8676  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8677  }
8678  VMA_ASSERT(0 && "Not found.");
8679  }
8680 
8681  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8682 }
8683 
8684 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8685  VkDeviceSize bufferImageGranularity,
8686  VmaSuballocationType& inOutPrevSuballocType) const
8687 {
8688  if(bufferImageGranularity == 1 || IsEmpty())
8689  {
8690  return false;
8691  }
8692 
8693  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8694  bool typeConflictFound = false;
8695  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8696  it != m_Suballocations.cend();
8697  ++it)
8698  {
8699  const VmaSuballocationType suballocType = it->type;
8700  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8701  {
8702  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8703  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8704  {
8705  typeConflictFound = true;
8706  }
8707  inOutPrevSuballocType = suballocType;
8708  }
8709  }
8710 
8711  return typeConflictFound || minAlignment >= bufferImageGranularity;
8712 }
8713 
8715 // class VmaBlockMetadata_Linear
8716 
8717 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8718  VmaBlockMetadata(hAllocator),
8719  m_SumFreeSize(0),
8720  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8721  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8722  m_1stVectorIndex(0),
8723  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8724  m_1stNullItemsBeginCount(0),
8725  m_1stNullItemsMiddleCount(0),
8726  m_2ndNullItemsCount(0)
8727 {
8728 }
8729 
8730 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8731 {
8732 }
8733 
8734 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8735 {
8736  VmaBlockMetadata::Init(size);
8737  m_SumFreeSize = size;
8738 }
8739 
8740 bool VmaBlockMetadata_Linear::Validate() const
8741 {
8742  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8743  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8744 
8745  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8746  VMA_VALIDATE(!suballocations1st.empty() ||
8747  suballocations2nd.empty() ||
8748  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8749 
8750  if(!suballocations1st.empty())
8751  {
8752  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8753  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8754  // Null item at the end should be just pop_back().
8755  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8756  }
8757  if(!suballocations2nd.empty())
8758  {
8759  // Null item at the end should be just pop_back().
8760  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8761  }
8762 
8763  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8764  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8765 
8766  VkDeviceSize sumUsedSize = 0;
8767  const size_t suballoc1stCount = suballocations1st.size();
8768  VkDeviceSize offset = VMA_DEBUG_MARGIN;
8769 
8770  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8771  {
8772  const size_t suballoc2ndCount = suballocations2nd.size();
8773  size_t nullItem2ndCount = 0;
8774  for(size_t i = 0; i < suballoc2ndCount; ++i)
8775  {
8776  const VmaSuballocation& suballoc = suballocations2nd[i];
8777  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8778 
8779  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8780  VMA_VALIDATE(suballoc.offset >= offset);
8781 
8782  if(!currFree)
8783  {
8784  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8785  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8786  sumUsedSize += suballoc.size;
8787  }
8788  else
8789  {
8790  ++nullItem2ndCount;
8791  }
8792 
8793  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8794  }
8795 
8796  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8797  }
8798 
8799  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8800  {
8801  const VmaSuballocation& suballoc = suballocations1st[i];
8802  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8803  suballoc.hAllocation == VK_NULL_HANDLE);
8804  }
8805 
8806  size_t nullItem1stCount = m_1stNullItemsBeginCount;
8807 
8808  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8809  {
8810  const VmaSuballocation& suballoc = suballocations1st[i];
8811  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8812 
8813  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8814  VMA_VALIDATE(suballoc.offset >= offset);
8815  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8816 
8817  if(!currFree)
8818  {
8819  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8820  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8821  sumUsedSize += suballoc.size;
8822  }
8823  else
8824  {
8825  ++nullItem1stCount;
8826  }
8827 
8828  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8829  }
8830  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8831 
8832  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8833  {
8834  const size_t suballoc2ndCount = suballocations2nd.size();
8835  size_t nullItem2ndCount = 0;
8836  for(size_t i = suballoc2ndCount; i--; )
8837  {
8838  const VmaSuballocation& suballoc = suballocations2nd[i];
8839  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8840 
8841  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8842  VMA_VALIDATE(suballoc.offset >= offset);
8843 
8844  if(!currFree)
8845  {
8846  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8847  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8848  sumUsedSize += suballoc.size;
8849  }
8850  else
8851  {
8852  ++nullItem2ndCount;
8853  }
8854 
8855  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8856  }
8857 
8858  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8859  }
8860 
8861  VMA_VALIDATE(offset <= GetSize());
8862  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8863 
8864  return true;
8865 }
8866 
8867 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8868 {
8869  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8870  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8871 }
8872 
8873 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8874 {
8875  const VkDeviceSize size = GetSize();
8876 
8877  /*
8878  We don't consider gaps inside allocation vectors with freed allocations because
8879  they are not suitable for reuse in linear allocator. We consider only space that
8880  is available for new allocations.
8881  */
8882  if(IsEmpty())
8883  {
8884  return size;
8885  }
8886 
8887  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8888 
8889  switch(m_2ndVectorMode)
8890  {
8891  case SECOND_VECTOR_EMPTY:
8892  /*
8893  Available space is after end of 1st, as well as before beginning of 1st (which
8894  whould make it a ring buffer).
8895  */
8896  {
8897  const size_t suballocations1stCount = suballocations1st.size();
8898  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8899  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8900  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8901  return VMA_MAX(
8902  firstSuballoc.offset,
8903  size - (lastSuballoc.offset + lastSuballoc.size));
8904  }
8905  break;
8906 
8907  case SECOND_VECTOR_RING_BUFFER:
8908  /*
8909  Available space is only between end of 2nd and beginning of 1st.
8910  */
8911  {
8912  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8913  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8914  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8915  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8916  }
8917  break;
8918 
8919  case SECOND_VECTOR_DOUBLE_STACK:
8920  /*
8921  Available space is only between end of 1st and top of 2nd.
8922  */
8923  {
8924  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8925  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8926  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8927  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8928  }
8929  break;
8930 
8931  default:
8932  VMA_ASSERT(0);
8933  return 0;
8934  }
8935 }
8936 
8937 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8938 {
8939  const VkDeviceSize size = GetSize();
8940  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8941  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8942  const size_t suballoc1stCount = suballocations1st.size();
8943  const size_t suballoc2ndCount = suballocations2nd.size();
8944 
8945  outInfo.blockCount = 1;
8946  outInfo.allocationCount = (uint32_t)GetAllocationCount();
8947  outInfo.unusedRangeCount = 0;
8948  outInfo.usedBytes = 0;
8949  outInfo.allocationSizeMin = UINT64_MAX;
8950  outInfo.allocationSizeMax = 0;
8951  outInfo.unusedRangeSizeMin = UINT64_MAX;
8952  outInfo.unusedRangeSizeMax = 0;
8953 
8954  VkDeviceSize lastOffset = 0;
8955 
8956  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8957  {
8958  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8959  size_t nextAlloc2ndIndex = 0;
8960  while(lastOffset < freeSpace2ndTo1stEnd)
8961  {
8962  // Find next non-null allocation or move nextAllocIndex to the end.
8963  while(nextAlloc2ndIndex < suballoc2ndCount &&
8964  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8965  {
8966  ++nextAlloc2ndIndex;
8967  }
8968 
8969  // Found non-null allocation.
8970  if(nextAlloc2ndIndex < suballoc2ndCount)
8971  {
8972  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8973 
8974  // 1. Process free space before this allocation.
8975  if(lastOffset < suballoc.offset)
8976  {
8977  // There is free space from lastOffset to suballoc.offset.
8978  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8979  ++outInfo.unusedRangeCount;
8980  outInfo.unusedBytes += unusedRangeSize;
8981  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8982  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8983  }
8984 
8985  // 2. Process this allocation.
8986  // There is allocation with suballoc.offset, suballoc.size.
8987  outInfo.usedBytes += suballoc.size;
8988  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8989  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
8990 
8991  // 3. Prepare for next iteration.
8992  lastOffset = suballoc.offset + suballoc.size;
8993  ++nextAlloc2ndIndex;
8994  }
8995  // We are at the end.
8996  else
8997  {
8998  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8999  if(lastOffset < freeSpace2ndTo1stEnd)
9000  {
9001  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9002  ++outInfo.unusedRangeCount;
9003  outInfo.unusedBytes += unusedRangeSize;
9004  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9005  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9006  }
9007 
9008  // End of loop.
9009  lastOffset = freeSpace2ndTo1stEnd;
9010  }
9011  }
9012  }
9013 
9014  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9015  const VkDeviceSize freeSpace1stTo2ndEnd =
9016  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9017  while(lastOffset < freeSpace1stTo2ndEnd)
9018  {
9019  // Find next non-null allocation or move nextAllocIndex to the end.
9020  while(nextAlloc1stIndex < suballoc1stCount &&
9021  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9022  {
9023  ++nextAlloc1stIndex;
9024  }
9025 
9026  // Found non-null allocation.
9027  if(nextAlloc1stIndex < suballoc1stCount)
9028  {
9029  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9030 
9031  // 1. Process free space before this allocation.
9032  if(lastOffset < suballoc.offset)
9033  {
9034  // There is free space from lastOffset to suballoc.offset.
9035  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9036  ++outInfo.unusedRangeCount;
9037  outInfo.unusedBytes += unusedRangeSize;
9038  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9039  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9040  }
9041 
9042  // 2. Process this allocation.
9043  // There is allocation with suballoc.offset, suballoc.size.
9044  outInfo.usedBytes += suballoc.size;
9045  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9046  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9047 
9048  // 3. Prepare for next iteration.
9049  lastOffset = suballoc.offset + suballoc.size;
9050  ++nextAlloc1stIndex;
9051  }
9052  // We are at the end.
9053  else
9054  {
9055  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9056  if(lastOffset < freeSpace1stTo2ndEnd)
9057  {
9058  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9059  ++outInfo.unusedRangeCount;
9060  outInfo.unusedBytes += unusedRangeSize;
9061  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9062  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9063  }
9064 
9065  // End of loop.
9066  lastOffset = freeSpace1stTo2ndEnd;
9067  }
9068  }
9069 
9070  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9071  {
9072  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9073  while(lastOffset < size)
9074  {
9075  // Find next non-null allocation or move nextAllocIndex to the end.
9076  while(nextAlloc2ndIndex != SIZE_MAX &&
9077  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9078  {
9079  --nextAlloc2ndIndex;
9080  }
9081 
9082  // Found non-null allocation.
9083  if(nextAlloc2ndIndex != SIZE_MAX)
9084  {
9085  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9086 
9087  // 1. Process free space before this allocation.
9088  if(lastOffset < suballoc.offset)
9089  {
9090  // There is free space from lastOffset to suballoc.offset.
9091  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9092  ++outInfo.unusedRangeCount;
9093  outInfo.unusedBytes += unusedRangeSize;
9094  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9095  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9096  }
9097 
9098  // 2. Process this allocation.
9099  // There is allocation with suballoc.offset, suballoc.size.
9100  outInfo.usedBytes += suballoc.size;
9101  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9102  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9103 
9104  // 3. Prepare for next iteration.
9105  lastOffset = suballoc.offset + suballoc.size;
9106  --nextAlloc2ndIndex;
9107  }
9108  // We are at the end.
9109  else
9110  {
9111  // There is free space from lastOffset to size.
9112  if(lastOffset < size)
9113  {
9114  const VkDeviceSize unusedRangeSize = size - lastOffset;
9115  ++outInfo.unusedRangeCount;
9116  outInfo.unusedBytes += unusedRangeSize;
9117  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9118  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9119  }
9120 
9121  // End of loop.
9122  lastOffset = size;
9123  }
9124  }
9125  }
9126 
9127  outInfo.unusedBytes = size - outInfo.usedBytes;
9128 }
9129 
9130 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9131 {
9132  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9133  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9134  const VkDeviceSize size = GetSize();
9135  const size_t suballoc1stCount = suballocations1st.size();
9136  const size_t suballoc2ndCount = suballocations2nd.size();
9137 
9138  inoutStats.size += size;
9139 
9140  VkDeviceSize lastOffset = 0;
9141 
9142  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9143  {
9144  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9145  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9146  while(lastOffset < freeSpace2ndTo1stEnd)
9147  {
9148  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9149  while(nextAlloc2ndIndex < suballoc2ndCount &&
9150  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9151  {
9152  ++nextAlloc2ndIndex;
9153  }
9154 
9155  // Found non-null allocation.
9156  if(nextAlloc2ndIndex < suballoc2ndCount)
9157  {
9158  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9159 
9160  // 1. Process free space before this allocation.
9161  if(lastOffset < suballoc.offset)
9162  {
9163  // There is free space from lastOffset to suballoc.offset.
9164  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9165  inoutStats.unusedSize += unusedRangeSize;
9166  ++inoutStats.unusedRangeCount;
9167  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9168  }
9169 
9170  // 2. Process this allocation.
9171  // There is allocation with suballoc.offset, suballoc.size.
9172  ++inoutStats.allocationCount;
9173 
9174  // 3. Prepare for next iteration.
9175  lastOffset = suballoc.offset + suballoc.size;
9176  ++nextAlloc2ndIndex;
9177  }
9178  // We are at the end.
9179  else
9180  {
9181  if(lastOffset < freeSpace2ndTo1stEnd)
9182  {
9183  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9184  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9185  inoutStats.unusedSize += unusedRangeSize;
9186  ++inoutStats.unusedRangeCount;
9187  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9188  }
9189 
9190  // End of loop.
9191  lastOffset = freeSpace2ndTo1stEnd;
9192  }
9193  }
9194  }
9195 
9196  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9197  const VkDeviceSize freeSpace1stTo2ndEnd =
9198  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9199  while(lastOffset < freeSpace1stTo2ndEnd)
9200  {
9201  // Find next non-null allocation or move nextAllocIndex to the end.
9202  while(nextAlloc1stIndex < suballoc1stCount &&
9203  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9204  {
9205  ++nextAlloc1stIndex;
9206  }
9207 
9208  // Found non-null allocation.
9209  if(nextAlloc1stIndex < suballoc1stCount)
9210  {
9211  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9212 
9213  // 1. Process free space before this allocation.
9214  if(lastOffset < suballoc.offset)
9215  {
9216  // There is free space from lastOffset to suballoc.offset.
9217  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9218  inoutStats.unusedSize += unusedRangeSize;
9219  ++inoutStats.unusedRangeCount;
9220  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9221  }
9222 
9223  // 2. Process this allocation.
9224  // There is allocation with suballoc.offset, suballoc.size.
9225  ++inoutStats.allocationCount;
9226 
9227  // 3. Prepare for next iteration.
9228  lastOffset = suballoc.offset + suballoc.size;
9229  ++nextAlloc1stIndex;
9230  }
9231  // We are at the end.
9232  else
9233  {
9234  if(lastOffset < freeSpace1stTo2ndEnd)
9235  {
9236  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9237  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9238  inoutStats.unusedSize += unusedRangeSize;
9239  ++inoutStats.unusedRangeCount;
9240  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9241  }
9242 
9243  // End of loop.
9244  lastOffset = freeSpace1stTo2ndEnd;
9245  }
9246  }
9247 
9248  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9249  {
9250  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9251  while(lastOffset < size)
9252  {
9253  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9254  while(nextAlloc2ndIndex != SIZE_MAX &&
9255  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9256  {
9257  --nextAlloc2ndIndex;
9258  }
9259 
9260  // Found non-null allocation.
9261  if(nextAlloc2ndIndex != SIZE_MAX)
9262  {
9263  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9264 
9265  // 1. Process free space before this allocation.
9266  if(lastOffset < suballoc.offset)
9267  {
9268  // There is free space from lastOffset to suballoc.offset.
9269  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9270  inoutStats.unusedSize += unusedRangeSize;
9271  ++inoutStats.unusedRangeCount;
9272  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9273  }
9274 
9275  // 2. Process this allocation.
9276  // There is allocation with suballoc.offset, suballoc.size.
9277  ++inoutStats.allocationCount;
9278 
9279  // 3. Prepare for next iteration.
9280  lastOffset = suballoc.offset + suballoc.size;
9281  --nextAlloc2ndIndex;
9282  }
9283  // We are at the end.
9284  else
9285  {
9286  if(lastOffset < size)
9287  {
9288  // There is free space from lastOffset to size.
9289  const VkDeviceSize unusedRangeSize = size - lastOffset;
9290  inoutStats.unusedSize += unusedRangeSize;
9291  ++inoutStats.unusedRangeCount;
9292  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9293  }
9294 
9295  // End of loop.
9296  lastOffset = size;
9297  }
9298  }
9299  }
9300 }
9301 
9302 #if VMA_STATS_STRING_ENABLED
9303 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9304 {
9305  const VkDeviceSize size = GetSize();
9306  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9307  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9308  const size_t suballoc1stCount = suballocations1st.size();
9309  const size_t suballoc2ndCount = suballocations2nd.size();
9310 
9311  // FIRST PASS
9312 
9313  size_t unusedRangeCount = 0;
9314  VkDeviceSize usedBytes = 0;
9315 
9316  VkDeviceSize lastOffset = 0;
9317 
9318  size_t alloc2ndCount = 0;
9319  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9320  {
9321  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9322  size_t nextAlloc2ndIndex = 0;
9323  while(lastOffset < freeSpace2ndTo1stEnd)
9324  {
9325  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9326  while(nextAlloc2ndIndex < suballoc2ndCount &&
9327  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9328  {
9329  ++nextAlloc2ndIndex;
9330  }
9331 
9332  // Found non-null allocation.
9333  if(nextAlloc2ndIndex < suballoc2ndCount)
9334  {
9335  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9336 
9337  // 1. Process free space before this allocation.
9338  if(lastOffset < suballoc.offset)
9339  {
9340  // There is free space from lastOffset to suballoc.offset.
9341  ++unusedRangeCount;
9342  }
9343 
9344  // 2. Process this allocation.
9345  // There is allocation with suballoc.offset, suballoc.size.
9346  ++alloc2ndCount;
9347  usedBytes += suballoc.size;
9348 
9349  // 3. Prepare for next iteration.
9350  lastOffset = suballoc.offset + suballoc.size;
9351  ++nextAlloc2ndIndex;
9352  }
9353  // We are at the end.
9354  else
9355  {
9356  if(lastOffset < freeSpace2ndTo1stEnd)
9357  {
9358  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9359  ++unusedRangeCount;
9360  }
9361 
9362  // End of loop.
9363  lastOffset = freeSpace2ndTo1stEnd;
9364  }
9365  }
9366  }
9367 
9368  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9369  size_t alloc1stCount = 0;
9370  const VkDeviceSize freeSpace1stTo2ndEnd =
9371  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9372  while(lastOffset < freeSpace1stTo2ndEnd)
9373  {
9374  // Find next non-null allocation or move nextAllocIndex to the end.
9375  while(nextAlloc1stIndex < suballoc1stCount &&
9376  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9377  {
9378  ++nextAlloc1stIndex;
9379  }
9380 
9381  // Found non-null allocation.
9382  if(nextAlloc1stIndex < suballoc1stCount)
9383  {
9384  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9385 
9386  // 1. Process free space before this allocation.
9387  if(lastOffset < suballoc.offset)
9388  {
9389  // There is free space from lastOffset to suballoc.offset.
9390  ++unusedRangeCount;
9391  }
9392 
9393  // 2. Process this allocation.
9394  // There is allocation with suballoc.offset, suballoc.size.
9395  ++alloc1stCount;
9396  usedBytes += suballoc.size;
9397 
9398  // 3. Prepare for next iteration.
9399  lastOffset = suballoc.offset + suballoc.size;
9400  ++nextAlloc1stIndex;
9401  }
9402  // We are at the end.
9403  else
9404  {
9405  if(lastOffset < size)
9406  {
9407  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9408  ++unusedRangeCount;
9409  }
9410 
9411  // End of loop.
9412  lastOffset = freeSpace1stTo2ndEnd;
9413  }
9414  }
9415 
9416  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9417  {
9418  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9419  while(lastOffset < size)
9420  {
9421  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9422  while(nextAlloc2ndIndex != SIZE_MAX &&
9423  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9424  {
9425  --nextAlloc2ndIndex;
9426  }
9427 
9428  // Found non-null allocation.
9429  if(nextAlloc2ndIndex != SIZE_MAX)
9430  {
9431  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9432 
9433  // 1. Process free space before this allocation.
9434  if(lastOffset < suballoc.offset)
9435  {
9436  // There is free space from lastOffset to suballoc.offset.
9437  ++unusedRangeCount;
9438  }
9439 
9440  // 2. Process this allocation.
9441  // There is allocation with suballoc.offset, suballoc.size.
9442  ++alloc2ndCount;
9443  usedBytes += suballoc.size;
9444 
9445  // 3. Prepare for next iteration.
9446  lastOffset = suballoc.offset + suballoc.size;
9447  --nextAlloc2ndIndex;
9448  }
9449  // We are at the end.
9450  else
9451  {
9452  if(lastOffset < size)
9453  {
9454  // There is free space from lastOffset to size.
9455  ++unusedRangeCount;
9456  }
9457 
9458  // End of loop.
9459  lastOffset = size;
9460  }
9461  }
9462  }
9463 
9464  const VkDeviceSize unusedBytes = size - usedBytes;
9465  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9466 
9467  // SECOND PASS
9468  lastOffset = 0;
9469 
9470  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9471  {
9472  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9473  size_t nextAlloc2ndIndex = 0;
9474  while(lastOffset < freeSpace2ndTo1stEnd)
9475  {
9476  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9477  while(nextAlloc2ndIndex < suballoc2ndCount &&
9478  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9479  {
9480  ++nextAlloc2ndIndex;
9481  }
9482 
9483  // Found non-null allocation.
9484  if(nextAlloc2ndIndex < suballoc2ndCount)
9485  {
9486  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9487 
9488  // 1. Process free space before this allocation.
9489  if(lastOffset < suballoc.offset)
9490  {
9491  // There is free space from lastOffset to suballoc.offset.
9492  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9493  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9494  }
9495 
9496  // 2. Process this allocation.
9497  // There is allocation with suballoc.offset, suballoc.size.
9498  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9499 
9500  // 3. Prepare for next iteration.
9501  lastOffset = suballoc.offset + suballoc.size;
9502  ++nextAlloc2ndIndex;
9503  }
9504  // We are at the end.
9505  else
9506  {
9507  if(lastOffset < freeSpace2ndTo1stEnd)
9508  {
9509  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9510  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9511  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9512  }
9513 
9514  // End of loop.
9515  lastOffset = freeSpace2ndTo1stEnd;
9516  }
9517  }
9518  }
9519 
9520  nextAlloc1stIndex = m_1stNullItemsBeginCount;
9521  while(lastOffset < freeSpace1stTo2ndEnd)
9522  {
9523  // Find next non-null allocation or move nextAllocIndex to the end.
9524  while(nextAlloc1stIndex < suballoc1stCount &&
9525  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9526  {
9527  ++nextAlloc1stIndex;
9528  }
9529 
9530  // Found non-null allocation.
9531  if(nextAlloc1stIndex < suballoc1stCount)
9532  {
9533  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9534 
9535  // 1. Process free space before this allocation.
9536  if(lastOffset < suballoc.offset)
9537  {
9538  // There is free space from lastOffset to suballoc.offset.
9539  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9540  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9541  }
9542 
9543  // 2. Process this allocation.
9544  // There is allocation with suballoc.offset, suballoc.size.
9545  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9546 
9547  // 3. Prepare for next iteration.
9548  lastOffset = suballoc.offset + suballoc.size;
9549  ++nextAlloc1stIndex;
9550  }
9551  // We are at the end.
9552  else
9553  {
9554  if(lastOffset < freeSpace1stTo2ndEnd)
9555  {
9556  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9557  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9558  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9559  }
9560 
9561  // End of loop.
9562  lastOffset = freeSpace1stTo2ndEnd;
9563  }
9564  }
9565 
9566  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9567  {
9568  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9569  while(lastOffset < size)
9570  {
9571  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9572  while(nextAlloc2ndIndex != SIZE_MAX &&
9573  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9574  {
9575  --nextAlloc2ndIndex;
9576  }
9577 
9578  // Found non-null allocation.
9579  if(nextAlloc2ndIndex != SIZE_MAX)
9580  {
9581  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9582 
9583  // 1. Process free space before this allocation.
9584  if(lastOffset < suballoc.offset)
9585  {
9586  // There is free space from lastOffset to suballoc.offset.
9587  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9588  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9589  }
9590 
9591  // 2. Process this allocation.
9592  // There is allocation with suballoc.offset, suballoc.size.
9593  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9594 
9595  // 3. Prepare for next iteration.
9596  lastOffset = suballoc.offset + suballoc.size;
9597  --nextAlloc2ndIndex;
9598  }
9599  // We are at the end.
9600  else
9601  {
9602  if(lastOffset < size)
9603  {
9604  // There is free space from lastOffset to size.
9605  const VkDeviceSize unusedRangeSize = size - lastOffset;
9606  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9607  }
9608 
9609  // End of loop.
9610  lastOffset = size;
9611  }
9612  }
9613  }
9614 
9615  PrintDetailedMap_End(json);
9616 }
9617 #endif // #if VMA_STATS_STRING_ENABLED
9618 
9619 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9620  uint32_t currentFrameIndex,
9621  uint32_t frameInUseCount,
9622  VkDeviceSize bufferImageGranularity,
9623  VkDeviceSize allocSize,
9624  VkDeviceSize allocAlignment,
9625  bool upperAddress,
9626  VmaSuballocationType allocType,
9627  bool canMakeOtherLost,
9628  uint32_t strategy,
9629  VmaAllocationRequest* pAllocationRequest)
9630 {
9631  VMA_ASSERT(allocSize > 0);
9632  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9633  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9634  VMA_HEAVY_ASSERT(Validate());
9635 
9636  const VkDeviceSize size = GetSize();
9637  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9638  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9639 
9640  if(upperAddress)
9641  {
9642  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9643  {
9644  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9645  return false;
9646  }
9647 
9648  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9649  if(allocSize > size)
9650  {
9651  return false;
9652  }
9653  VkDeviceSize resultBaseOffset = size - allocSize;
9654  if(!suballocations2nd.empty())
9655  {
9656  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9657  resultBaseOffset = lastSuballoc.offset - allocSize;
9658  if(allocSize > lastSuballoc.offset)
9659  {
9660  return false;
9661  }
9662  }
9663 
9664  // Start from offset equal to end of free space.
9665  VkDeviceSize resultOffset = resultBaseOffset;
9666 
9667  // Apply VMA_DEBUG_MARGIN at the end.
9668  if(VMA_DEBUG_MARGIN > 0)
9669  {
9670  if(resultOffset < VMA_DEBUG_MARGIN)
9671  {
9672  return false;
9673  }
9674  resultOffset -= VMA_DEBUG_MARGIN;
9675  }
9676 
9677  // Apply alignment.
9678  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9679 
9680  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9681  // Make bigger alignment if necessary.
9682  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9683  {
9684  bool bufferImageGranularityConflict = false;
9685  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9686  {
9687  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9688  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9689  {
9690  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9691  {
9692  bufferImageGranularityConflict = true;
9693  break;
9694  }
9695  }
9696  else
9697  // Already on previous page.
9698  break;
9699  }
9700  if(bufferImageGranularityConflict)
9701  {
9702  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9703  }
9704  }
9705 
9706  // There is enough free space.
9707  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9708  suballocations1st.back().offset + suballocations1st.back().size :
9709  0;
9710  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9711  {
9712  // Check previous suballocations for BufferImageGranularity conflicts.
9713  // If conflict exists, allocation cannot be made here.
9714  if(bufferImageGranularity > 1)
9715  {
9716  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9717  {
9718  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9719  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9720  {
9721  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9722  {
9723  return false;
9724  }
9725  }
9726  else
9727  {
9728  // Already on next page.
9729  break;
9730  }
9731  }
9732  }
9733 
9734  // All tests passed: Success.
9735  pAllocationRequest->offset = resultOffset;
9736  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9737  pAllocationRequest->sumItemSize = 0;
9738  // pAllocationRequest->item unused.
9739  pAllocationRequest->itemsToMakeLostCount = 0;
9740  return true;
9741  }
9742  }
9743  else // !upperAddress
9744  {
9745  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9746  {
9747  // Try to allocate at the end of 1st vector.
9748 
9749  VkDeviceSize resultBaseOffset = 0;
9750  if(!suballocations1st.empty())
9751  {
9752  const VmaSuballocation& lastSuballoc = suballocations1st.back();
9753  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9754  }
9755 
9756  // Start from offset equal to beginning of free space.
9757  VkDeviceSize resultOffset = resultBaseOffset;
9758 
9759  // Apply VMA_DEBUG_MARGIN at the beginning.
9760  if(VMA_DEBUG_MARGIN > 0)
9761  {
9762  resultOffset += VMA_DEBUG_MARGIN;
9763  }
9764 
9765  // Apply alignment.
9766  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9767 
9768  // Check previous suballocations for BufferImageGranularity conflicts.
9769  // Make bigger alignment if necessary.
9770  if(bufferImageGranularity > 1 && !suballocations1st.empty())
9771  {
9772  bool bufferImageGranularityConflict = false;
9773  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9774  {
9775  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9776  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9777  {
9778  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9779  {
9780  bufferImageGranularityConflict = true;
9781  break;
9782  }
9783  }
9784  else
9785  // Already on previous page.
9786  break;
9787  }
9788  if(bufferImageGranularityConflict)
9789  {
9790  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9791  }
9792  }
9793 
9794  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9795  suballocations2nd.back().offset : size;
9796 
9797  // There is enough free space at the end after alignment.
9798  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9799  {
9800  // Check next suballocations for BufferImageGranularity conflicts.
9801  // If conflict exists, allocation cannot be made here.
9802  if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9803  {
9804  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9805  {
9806  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9807  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9808  {
9809  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9810  {
9811  return false;
9812  }
9813  }
9814  else
9815  {
9816  // Already on previous page.
9817  break;
9818  }
9819  }
9820  }
9821 
9822  // All tests passed: Success.
9823  pAllocationRequest->offset = resultOffset;
9824  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9825  pAllocationRequest->sumItemSize = 0;
9826  // pAllocationRequest->item unused.
9827  pAllocationRequest->itemsToMakeLostCount = 0;
9828  return true;
9829  }
9830  }
9831 
9832  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9833  // beginning of 1st vector as the end of free space.
9834  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9835  {
9836  VMA_ASSERT(!suballocations1st.empty());
9837 
9838  VkDeviceSize resultBaseOffset = 0;
9839  if(!suballocations2nd.empty())
9840  {
9841  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9842  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9843  }
9844 
9845  // Start from offset equal to beginning of free space.
9846  VkDeviceSize resultOffset = resultBaseOffset;
9847 
9848  // Apply VMA_DEBUG_MARGIN at the beginning.
9849  if(VMA_DEBUG_MARGIN > 0)
9850  {
9851  resultOffset += VMA_DEBUG_MARGIN;
9852  }
9853 
9854  // Apply alignment.
9855  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9856 
9857  // Check previous suballocations for BufferImageGranularity conflicts.
9858  // Make bigger alignment if necessary.
9859  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9860  {
9861  bool bufferImageGranularityConflict = false;
9862  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9863  {
9864  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9865  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9866  {
9867  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9868  {
9869  bufferImageGranularityConflict = true;
9870  break;
9871  }
9872  }
9873  else
9874  // Already on previous page.
9875  break;
9876  }
9877  if(bufferImageGranularityConflict)
9878  {
9879  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9880  }
9881  }
9882 
9883  pAllocationRequest->itemsToMakeLostCount = 0;
9884  pAllocationRequest->sumItemSize = 0;
9885  size_t index1st = m_1stNullItemsBeginCount;
9886 
9887  if(canMakeOtherLost)
9888  {
9889  while(index1st < suballocations1st.size() &&
9890  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9891  {
9892  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9893  const VmaSuballocation& suballoc = suballocations1st[index1st];
9894  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9895  {
9896  // No problem.
9897  }
9898  else
9899  {
9900  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9901  if(suballoc.hAllocation->CanBecomeLost() &&
9902  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9903  {
9904  ++pAllocationRequest->itemsToMakeLostCount;
9905  pAllocationRequest->sumItemSize += suballoc.size;
9906  }
9907  else
9908  {
9909  return false;
9910  }
9911  }
9912  ++index1st;
9913  }
9914 
9915  // Check next suballocations for BufferImageGranularity conflicts.
9916  // If conflict exists, we must mark more allocations lost or fail.
9917  if(bufferImageGranularity > 1)
9918  {
9919  while(index1st < suballocations1st.size())
9920  {
9921  const VmaSuballocation& suballoc = suballocations1st[index1st];
9922  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9923  {
9924  if(suballoc.hAllocation != VK_NULL_HANDLE)
9925  {
9926  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9927  if(suballoc.hAllocation->CanBecomeLost() &&
9928  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9929  {
9930  ++pAllocationRequest->itemsToMakeLostCount;
9931  pAllocationRequest->sumItemSize += suballoc.size;
9932  }
9933  else
9934  {
9935  return false;
9936  }
9937  }
9938  }
9939  else
9940  {
9941  // Already on next page.
9942  break;
9943  }
9944  ++index1st;
9945  }
9946  }
9947  }
9948 
9949  // There is enough free space at the end after alignment.
9950  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9951  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9952  {
9953  // Check next suballocations for BufferImageGranularity conflicts.
9954  // If conflict exists, allocation cannot be made here.
9955  if(bufferImageGranularity > 1)
9956  {
9957  for(size_t nextSuballocIndex = index1st;
9958  nextSuballocIndex < suballocations1st.size();
9959  nextSuballocIndex++)
9960  {
9961  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9962  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9963  {
9964  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9965  {
9966  return false;
9967  }
9968  }
9969  else
9970  {
9971  // Already on next page.
9972  break;
9973  }
9974  }
9975  }
9976 
9977  // All tests passed: Success.
9978  pAllocationRequest->offset = resultOffset;
9979  pAllocationRequest->sumFreeSize =
9980  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9981  - resultBaseOffset
9982  - pAllocationRequest->sumItemSize;
9983  // pAllocationRequest->item unused.
9984  return true;
9985  }
9986  }
9987  }
9988 
9989  return false;
9990 }
9991 
9992 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
9993  uint32_t currentFrameIndex,
9994  uint32_t frameInUseCount,
9995  VmaAllocationRequest* pAllocationRequest)
9996 {
9997  if(pAllocationRequest->itemsToMakeLostCount == 0)
9998  {
9999  return true;
10000  }
10001 
10002  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10003 
10004  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10005  size_t index1st = m_1stNullItemsBeginCount;
10006  size_t madeLostCount = 0;
10007  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10008  {
10009  VMA_ASSERT(index1st < suballocations1st.size());
10010  VmaSuballocation& suballoc = suballocations1st[index1st];
10011  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10012  {
10013  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10014  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10015  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10016  {
10017  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10018  suballoc.hAllocation = VK_NULL_HANDLE;
10019  m_SumFreeSize += suballoc.size;
10020  ++m_1stNullItemsMiddleCount;
10021  ++madeLostCount;
10022  }
10023  else
10024  {
10025  return false;
10026  }
10027  }
10028  ++index1st;
10029  }
10030 
10031  CleanupAfterFree();
10032  //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10033 
10034  return true;
10035 }
10036 
10037 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10038 {
10039  uint32_t lostAllocationCount = 0;
10040 
10041  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10042  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10043  {
10044  VmaSuballocation& suballoc = suballocations1st[i];
10045  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10046  suballoc.hAllocation->CanBecomeLost() &&
10047  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10048  {
10049  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10050  suballoc.hAllocation = VK_NULL_HANDLE;
10051  ++m_1stNullItemsMiddleCount;
10052  m_SumFreeSize += suballoc.size;
10053  ++lostAllocationCount;
10054  }
10055  }
10056 
10057  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10058  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10059  {
10060  VmaSuballocation& suballoc = suballocations2nd[i];
10061  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10062  suballoc.hAllocation->CanBecomeLost() &&
10063  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10064  {
10065  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10066  suballoc.hAllocation = VK_NULL_HANDLE;
10067  ++m_2ndNullItemsCount;
10068  ++lostAllocationCount;
10069  }
10070  }
10071 
10072  if(lostAllocationCount)
10073  {
10074  CleanupAfterFree();
10075  }
10076 
10077  return lostAllocationCount;
10078 }
10079 
10080 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10081 {
10082  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10083  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10084  {
10085  const VmaSuballocation& suballoc = suballocations1st[i];
10086  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10087  {
10088  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10089  {
10090  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10091  return VK_ERROR_VALIDATION_FAILED_EXT;
10092  }
10093  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10094  {
10095  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10096  return VK_ERROR_VALIDATION_FAILED_EXT;
10097  }
10098  }
10099  }
10100 
10101  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10102  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10103  {
10104  const VmaSuballocation& suballoc = suballocations2nd[i];
10105  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10106  {
10107  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10108  {
10109  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10110  return VK_ERROR_VALIDATION_FAILED_EXT;
10111  }
10112  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10113  {
10114  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10115  return VK_ERROR_VALIDATION_FAILED_EXT;
10116  }
10117  }
10118  }
10119 
10120  return VK_SUCCESS;
10121 }
10122 
10123 void VmaBlockMetadata_Linear::Alloc(
10124  const VmaAllocationRequest& request,
10125  VmaSuballocationType type,
10126  VkDeviceSize allocSize,
10127  bool upperAddress,
10128  VmaAllocation hAllocation)
10129 {
10130  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10131 
10132  if(upperAddress)
10133  {
10134  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10135  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10136  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10137  suballocations2nd.push_back(newSuballoc);
10138  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10139  }
10140  else
10141  {
10142  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10143 
10144  // First allocation.
10145  if(suballocations1st.empty())
10146  {
10147  suballocations1st.push_back(newSuballoc);
10148  }
10149  else
10150  {
10151  // New allocation at the end of 1st vector.
10152  if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10153  {
10154  // Check if it fits before the end of the block.
10155  VMA_ASSERT(request.offset + allocSize <= GetSize());
10156  suballocations1st.push_back(newSuballoc);
10157  }
10158  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10159  else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10160  {
10161  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10162 
10163  switch(m_2ndVectorMode)
10164  {
10165  case SECOND_VECTOR_EMPTY:
10166  // First allocation from second part ring buffer.
10167  VMA_ASSERT(suballocations2nd.empty());
10168  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10169  break;
10170  case SECOND_VECTOR_RING_BUFFER:
10171  // 2-part ring buffer is already started.
10172  VMA_ASSERT(!suballocations2nd.empty());
10173  break;
10174  case SECOND_VECTOR_DOUBLE_STACK:
10175  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10176  break;
10177  default:
10178  VMA_ASSERT(0);
10179  }
10180 
10181  suballocations2nd.push_back(newSuballoc);
10182  }
10183  else
10184  {
10185  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10186  }
10187  }
10188  }
10189 
10190  m_SumFreeSize -= newSuballoc.size;
10191 }
10192 
10193 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10194 {
10195  FreeAtOffset(allocation->GetOffset());
10196 }
10197 
10198 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10199 {
10200  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10201  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10202 
10203  if(!suballocations1st.empty())
10204  {
10205  // First allocation: Mark it as next empty at the beginning.
10206  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10207  if(firstSuballoc.offset == offset)
10208  {
10209  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10210  firstSuballoc.hAllocation = VK_NULL_HANDLE;
10211  m_SumFreeSize += firstSuballoc.size;
10212  ++m_1stNullItemsBeginCount;
10213  CleanupAfterFree();
10214  return;
10215  }
10216  }
10217 
10218  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10219  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10220  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10221  {
10222  VmaSuballocation& lastSuballoc = suballocations2nd.back();
10223  if(lastSuballoc.offset == offset)
10224  {
10225  m_SumFreeSize += lastSuballoc.size;
10226  suballocations2nd.pop_back();
10227  CleanupAfterFree();
10228  return;
10229  }
10230  }
10231  // Last allocation in 1st vector.
10232  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10233  {
10234  VmaSuballocation& lastSuballoc = suballocations1st.back();
10235  if(lastSuballoc.offset == offset)
10236  {
10237  m_SumFreeSize += lastSuballoc.size;
10238  suballocations1st.pop_back();
10239  CleanupAfterFree();
10240  return;
10241  }
10242  }
10243 
10244  // Item from the middle of 1st vector.
10245  {
10246  VmaSuballocation refSuballoc;
10247  refSuballoc.offset = offset;
10248  // Rest of members stays uninitialized intentionally for better performance.
10249  SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10250  suballocations1st.begin() + m_1stNullItemsBeginCount,
10251  suballocations1st.end(),
10252  refSuballoc);
10253  if(it != suballocations1st.end())
10254  {
10255  it->type = VMA_SUBALLOCATION_TYPE_FREE;
10256  it->hAllocation = VK_NULL_HANDLE;
10257  ++m_1stNullItemsMiddleCount;
10258  m_SumFreeSize += it->size;
10259  CleanupAfterFree();
10260  return;
10261  }
10262  }
10263 
10264  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10265  {
10266  // Item from the middle of 2nd vector.
10267  VmaSuballocation refSuballoc;
10268  refSuballoc.offset = offset;
10269  // Rest of members stays uninitialized intentionally for better performance.
10270  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10271  VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10272  VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10273  if(it != suballocations2nd.end())
10274  {
10275  it->type = VMA_SUBALLOCATION_TYPE_FREE;
10276  it->hAllocation = VK_NULL_HANDLE;
10277  ++m_2ndNullItemsCount;
10278  m_SumFreeSize += it->size;
10279  CleanupAfterFree();
10280  return;
10281  }
10282  }
10283 
10284  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10285 }
10286 
10287 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10288 {
10289  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10290  const size_t suballocCount = AccessSuballocations1st().size();
10291  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10292 }
10293 
10294 void VmaBlockMetadata_Linear::CleanupAfterFree()
10295 {
10296  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10297  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10298 
10299  if(IsEmpty())
10300  {
10301  suballocations1st.clear();
10302  suballocations2nd.clear();
10303  m_1stNullItemsBeginCount = 0;
10304  m_1stNullItemsMiddleCount = 0;
10305  m_2ndNullItemsCount = 0;
10306  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10307  }
10308  else
10309  {
10310  const size_t suballoc1stCount = suballocations1st.size();
10311  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10312  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10313 
10314  // Find more null items at the beginning of 1st vector.
10315  while(m_1stNullItemsBeginCount < suballoc1stCount &&
10316  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10317  {
10318  ++m_1stNullItemsBeginCount;
10319  --m_1stNullItemsMiddleCount;
10320  }
10321 
10322  // Find more null items at the end of 1st vector.
10323  while(m_1stNullItemsMiddleCount > 0 &&
10324  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10325  {
10326  --m_1stNullItemsMiddleCount;
10327  suballocations1st.pop_back();
10328  }
10329 
10330  // Find more null items at the end of 2nd vector.
10331  while(m_2ndNullItemsCount > 0 &&
10332  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10333  {
10334  --m_2ndNullItemsCount;
10335  suballocations2nd.pop_back();
10336  }
10337 
10338  if(ShouldCompact1st())
10339  {
10340  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10341  size_t srcIndex = m_1stNullItemsBeginCount;
10342  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10343  {
10344  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10345  {
10346  ++srcIndex;
10347  }
10348  if(dstIndex != srcIndex)
10349  {
10350  suballocations1st[dstIndex] = suballocations1st[srcIndex];
10351  }
10352  ++srcIndex;
10353  }
10354  suballocations1st.resize(nonNullItemCount);
10355  m_1stNullItemsBeginCount = 0;
10356  m_1stNullItemsMiddleCount = 0;
10357  }
10358 
10359  // 2nd vector became empty.
10360  if(suballocations2nd.empty())
10361  {
10362  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10363  }
10364 
10365  // 1st vector became empty.
10366  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10367  {
10368  suballocations1st.clear();
10369  m_1stNullItemsBeginCount = 0;
10370 
10371  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10372  {
10373  // Swap 1st with 2nd. Now 2nd is empty.
10374  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10375  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10376  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10377  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10378  {
10379  ++m_1stNullItemsBeginCount;
10380  --m_1stNullItemsMiddleCount;
10381  }
10382  m_2ndNullItemsCount = 0;
10383  m_1stVectorIndex ^= 1;
10384  }
10385  }
10386  }
10387 
10388  VMA_HEAVY_ASSERT(Validate());
10389 }
10390 
10391 
10393 // class VmaBlockMetadata_Buddy
10394 
10395 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10396  VmaBlockMetadata(hAllocator),
10397  m_Root(VMA_NULL),
10398  m_AllocationCount(0),
10399  m_FreeCount(1),
10400  m_SumFreeSize(0)
10401 {
10402  memset(m_FreeList, 0, sizeof(m_FreeList));
10403 }
10404 
10405 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10406 {
10407  DeleteNode(m_Root);
10408 }
10409 
10410 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10411 {
10412  VmaBlockMetadata::Init(size);
10413 
10414  m_UsableSize = VmaPrevPow2(size);
10415  m_SumFreeSize = m_UsableSize;
10416 
10417  // Calculate m_LevelCount.
10418  m_LevelCount = 1;
10419  while(m_LevelCount < MAX_LEVELS &&
10420  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10421  {
10422  ++m_LevelCount;
10423  }
10424 
10425  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10426  rootNode->offset = 0;
10427  rootNode->type = Node::TYPE_FREE;
10428  rootNode->parent = VMA_NULL;
10429  rootNode->buddy = VMA_NULL;
10430 
10431  m_Root = rootNode;
10432  AddToFreeListFront(0, rootNode);
10433 }
10434 
10435 bool VmaBlockMetadata_Buddy::Validate() const
10436 {
10437  // Validate tree.
10438  ValidationContext ctx;
10439  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10440  {
10441  VMA_VALIDATE(false && "ValidateNode failed.");
10442  }
10443  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10444  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10445 
10446  // Validate free node lists.
10447  for(uint32_t level = 0; level < m_LevelCount; ++level)
10448  {
10449  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10450  m_FreeList[level].front->free.prev == VMA_NULL);
10451 
10452  for(Node* node = m_FreeList[level].front;
10453  node != VMA_NULL;
10454  node = node->free.next)
10455  {
10456  VMA_VALIDATE(node->type == Node::TYPE_FREE);
10457 
10458  if(node->free.next == VMA_NULL)
10459  {
10460  VMA_VALIDATE(m_FreeList[level].back == node);
10461  }
10462  else
10463  {
10464  VMA_VALIDATE(node->free.next->free.prev == node);
10465  }
10466  }
10467  }
10468 
10469  // Validate that free lists ar higher levels are empty.
10470  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10471  {
10472  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10473  }
10474 
10475  return true;
10476 }
10477 
10478 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10479 {
10480  for(uint32_t level = 0; level < m_LevelCount; ++level)
10481  {
10482  if(m_FreeList[level].front != VMA_NULL)
10483  {
10484  return LevelToNodeSize(level);
10485  }
10486  }
10487  return 0;
10488 }
10489 
10490 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10491 {
10492  const VkDeviceSize unusableSize = GetUnusableSize();
10493 
10494  outInfo.blockCount = 1;
10495 
10496  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10497  outInfo.usedBytes = outInfo.unusedBytes = 0;
10498 
10499  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10500  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10501  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10502 
10503  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10504 
10505  if(unusableSize > 0)
10506  {
10507  ++outInfo.unusedRangeCount;
10508  outInfo.unusedBytes += unusableSize;
10509  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10510  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10511  }
10512 }
10513 
10514 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10515 {
10516  const VkDeviceSize unusableSize = GetUnusableSize();
10517 
10518  inoutStats.size += GetSize();
10519  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10520  inoutStats.allocationCount += m_AllocationCount;
10521  inoutStats.unusedRangeCount += m_FreeCount;
10522  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10523 
10524  if(unusableSize > 0)
10525  {
10526  ++inoutStats.unusedRangeCount;
10527  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10528  }
10529 }
10530 
10531 #if VMA_STATS_STRING_ENABLED
10532 
10533 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10534 {
10535  // TODO optimize
10536  VmaStatInfo stat;
10537  CalcAllocationStatInfo(stat);
10538 
10539  PrintDetailedMap_Begin(
10540  json,
10541  stat.unusedBytes,
10542  stat.allocationCount,
10543  stat.unusedRangeCount);
10544 
10545  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10546 
10547  const VkDeviceSize unusableSize = GetUnusableSize();
10548  if(unusableSize > 0)
10549  {
10550  PrintDetailedMap_UnusedRange(json,
10551  m_UsableSize, // offset
10552  unusableSize); // size
10553  }
10554 
10555  PrintDetailedMap_End(json);
10556 }
10557 
10558 #endif // #if VMA_STATS_STRING_ENABLED
10559 
10560 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10561  uint32_t currentFrameIndex,
10562  uint32_t frameInUseCount,
10563  VkDeviceSize bufferImageGranularity,
10564  VkDeviceSize allocSize,
10565  VkDeviceSize allocAlignment,
10566  bool upperAddress,
10567  VmaSuballocationType allocType,
10568  bool canMakeOtherLost,
10569  uint32_t strategy,
10570  VmaAllocationRequest* pAllocationRequest)
10571 {
10572  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10573 
10574  // Simple way to respect bufferImageGranularity. May be optimized some day.
10575  // Whenever it might be an OPTIMAL image...
10576  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10577  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10578  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10579  {
10580  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10581  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10582  }
10583 
10584  if(allocSize > m_UsableSize)
10585  {
10586  return false;
10587  }
10588 
10589  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10590  for(uint32_t level = targetLevel + 1; level--; )
10591  {
10592  for(Node* freeNode = m_FreeList[level].front;
10593  freeNode != VMA_NULL;
10594  freeNode = freeNode->free.next)
10595  {
10596  if(freeNode->offset % allocAlignment == 0)
10597  {
10598  pAllocationRequest->offset = freeNode->offset;
10599  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10600  pAllocationRequest->sumItemSize = 0;
10601  pAllocationRequest->itemsToMakeLostCount = 0;
10602  pAllocationRequest->customData = (void*)(uintptr_t)level;
10603  return true;
10604  }
10605  }
10606  }
10607 
10608  return false;
10609 }
10610 
10611 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10612  uint32_t currentFrameIndex,
10613  uint32_t frameInUseCount,
10614  VmaAllocationRequest* pAllocationRequest)
10615 {
10616  /*
10617  Lost allocations are not supported in buddy allocator at the moment.
10618  Support might be added in the future.
10619  */
10620  return pAllocationRequest->itemsToMakeLostCount == 0;
10621 }
10622 
10623 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10624 {
10625  /*
10626  Lost allocations are not supported in buddy allocator at the moment.
10627  Support might be added in the future.
10628  */
10629  return 0;
10630 }
10631 
10632 void VmaBlockMetadata_Buddy::Alloc(
10633  const VmaAllocationRequest& request,
10634  VmaSuballocationType type,
10635  VkDeviceSize allocSize,
10636  bool upperAddress,
10637  VmaAllocation hAllocation)
10638 {
10639  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10640  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10641 
10642  Node* currNode = m_FreeList[currLevel].front;
10643  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10644  while(currNode->offset != request.offset)
10645  {
10646  currNode = currNode->free.next;
10647  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10648  }
10649 
10650  // Go down, splitting free nodes.
10651  while(currLevel < targetLevel)
10652  {
10653  // currNode is already first free node at currLevel.
10654  // Remove it from list of free nodes at this currLevel.
10655  RemoveFromFreeList(currLevel, currNode);
10656 
10657  const uint32_t childrenLevel = currLevel + 1;
10658 
10659  // Create two free sub-nodes.
10660  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10661  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10662 
10663  leftChild->offset = currNode->offset;
10664  leftChild->type = Node::TYPE_FREE;
10665  leftChild->parent = currNode;
10666  leftChild->buddy = rightChild;
10667 
10668  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10669  rightChild->type = Node::TYPE_FREE;
10670  rightChild->parent = currNode;
10671  rightChild->buddy = leftChild;
10672 
10673  // Convert current currNode to split type.
10674  currNode->type = Node::TYPE_SPLIT;
10675  currNode->split.leftChild = leftChild;
10676 
10677  // Add child nodes to free list. Order is important!
10678  AddToFreeListFront(childrenLevel, rightChild);
10679  AddToFreeListFront(childrenLevel, leftChild);
10680 
10681  ++m_FreeCount;
10682  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10683  ++currLevel;
10684  currNode = m_FreeList[currLevel].front;
10685 
10686  /*
10687  We can be sure that currNode, as left child of node previously split,
10688  also fullfills the alignment requirement.
10689  */
10690  }
10691 
10692  // Remove from free list.
10693  VMA_ASSERT(currLevel == targetLevel &&
10694  currNode != VMA_NULL &&
10695  currNode->type == Node::TYPE_FREE);
10696  RemoveFromFreeList(currLevel, currNode);
10697 
10698  // Convert to allocation node.
10699  currNode->type = Node::TYPE_ALLOCATION;
10700  currNode->allocation.alloc = hAllocation;
10701 
10702  ++m_AllocationCount;
10703  --m_FreeCount;
10704  m_SumFreeSize -= allocSize;
10705 }
10706 
10707 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10708 {
10709  if(node->type == Node::TYPE_SPLIT)
10710  {
10711  DeleteNode(node->split.leftChild->buddy);
10712  DeleteNode(node->split.leftChild);
10713  }
10714 
10715  vma_delete(GetAllocationCallbacks(), node);
10716 }
10717 
10718 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10719 {
10720  VMA_VALIDATE(level < m_LevelCount);
10721  VMA_VALIDATE(curr->parent == parent);
10722  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10723  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10724  switch(curr->type)
10725  {
10726  case Node::TYPE_FREE:
10727  // curr->free.prev, next are validated separately.
10728  ctx.calculatedSumFreeSize += levelNodeSize;
10729  ++ctx.calculatedFreeCount;
10730  break;
10731  case Node::TYPE_ALLOCATION:
10732  ++ctx.calculatedAllocationCount;
10733  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10734  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10735  break;
10736  case Node::TYPE_SPLIT:
10737  {
10738  const uint32_t childrenLevel = level + 1;
10739  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10740  const Node* const leftChild = curr->split.leftChild;
10741  VMA_VALIDATE(leftChild != VMA_NULL);
10742  VMA_VALIDATE(leftChild->offset == curr->offset);
10743  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10744  {
10745  VMA_VALIDATE(false && "ValidateNode for left child failed.");
10746  }
10747  const Node* const rightChild = leftChild->buddy;
10748  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10749  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10750  {
10751  VMA_VALIDATE(false && "ValidateNode for right child failed.");
10752  }
10753  }
10754  break;
10755  default:
10756  return false;
10757  }
10758 
10759  return true;
10760 }
10761 
10762 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10763 {
10764  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10765  uint32_t level = 0;
10766  VkDeviceSize currLevelNodeSize = m_UsableSize;
10767  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10768  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10769  {
10770  ++level;
10771  currLevelNodeSize = nextLevelNodeSize;
10772  nextLevelNodeSize = currLevelNodeSize >> 1;
10773  }
10774  return level;
10775 }
10776 
10777 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10778 {
10779  // Find node and level.
10780  Node* node = m_Root;
10781  VkDeviceSize nodeOffset = 0;
10782  uint32_t level = 0;
10783  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10784  while(node->type == Node::TYPE_SPLIT)
10785  {
10786  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10787  if(offset < nodeOffset + nextLevelSize)
10788  {
10789  node = node->split.leftChild;
10790  }
10791  else
10792  {
10793  node = node->split.leftChild->buddy;
10794  nodeOffset += nextLevelSize;
10795  }
10796  ++level;
10797  levelNodeSize = nextLevelSize;
10798  }
10799 
10800  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10801  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10802 
10803  ++m_FreeCount;
10804  --m_AllocationCount;
10805  m_SumFreeSize += alloc->GetSize();
10806 
10807  node->type = Node::TYPE_FREE;
10808 
10809  // Join free nodes if possible.
10810  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10811  {
10812  RemoveFromFreeList(level, node->buddy);
10813  Node* const parent = node->parent;
10814 
10815  vma_delete(GetAllocationCallbacks(), node->buddy);
10816  vma_delete(GetAllocationCallbacks(), node);
10817  parent->type = Node::TYPE_FREE;
10818 
10819  node = parent;
10820  --level;
10821  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10822  --m_FreeCount;
10823  }
10824 
10825  AddToFreeListFront(level, node);
10826 }
10827 
10828 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10829 {
10830  switch(node->type)
10831  {
10832  case Node::TYPE_FREE:
10833  ++outInfo.unusedRangeCount;
10834  outInfo.unusedBytes += levelNodeSize;
10835  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10836  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10837  break;
10838  case Node::TYPE_ALLOCATION:
10839  {
10840  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10841  ++outInfo.allocationCount;
10842  outInfo.usedBytes += allocSize;
10843  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10844  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10845 
10846  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10847  if(unusedRangeSize > 0)
10848  {
10849  ++outInfo.unusedRangeCount;
10850  outInfo.unusedBytes += unusedRangeSize;
10851  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10852  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10853  }
10854  }
10855  break;
10856  case Node::TYPE_SPLIT:
10857  {
10858  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10859  const Node* const leftChild = node->split.leftChild;
10860  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10861  const Node* const rightChild = leftChild->buddy;
10862  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10863  }
10864  break;
10865  default:
10866  VMA_ASSERT(0);
10867  }
10868 }
10869 
10870 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10871 {
10872  VMA_ASSERT(node->type == Node::TYPE_FREE);
10873 
10874  // List is empty.
10875  Node* const frontNode = m_FreeList[level].front;
10876  if(frontNode == VMA_NULL)
10877  {
10878  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10879  node->free.prev = node->free.next = VMA_NULL;
10880  m_FreeList[level].front = m_FreeList[level].back = node;
10881  }
10882  else
10883  {
10884  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10885  node->free.prev = VMA_NULL;
10886  node->free.next = frontNode;
10887  frontNode->free.prev = node;
10888  m_FreeList[level].front = node;
10889  }
10890 }
10891 
10892 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10893 {
10894  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10895 
10896  // It is at the front.
10897  if(node->free.prev == VMA_NULL)
10898  {
10899  VMA_ASSERT(m_FreeList[level].front == node);
10900  m_FreeList[level].front = node->free.next;
10901  }
10902  else
10903  {
10904  Node* const prevFreeNode = node->free.prev;
10905  VMA_ASSERT(prevFreeNode->free.next == node);
10906  prevFreeNode->free.next = node->free.next;
10907  }
10908 
10909  // It is at the back.
10910  if(node->free.next == VMA_NULL)
10911  {
10912  VMA_ASSERT(m_FreeList[level].back == node);
10913  m_FreeList[level].back = node->free.prev;
10914  }
10915  else
10916  {
10917  Node* const nextFreeNode = node->free.next;
10918  VMA_ASSERT(nextFreeNode->free.prev == node);
10919  nextFreeNode->free.prev = node->free.prev;
10920  }
10921 }
10922 
10923 #if VMA_STATS_STRING_ENABLED
10924 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10925 {
10926  switch(node->type)
10927  {
10928  case Node::TYPE_FREE:
10929  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10930  break;
10931  case Node::TYPE_ALLOCATION:
10932  {
10933  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10934  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10935  if(allocSize < levelNodeSize)
10936  {
10937  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10938  }
10939  }
10940  break;
10941  case Node::TYPE_SPLIT:
10942  {
10943  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10944  const Node* const leftChild = node->split.leftChild;
10945  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10946  const Node* const rightChild = leftChild->buddy;
10947  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10948  }
10949  break;
10950  default:
10951  VMA_ASSERT(0);
10952  }
10953 }
10954 #endif // #if VMA_STATS_STRING_ENABLED
10955 
10956 
10958 // class VmaDeviceMemoryBlock
10959 
10960 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10961  m_pMetadata(VMA_NULL),
10962  m_MemoryTypeIndex(UINT32_MAX),
10963  m_Id(0),
10964  m_hMemory(VK_NULL_HANDLE),
10965  m_MapCount(0),
10966  m_pMappedData(VMA_NULL)
10967 {
10968 }
10969 
10970 void VmaDeviceMemoryBlock::Init(
10971  VmaAllocator hAllocator,
10972  uint32_t newMemoryTypeIndex,
10973  VkDeviceMemory newMemory,
10974  VkDeviceSize newSize,
10975  uint32_t id,
10976  uint32_t algorithm)
10977 {
10978  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10979 
10980  m_MemoryTypeIndex = newMemoryTypeIndex;
10981  m_Id = id;
10982  m_hMemory = newMemory;
10983 
10984  switch(algorithm)
10985  {
10987  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
10988  break;
10990  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
10991  break;
10992  default:
10993  VMA_ASSERT(0);
10994  // Fall-through.
10995  case 0:
10996  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
10997  }
10998  m_pMetadata->Init(newSize);
10999 }
11000 
11001 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11002 {
11003  // This is the most important assert in the entire library.
11004  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11005  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11006 
11007  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11008  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11009  m_hMemory = VK_NULL_HANDLE;
11010 
11011  vma_delete(allocator, m_pMetadata);
11012  m_pMetadata = VMA_NULL;
11013 }
11014 
11015 bool VmaDeviceMemoryBlock::Validate() const
11016 {
11017  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11018  (m_pMetadata->GetSize() != 0));
11019 
11020  return m_pMetadata->Validate();
11021 }
11022 
11023 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11024 {
11025  void* pData = nullptr;
11026  VkResult res = Map(hAllocator, 1, &pData);
11027  if(res != VK_SUCCESS)
11028  {
11029  return res;
11030  }
11031 
11032  res = m_pMetadata->CheckCorruption(pData);
11033 
11034  Unmap(hAllocator, 1);
11035 
11036  return res;
11037 }
11038 
11039 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11040 {
11041  if(count == 0)
11042  {
11043  return VK_SUCCESS;
11044  }
11045 
11046  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11047  if(m_MapCount != 0)
11048  {
11049  m_MapCount += count;
11050  VMA_ASSERT(m_pMappedData != VMA_NULL);
11051  if(ppData != VMA_NULL)
11052  {
11053  *ppData = m_pMappedData;
11054  }
11055  return VK_SUCCESS;
11056  }
11057  else
11058  {
11059  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11060  hAllocator->m_hDevice,
11061  m_hMemory,
11062  0, // offset
11063  VK_WHOLE_SIZE,
11064  0, // flags
11065  &m_pMappedData);
11066  if(result == VK_SUCCESS)
11067  {
11068  if(ppData != VMA_NULL)
11069  {
11070  *ppData = m_pMappedData;
11071  }
11072  m_MapCount = count;
11073  }
11074  return result;
11075  }
11076 }
11077 
11078 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11079 {
11080  if(count == 0)
11081  {
11082  return;
11083  }
11084 
11085  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11086  if(m_MapCount >= count)
11087  {
11088  m_MapCount -= count;
11089  if(m_MapCount == 0)
11090  {
11091  m_pMappedData = VMA_NULL;
11092  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11093  }
11094  }
11095  else
11096  {
11097  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11098  }
11099 }
11100 
11101 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11102 {
11103  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11104  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11105 
11106  void* pData;
11107  VkResult res = Map(hAllocator, 1, &pData);
11108  if(res != VK_SUCCESS)
11109  {
11110  return res;
11111  }
11112 
11113  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11114  VmaWriteMagicValue(pData, allocOffset + allocSize);
11115 
11116  Unmap(hAllocator, 1);
11117 
11118  return VK_SUCCESS;
11119 }
11120 
11121 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11122 {
11123  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11124  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11125 
11126  void* pData;
11127  VkResult res = Map(hAllocator, 1, &pData);
11128  if(res != VK_SUCCESS)
11129  {
11130  return res;
11131  }
11132 
11133  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11134  {
11135  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11136  }
11137  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11138  {
11139  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11140  }
11141 
11142  Unmap(hAllocator, 1);
11143 
11144  return VK_SUCCESS;
11145 }
11146 
11147 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11148  const VmaAllocator hAllocator,
11149  const VmaAllocation hAllocation,
11150  VkBuffer hBuffer)
11151 {
11152  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11153  hAllocation->GetBlock() == this);
11154  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11155  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11156  return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11157  hAllocator->m_hDevice,
11158  hBuffer,
11159  m_hMemory,
11160  hAllocation->GetOffset());
11161 }
11162 
11163 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11164  const VmaAllocator hAllocator,
11165  const VmaAllocation hAllocation,
11166  VkImage hImage)
11167 {
11168  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11169  hAllocation->GetBlock() == this);
11170  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11171  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11172  return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11173  hAllocator->m_hDevice,
11174  hImage,
11175  m_hMemory,
11176  hAllocation->GetOffset());
11177 }
11178 
11179 static void InitStatInfo(VmaStatInfo& outInfo)
11180 {
11181  memset(&outInfo, 0, sizeof(outInfo));
11182  outInfo.allocationSizeMin = UINT64_MAX;
11183  outInfo.unusedRangeSizeMin = UINT64_MAX;
11184 }
11185 
11186 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
11187 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11188 {
11189  inoutInfo.blockCount += srcInfo.blockCount;
11190  inoutInfo.allocationCount += srcInfo.allocationCount;
11191  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11192  inoutInfo.usedBytes += srcInfo.usedBytes;
11193  inoutInfo.unusedBytes += srcInfo.unusedBytes;
11194  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11195  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11196  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11197  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11198 }
11199 
11200 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11201 {
11202  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11203  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11204  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11205  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11206 }
11207 
11208 VmaPool_T::VmaPool_T(
11209  VmaAllocator hAllocator,
11210  const VmaPoolCreateInfo& createInfo,
11211  VkDeviceSize preferredBlockSize) :
11212  m_BlockVector(
11213  hAllocator,
11214  createInfo.memoryTypeIndex,
11215  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11216  createInfo.minBlockCount,
11217  createInfo.maxBlockCount,
11218  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11219  createInfo.frameInUseCount,
11220  true, // isCustomPool
11221  createInfo.blockSize != 0, // explicitBlockSize
11222  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11223  m_Id(0)
11224 {
11225 }
11226 
11227 VmaPool_T::~VmaPool_T()
11228 {
11229 }
11230 
11231 #if VMA_STATS_STRING_ENABLED
11232 
11233 #endif // #if VMA_STATS_STRING_ENABLED
11234 
11235 VmaBlockVector::VmaBlockVector(
11236  VmaAllocator hAllocator,
11237  uint32_t memoryTypeIndex,
11238  VkDeviceSize preferredBlockSize,
11239  size_t minBlockCount,
11240  size_t maxBlockCount,
11241  VkDeviceSize bufferImageGranularity,
11242  uint32_t frameInUseCount,
11243  bool isCustomPool,
11244  bool explicitBlockSize,
11245  uint32_t algorithm) :
11246  m_hAllocator(hAllocator),
11247  m_MemoryTypeIndex(memoryTypeIndex),
11248  m_PreferredBlockSize(preferredBlockSize),
11249  m_MinBlockCount(minBlockCount),
11250  m_MaxBlockCount(maxBlockCount),
11251  m_BufferImageGranularity(bufferImageGranularity),
11252  m_FrameInUseCount(frameInUseCount),
11253  m_IsCustomPool(isCustomPool),
11254  m_ExplicitBlockSize(explicitBlockSize),
11255  m_Algorithm(algorithm),
11256  m_HasEmptyBlock(false),
11257  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11258  m_NextBlockId(0)
11259 {
11260 }
11261 
11262 VmaBlockVector::~VmaBlockVector()
11263 {
11264  for(size_t i = m_Blocks.size(); i--; )
11265  {
11266  m_Blocks[i]->Destroy(m_hAllocator);
11267  vma_delete(m_hAllocator, m_Blocks[i]);
11268  }
11269 }
11270 
11271 VkResult VmaBlockVector::CreateMinBlocks()
11272 {
11273  for(size_t i = 0; i < m_MinBlockCount; ++i)
11274  {
11275  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11276  if(res != VK_SUCCESS)
11277  {
11278  return res;
11279  }
11280  }
11281  return VK_SUCCESS;
11282 }
11283 
11284 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11285 {
11286  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11287 
11288  const size_t blockCount = m_Blocks.size();
11289 
11290  pStats->size = 0;
11291  pStats->unusedSize = 0;
11292  pStats->allocationCount = 0;
11293  pStats->unusedRangeCount = 0;
11294  pStats->unusedRangeSizeMax = 0;
11295  pStats->blockCount = blockCount;
11296 
11297  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11298  {
11299  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11300  VMA_ASSERT(pBlock);
11301  VMA_HEAVY_ASSERT(pBlock->Validate());
11302  pBlock->m_pMetadata->AddPoolStats(*pStats);
11303  }
11304 }
11305 
11306 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11307 {
11308  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11309  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11310  (VMA_DEBUG_MARGIN > 0) &&
11311  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11312 }
11313 
11314 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11315 
11316 VkResult VmaBlockVector::Allocate(
11317  VmaPool hCurrentPool,
11318  uint32_t currentFrameIndex,
11319  VkDeviceSize size,
11320  VkDeviceSize alignment,
11321  const VmaAllocationCreateInfo& createInfo,
11322  VmaSuballocationType suballocType,
11323  size_t allocationCount,
11324  VmaAllocation* pAllocations)
11325 {
11326  size_t allocIndex;
11327  VkResult res = VK_SUCCESS;
11328 
11329  {
11330  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11331  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11332  {
11333  res = AllocatePage(
11334  hCurrentPool,
11335  currentFrameIndex,
11336  size,
11337  alignment,
11338  createInfo,
11339  suballocType,
11340  pAllocations + allocIndex);
11341  if(res != VK_SUCCESS)
11342  {
11343  break;
11344  }
11345  }
11346  }
11347 
11348  if(res != VK_SUCCESS)
11349  {
11350  // Free all already created allocations.
11351  while(allocIndex--)
11352  {
11353  Free(pAllocations[allocIndex]);
11354  }
11355  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11356  }
11357 
11358  return res;
11359 }
11360 
11361 VkResult VmaBlockVector::AllocatePage(
11362  VmaPool hCurrentPool,
11363  uint32_t currentFrameIndex,
11364  VkDeviceSize size,
11365  VkDeviceSize alignment,
11366  const VmaAllocationCreateInfo& createInfo,
11367  VmaSuballocationType suballocType,
11368  VmaAllocation* pAllocation)
11369 {
11370  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11371  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11372  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11373  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11374  const bool canCreateNewBlock =
11375  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11376  (m_Blocks.size() < m_MaxBlockCount);
11377  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11378 
11379  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11380  // Which in turn is available only when maxBlockCount = 1.
11381  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11382  {
11383  canMakeOtherLost = false;
11384  }
11385 
11386  // Upper address can only be used with linear allocator and within single memory block.
11387  if(isUpperAddress &&
11388  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11389  {
11390  return VK_ERROR_FEATURE_NOT_PRESENT;
11391  }
11392 
11393  // Validate strategy.
11394  switch(strategy)
11395  {
11396  case 0:
11398  break;
11402  break;
11403  default:
11404  return VK_ERROR_FEATURE_NOT_PRESENT;
11405  }
11406 
11407  // Early reject: requested allocation size is larger that maximum block size for this block vector.
11408  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11409  {
11410  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11411  }
11412 
11413  /*
11414  Under certain condition, this whole section can be skipped for optimization, so
11415  we move on directly to trying to allocate with canMakeOtherLost. That's the case
11416  e.g. for custom pools with linear algorithm.
11417  */
11418  if(!canMakeOtherLost || canCreateNewBlock)
11419  {
11420  // 1. Search existing allocations. Try to allocate without making other allocations lost.
11421  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11423 
11424  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11425  {
11426  // Use only last block.
11427  if(!m_Blocks.empty())
11428  {
11429  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11430  VMA_ASSERT(pCurrBlock);
11431  VkResult res = AllocateFromBlock(
11432  pCurrBlock,
11433  hCurrentPool,
11434  currentFrameIndex,
11435  size,
11436  alignment,
11437  allocFlagsCopy,
11438  createInfo.pUserData,
11439  suballocType,
11440  strategy,
11441  pAllocation);
11442  if(res == VK_SUCCESS)
11443  {
11444  VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11445  return VK_SUCCESS;
11446  }
11447  }
11448  }
11449  else
11450  {
11452  {
11453  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11454  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11455  {
11456  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11457  VMA_ASSERT(pCurrBlock);
11458  VkResult res = AllocateFromBlock(
11459  pCurrBlock,
11460  hCurrentPool,
11461  currentFrameIndex,
11462  size,
11463  alignment,
11464  allocFlagsCopy,
11465  createInfo.pUserData,
11466  suballocType,
11467  strategy,
11468  pAllocation);
11469  if(res == VK_SUCCESS)
11470  {
11471  VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11472  return VK_SUCCESS;
11473  }
11474  }
11475  }
11476  else // WORST_FIT, FIRST_FIT
11477  {
11478  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11479  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11480  {
11481  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11482  VMA_ASSERT(pCurrBlock);
11483  VkResult res = AllocateFromBlock(
11484  pCurrBlock,
11485  hCurrentPool,
11486  currentFrameIndex,
11487  size,
11488  alignment,
11489  allocFlagsCopy,
11490  createInfo.pUserData,
11491  suballocType,
11492  strategy,
11493  pAllocation);
11494  if(res == VK_SUCCESS)
11495  {
11496  VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11497  return VK_SUCCESS;
11498  }
11499  }
11500  }
11501  }
11502 
11503  // 2. Try to create new block.
11504  if(canCreateNewBlock)
11505  {
11506  // Calculate optimal size for new block.
11507  VkDeviceSize newBlockSize = m_PreferredBlockSize;
11508  uint32_t newBlockSizeShift = 0;
11509  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11510 
11511  if(!m_ExplicitBlockSize)
11512  {
11513  // Allocate 1/8, 1/4, 1/2 as first blocks.
11514  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11515  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11516  {
11517  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11518  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11519  {
11520  newBlockSize = smallerNewBlockSize;
11521  ++newBlockSizeShift;
11522  }
11523  else
11524  {
11525  break;
11526  }
11527  }
11528  }
11529 
11530  size_t newBlockIndex = 0;
11531  VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11532  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11533  if(!m_ExplicitBlockSize)
11534  {
11535  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11536  {
11537  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11538  if(smallerNewBlockSize >= size)
11539  {
11540  newBlockSize = smallerNewBlockSize;
11541  ++newBlockSizeShift;
11542  res = CreateBlock(newBlockSize, &newBlockIndex);
11543  }
11544  else
11545  {
11546  break;
11547  }
11548  }
11549  }
11550 
11551  if(res == VK_SUCCESS)
11552  {
11553  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11554  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11555 
11556  res = AllocateFromBlock(
11557  pBlock,
11558  hCurrentPool,
11559  currentFrameIndex,
11560  size,
11561  alignment,
11562  allocFlagsCopy,
11563  createInfo.pUserData,
11564  suballocType,
11565  strategy,
11566  pAllocation);
11567  if(res == VK_SUCCESS)
11568  {
11569  VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11570  return VK_SUCCESS;
11571  }
11572  else
11573  {
11574  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11575  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11576  }
11577  }
11578  }
11579  }
11580 
11581  // 3. Try to allocate from existing blocks with making other allocations lost.
11582  if(canMakeOtherLost)
11583  {
11584  uint32_t tryIndex = 0;
11585  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11586  {
11587  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11588  VmaAllocationRequest bestRequest = {};
11589  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11590 
11591  // 1. Search existing allocations.
11593  {
11594  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11595  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11596  {
11597  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11598  VMA_ASSERT(pCurrBlock);
11599  VmaAllocationRequest currRequest = {};
11600  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11601  currentFrameIndex,
11602  m_FrameInUseCount,
11603  m_BufferImageGranularity,
11604  size,
11605  alignment,
11606  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11607  suballocType,
11608  canMakeOtherLost,
11609  strategy,
11610  &currRequest))
11611  {
11612  const VkDeviceSize currRequestCost = currRequest.CalcCost();
11613  if(pBestRequestBlock == VMA_NULL ||
11614  currRequestCost < bestRequestCost)
11615  {
11616  pBestRequestBlock = pCurrBlock;
11617  bestRequest = currRequest;
11618  bestRequestCost = currRequestCost;
11619 
11620  if(bestRequestCost == 0)
11621  {
11622  break;
11623  }
11624  }
11625  }
11626  }
11627  }
11628  else // WORST_FIT, FIRST_FIT
11629  {
11630  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11631  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11632  {
11633  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11634  VMA_ASSERT(pCurrBlock);
11635  VmaAllocationRequest currRequest = {};
11636  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11637  currentFrameIndex,
11638  m_FrameInUseCount,
11639  m_BufferImageGranularity,
11640  size,
11641  alignment,
11642  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11643  suballocType,
11644  canMakeOtherLost,
11645  strategy,
11646  &currRequest))
11647  {
11648  const VkDeviceSize currRequestCost = currRequest.CalcCost();
11649  if(pBestRequestBlock == VMA_NULL ||
11650  currRequestCost < bestRequestCost ||
11652  {
11653  pBestRequestBlock = pCurrBlock;
11654  bestRequest = currRequest;
11655  bestRequestCost = currRequestCost;
11656 
11657  if(bestRequestCost == 0 ||
11659  {
11660  break;
11661  }
11662  }
11663  }
11664  }
11665  }
11666 
11667  if(pBestRequestBlock != VMA_NULL)
11668  {
11669  if(mapped)
11670  {
11671  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11672  if(res != VK_SUCCESS)
11673  {
11674  return res;
11675  }
11676  }
11677 
11678  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11679  currentFrameIndex,
11680  m_FrameInUseCount,
11681  &bestRequest))
11682  {
11683  // We no longer have an empty Allocation.
11684  if(pBestRequestBlock->m_pMetadata->IsEmpty())
11685  {
11686  m_HasEmptyBlock = false;
11687  }
11688  // Allocate from this pBlock.
11689  *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11690  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11691  (*pAllocation)->InitBlockAllocation(
11692  hCurrentPool,
11693  pBestRequestBlock,
11694  bestRequest.offset,
11695  alignment,
11696  size,
11697  suballocType,
11698  mapped,
11699  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11700  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11701  VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11702  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11703  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11704  {
11705  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11706  }
11707  if(IsCorruptionDetectionEnabled())
11708  {
11709  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11710  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11711  }
11712  return VK_SUCCESS;
11713  }
11714  // else: Some allocations must have been touched while we are here. Next try.
11715  }
11716  else
11717  {
11718  // Could not find place in any of the blocks - break outer loop.
11719  break;
11720  }
11721  }
11722  /* Maximum number of tries exceeded - a very unlike event when many other
11723  threads are simultaneously touching allocations making it impossible to make
11724  lost at the same time as we try to allocate. */
11725  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11726  {
11727  return VK_ERROR_TOO_MANY_OBJECTS;
11728  }
11729  }
11730 
11731  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11732 }
11733 
11734 void VmaBlockVector::Free(
11735  VmaAllocation hAllocation)
11736 {
11737  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11738 
11739  // Scope for lock.
11740  {
11741  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11742 
11743  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11744 
11745  if(IsCorruptionDetectionEnabled())
11746  {
11747  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11748  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11749  }
11750 
11751  if(hAllocation->IsPersistentMap())
11752  {
11753  pBlock->Unmap(m_hAllocator, 1);
11754  }
11755 
11756  pBlock->m_pMetadata->Free(hAllocation);
11757  VMA_HEAVY_ASSERT(pBlock->Validate());
11758 
11759  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11760 
11761  // pBlock became empty after this deallocation.
11762  if(pBlock->m_pMetadata->IsEmpty())
11763  {
11764  // Already has empty Allocation. We don't want to have two, so delete this one.
11765  if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11766  {
11767  pBlockToDelete = pBlock;
11768  Remove(pBlock);
11769  }
11770  // We now have first empty block.
11771  else
11772  {
11773  m_HasEmptyBlock = true;
11774  }
11775  }
11776  // pBlock didn't become empty, but we have another empty block - find and free that one.
11777  // (This is optional, heuristics.)
11778  else if(m_HasEmptyBlock)
11779  {
11780  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11781  if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11782  {
11783  pBlockToDelete = pLastBlock;
11784  m_Blocks.pop_back();
11785  m_HasEmptyBlock = false;
11786  }
11787  }
11788 
11789  IncrementallySortBlocks();
11790  }
11791 
11792  // Destruction of a free Allocation. Deferred until this point, outside of mutex
11793  // lock, for performance reason.
11794  if(pBlockToDelete != VMA_NULL)
11795  {
11796  VMA_DEBUG_LOG(" Deleted empty allocation");
11797  pBlockToDelete->Destroy(m_hAllocator);
11798  vma_delete(m_hAllocator, pBlockToDelete);
11799  }
11800 }
11801 
11802 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11803 {
11804  VkDeviceSize result = 0;
11805  for(size_t i = m_Blocks.size(); i--; )
11806  {
11807  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11808  if(result >= m_PreferredBlockSize)
11809  {
11810  break;
11811  }
11812  }
11813  return result;
11814 }
11815 
11816 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11817 {
11818  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11819  {
11820  if(m_Blocks[blockIndex] == pBlock)
11821  {
11822  VmaVectorRemove(m_Blocks, blockIndex);
11823  return;
11824  }
11825  }
11826  VMA_ASSERT(0);
11827 }
11828 
11829 void VmaBlockVector::IncrementallySortBlocks()
11830 {
11831  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11832  {
11833  // Bubble sort only until first swap.
11834  for(size_t i = 1; i < m_Blocks.size(); ++i)
11835  {
11836  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11837  {
11838  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11839  return;
11840  }
11841  }
11842  }
11843 }
11844 
11845 VkResult VmaBlockVector::AllocateFromBlock(
11846  VmaDeviceMemoryBlock* pBlock,
11847  VmaPool hCurrentPool,
11848  uint32_t currentFrameIndex,
11849  VkDeviceSize size,
11850  VkDeviceSize alignment,
11851  VmaAllocationCreateFlags allocFlags,
11852  void* pUserData,
11853  VmaSuballocationType suballocType,
11854  uint32_t strategy,
11855  VmaAllocation* pAllocation)
11856 {
11857  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11858  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11859  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11860  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11861 
11862  VmaAllocationRequest currRequest = {};
11863  if(pBlock->m_pMetadata->CreateAllocationRequest(
11864  currentFrameIndex,
11865  m_FrameInUseCount,
11866  m_BufferImageGranularity,
11867  size,
11868  alignment,
11869  isUpperAddress,
11870  suballocType,
11871  false, // canMakeOtherLost
11872  strategy,
11873  &currRequest))
11874  {
11875  // Allocate from pCurrBlock.
11876  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11877 
11878  if(mapped)
11879  {
11880  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11881  if(res != VK_SUCCESS)
11882  {
11883  return res;
11884  }
11885  }
11886 
11887  // We no longer have an empty Allocation.
11888  if(pBlock->m_pMetadata->IsEmpty())
11889  {
11890  m_HasEmptyBlock = false;
11891  }
11892 
11893  *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11894  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11895  (*pAllocation)->InitBlockAllocation(
11896  hCurrentPool,
11897  pBlock,
11898  currRequest.offset,
11899  alignment,
11900  size,
11901  suballocType,
11902  mapped,
11903  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11904  VMA_HEAVY_ASSERT(pBlock->Validate());
11905  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11906  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11907  {
11908  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11909  }
11910  if(IsCorruptionDetectionEnabled())
11911  {
11912  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11913  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11914  }
11915  return VK_SUCCESS;
11916  }
11917  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11918 }
11919 
11920 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11921 {
11922  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11923  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11924  allocInfo.allocationSize = blockSize;
11925  VkDeviceMemory mem = VK_NULL_HANDLE;
11926  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11927  if(res < 0)
11928  {
11929  return res;
11930  }
11931 
11932  // New VkDeviceMemory successfully created.
11933 
11934  // Create new Allocation for it.
11935  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11936  pBlock->Init(
11937  m_hAllocator,
11938  m_MemoryTypeIndex,
11939  mem,
11940  allocInfo.allocationSize,
11941  m_NextBlockId++,
11942  m_Algorithm);
11943 
11944  m_Blocks.push_back(pBlock);
11945  if(pNewBlockIndex != VMA_NULL)
11946  {
11947  *pNewBlockIndex = m_Blocks.size() - 1;
11948  }
11949 
11950  return VK_SUCCESS;
11951 }
11952 
11953 void VmaBlockVector::ApplyDefragmentationMovesCpu(
11954  class VmaBlockVectorDefragmentationContext* pDefragCtx,
11955  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11956 {
11957  const size_t blockCount = m_Blocks.size();
11958  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11959 
11960  enum BLOCK_FLAG
11961  {
11962  BLOCK_FLAG_USED = 0x00000001,
11963  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11964  };
11965 
11966  struct BlockInfo
11967  {
11968  uint32_t flags;
11969  void* pMappedData;
11970  };
11971  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11972  blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11973  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11974 
11975  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11976  const size_t moveCount = moves.size();
11977  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11978  {
11979  const VmaDefragmentationMove& move = moves[moveIndex];
11980  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11981  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11982  }
11983 
11984  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
11985 
11986  // Go over all blocks. Get mapped pointer or map if necessary.
11987  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
11988  {
11989  BlockInfo& currBlockInfo = blockInfo[blockIndex];
11990  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
11991  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
11992  {
11993  currBlockInfo.pMappedData = pBlock->GetMappedData();
11994  // It is not originally mapped - map it.
11995  if(currBlockInfo.pMappedData == VMA_NULL)
11996  {
11997  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
11998  if(pDefragCtx->res == VK_SUCCESS)
11999  {
12000  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12001  }
12002  }
12003  }
12004  }
12005 
12006  // Go over all moves. Do actual data transfer.
12007  if(pDefragCtx->res == VK_SUCCESS)
12008  {
12009  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12010  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12011 
12012  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12013  {
12014  const VmaDefragmentationMove& move = moves[moveIndex];
12015 
12016  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12017  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12018 
12019  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12020 
12021  // Invalidate source.
12022  if(isNonCoherent)
12023  {
12024  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12025  memRange.memory = pSrcBlock->GetDeviceMemory();
12026  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12027  memRange.size = VMA_MIN(
12028  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12029  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12030  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12031  }
12032 
12033  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12034  memmove(
12035  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12036  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12037  static_cast<size_t>(move.size));
12038 
12039  if(IsCorruptionDetectionEnabled())
12040  {
12041  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12042  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12043  }
12044 
12045  // Flush destination.
12046  if(isNonCoherent)
12047  {
12048  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12049  memRange.memory = pDstBlock->GetDeviceMemory();
12050  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12051  memRange.size = VMA_MIN(
12052  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12053  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12054  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12055  }
12056  }
12057  }
12058 
12059  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12060  // Regardless of pCtx->res == VK_SUCCESS.
12061  for(size_t blockIndex = blockCount; blockIndex--; )
12062  {
12063  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12064  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12065  {
12066  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12067  pBlock->Unmap(m_hAllocator, 1);
12068  }
12069  }
12070 }
12071 
12072 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12073  class VmaBlockVectorDefragmentationContext* pDefragCtx,
12074  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12075  VkCommandBuffer commandBuffer)
12076 {
12077  const size_t blockCount = m_Blocks.size();
12078 
12079  pDefragCtx->blockContexts.resize(blockCount);
12080  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12081 
12082  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12083  const size_t moveCount = moves.size();
12084  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12085  {
12086  const VmaDefragmentationMove& move = moves[moveIndex];
12087  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12088  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12089  }
12090 
12091  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12092 
12093  // Go over all blocks. Create and bind buffer for whole block if necessary.
12094  {
12095  VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12096  bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12097  VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12098 
12099  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12100  {
12101  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12102  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12103  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12104  {
12105  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12106  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12107  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12108  if(pDefragCtx->res == VK_SUCCESS)
12109  {
12110  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12111  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12112  }
12113  }
12114  }
12115  }
12116 
12117  // Go over all moves. Post data transfer commands to command buffer.
12118  if(pDefragCtx->res == VK_SUCCESS)
12119  {
12120  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12121  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12122 
12123  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12124  {
12125  const VmaDefragmentationMove& move = moves[moveIndex];
12126 
12127  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12128  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12129 
12130  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12131 
12132  VkBufferCopy region = {
12133  move.srcOffset,
12134  move.dstOffset,
12135  move.size };
12136  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12137  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12138  }
12139  }
12140 
12141  // Save buffers to defrag context for later destruction.
12142  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12143  {
12144  pDefragCtx->res = VK_NOT_READY;
12145  }
12146 }
12147 
12148 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12149 {
12150  m_HasEmptyBlock = false;
12151  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12152  {
12153  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12154  if(pBlock->m_pMetadata->IsEmpty())
12155  {
12156  if(m_Blocks.size() > m_MinBlockCount)
12157  {
12158  if(pDefragmentationStats != VMA_NULL)
12159  {
12160  ++pDefragmentationStats->deviceMemoryBlocksFreed;
12161  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12162  }
12163 
12164  VmaVectorRemove(m_Blocks, blockIndex);
12165  pBlock->Destroy(m_hAllocator);
12166  vma_delete(m_hAllocator, pBlock);
12167  }
12168  else
12169  {
12170  m_HasEmptyBlock = true;
12171  }
12172  }
12173  }
12174 }
12175 
12176 #if VMA_STATS_STRING_ENABLED
12177 
12178 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12179 {
12180  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12181 
12182  json.BeginObject();
12183 
12184  if(m_IsCustomPool)
12185  {
12186  json.WriteString("MemoryTypeIndex");
12187  json.WriteNumber(m_MemoryTypeIndex);
12188 
12189  json.WriteString("BlockSize");
12190  json.WriteNumber(m_PreferredBlockSize);
12191 
12192  json.WriteString("BlockCount");
12193  json.BeginObject(true);
12194  if(m_MinBlockCount > 0)
12195  {
12196  json.WriteString("Min");
12197  json.WriteNumber((uint64_t)m_MinBlockCount);
12198  }
12199  if(m_MaxBlockCount < SIZE_MAX)
12200  {
12201  json.WriteString("Max");
12202  json.WriteNumber((uint64_t)m_MaxBlockCount);
12203  }
12204  json.WriteString("Cur");
12205  json.WriteNumber((uint64_t)m_Blocks.size());
12206  json.EndObject();
12207 
12208  if(m_FrameInUseCount > 0)
12209  {
12210  json.WriteString("FrameInUseCount");
12211  json.WriteNumber(m_FrameInUseCount);
12212  }
12213 
12214  if(m_Algorithm != 0)
12215  {
12216  json.WriteString("Algorithm");
12217  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12218  }
12219  }
12220  else
12221  {
12222  json.WriteString("PreferredBlockSize");
12223  json.WriteNumber(m_PreferredBlockSize);
12224  }
12225 
12226  json.WriteString("Blocks");
12227  json.BeginObject();
12228  for(size_t i = 0; i < m_Blocks.size(); ++i)
12229  {
12230  json.BeginString();
12231  json.ContinueString(m_Blocks[i]->GetId());
12232  json.EndString();
12233 
12234  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12235  }
12236  json.EndObject();
12237 
12238  json.EndObject();
12239 }
12240 
12241 #endif // #if VMA_STATS_STRING_ENABLED
12242 
12243 void VmaBlockVector::Defragment(
12244  class VmaBlockVectorDefragmentationContext* pCtx,
12245  VmaDefragmentationStats* pStats,
12246  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12247  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12248  VkCommandBuffer commandBuffer)
12249 {
12250  pCtx->res = VK_SUCCESS;
12251 
12252  const VkMemoryPropertyFlags memPropFlags =
12253  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12254  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12255  const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12256 
12257  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12258  isHostVisible;
12259  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12260  (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12261 
12262  // There are options to defragment this memory type.
12263  if(canDefragmentOnCpu || canDefragmentOnGpu)
12264  {
12265  bool defragmentOnGpu;
12266  // There is only one option to defragment this memory type.
12267  if(canDefragmentOnGpu != canDefragmentOnCpu)
12268  {
12269  defragmentOnGpu = canDefragmentOnGpu;
12270  }
12271  // Both options are available: Heuristics to choose the best one.
12272  else
12273  {
12274  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12275  m_hAllocator->IsIntegratedGpu();
12276  }
12277 
12278  bool overlappingMoveSupported = !defragmentOnGpu;
12279 
12280  if(m_hAllocator->m_UseMutex)
12281  {
12282  m_Mutex.LockWrite();
12283  pCtx->mutexLocked = true;
12284  }
12285 
12286  pCtx->Begin(overlappingMoveSupported);
12287 
12288  // Defragment.
12289 
12290  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12291  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12292  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12293  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12294  pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12295 
12296  // Accumulate statistics.
12297  if(pStats != VMA_NULL)
12298  {
12299  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12300  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12301  pStats->bytesMoved += bytesMoved;
12302  pStats->allocationsMoved += allocationsMoved;
12303  VMA_ASSERT(bytesMoved <= maxBytesToMove);
12304  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12305  if(defragmentOnGpu)
12306  {
12307  maxGpuBytesToMove -= bytesMoved;
12308  maxGpuAllocationsToMove -= allocationsMoved;
12309  }
12310  else
12311  {
12312  maxCpuBytesToMove -= bytesMoved;
12313  maxCpuAllocationsToMove -= allocationsMoved;
12314  }
12315  }
12316 
12317  if(pCtx->res >= VK_SUCCESS)
12318  {
12319  if(defragmentOnGpu)
12320  {
12321  ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12322  }
12323  else
12324  {
12325  ApplyDefragmentationMovesCpu(pCtx, moves);
12326  }
12327  }
12328  }
12329 }
12330 
12331 void VmaBlockVector::DefragmentationEnd(
12332  class VmaBlockVectorDefragmentationContext* pCtx,
12333  VmaDefragmentationStats* pStats)
12334 {
12335  // Destroy buffers.
12336  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12337  {
12338  VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12339  if(blockCtx.hBuffer)
12340  {
12341  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12342  m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12343  }
12344  }
12345 
12346  if(pCtx->res >= VK_SUCCESS)
12347  {
12348  FreeEmptyBlocks(pStats);
12349  }
12350 
12351  if(pCtx->mutexLocked)
12352  {
12353  VMA_ASSERT(m_hAllocator->m_UseMutex);
12354  m_Mutex.UnlockWrite();
12355  }
12356 }
12357 
12358 size_t VmaBlockVector::CalcAllocationCount() const
12359 {
12360  size_t result = 0;
12361  for(size_t i = 0; i < m_Blocks.size(); ++i)
12362  {
12363  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12364  }
12365  return result;
12366 }
12367 
12368 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12369 {
12370  if(m_BufferImageGranularity == 1)
12371  {
12372  return false;
12373  }
12374  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12375  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12376  {
12377  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12378  VMA_ASSERT(m_Algorithm == 0);
12379  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12380  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12381  {
12382  return true;
12383  }
12384  }
12385  return false;
12386 }
12387 
12388 void VmaBlockVector::MakePoolAllocationsLost(
12389  uint32_t currentFrameIndex,
12390  size_t* pLostAllocationCount)
12391 {
12392  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12393  size_t lostAllocationCount = 0;
12394  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12395  {
12396  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12397  VMA_ASSERT(pBlock);
12398  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12399  }
12400  if(pLostAllocationCount != VMA_NULL)
12401  {
12402  *pLostAllocationCount = lostAllocationCount;
12403  }
12404 }
12405 
12406 VkResult VmaBlockVector::CheckCorruption()
12407 {
12408  if(!IsCorruptionDetectionEnabled())
12409  {
12410  return VK_ERROR_FEATURE_NOT_PRESENT;
12411  }
12412 
12413  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12414  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12415  {
12416  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12417  VMA_ASSERT(pBlock);
12418  VkResult res = pBlock->CheckCorruption(m_hAllocator);
12419  if(res != VK_SUCCESS)
12420  {
12421  return res;
12422  }
12423  }
12424  return VK_SUCCESS;
12425 }
12426 
12427 void VmaBlockVector::AddStats(VmaStats* pStats)
12428 {
12429  const uint32_t memTypeIndex = m_MemoryTypeIndex;
12430  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12431 
12432  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12433 
12434  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12435  {
12436  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12437  VMA_ASSERT(pBlock);
12438  VMA_HEAVY_ASSERT(pBlock->Validate());
12439  VmaStatInfo allocationStatInfo;
12440  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12441  VmaAddStatInfo(pStats->total, allocationStatInfo);
12442  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12443  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12444  }
12445 }
12446 
12448 // VmaDefragmentationAlgorithm_Generic members definition
12449 
12450 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12451  VmaAllocator hAllocator,
12452  VmaBlockVector* pBlockVector,
12453  uint32_t currentFrameIndex,
12454  bool overlappingMoveSupported) :
12455  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12456  m_AllAllocations(false),
12457  m_AllocationCount(0),
12458  m_BytesMoved(0),
12459  m_AllocationsMoved(0),
12460  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12461 {
12462  // Create block info for each block.
12463  const size_t blockCount = m_pBlockVector->m_Blocks.size();
12464  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12465  {
12466  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12467  pBlockInfo->m_OriginalBlockIndex = blockIndex;
12468  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12469  m_Blocks.push_back(pBlockInfo);
12470  }
12471 
12472  // Sort them by m_pBlock pointer value.
12473  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12474 }
12475 
12476 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12477 {
12478  for(size_t i = m_Blocks.size(); i--; )
12479  {
12480  vma_delete(m_hAllocator, m_Blocks[i]);
12481  }
12482 }
12483 
12484 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12485 {
12486  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12487  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12488  {
12489  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12490  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12491  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12492  {
12493  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12494  (*it)->m_Allocations.push_back(allocInfo);
12495  }
12496  else
12497  {
12498  VMA_ASSERT(0);
12499  }
12500 
12501  ++m_AllocationCount;
12502  }
12503 }
12504 
12505 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12506  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12507  VkDeviceSize maxBytesToMove,
12508  uint32_t maxAllocationsToMove)
12509 {
12510  if(m_Blocks.empty())
12511  {
12512  return VK_SUCCESS;
12513  }
12514 
12515  // This is a choice based on research.
12516  // Option 1:
12517  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12518  // Option 2:
12519  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12520  // Option 3:
12521  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12522 
12523  size_t srcBlockMinIndex = 0;
12524  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12525  /*
12526  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12527  {
12528  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12529  if(blocksWithNonMovableCount > 0)
12530  {
12531  srcBlockMinIndex = blocksWithNonMovableCount - 1;
12532  }
12533  }
12534  */
12535 
12536  size_t srcBlockIndex = m_Blocks.size() - 1;
12537  size_t srcAllocIndex = SIZE_MAX;
12538  for(;;)
12539  {
12540  // 1. Find next allocation to move.
12541  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12542  // 1.2. Then start from last to first m_Allocations.
12543  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12544  {
12545  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12546  {
12547  // Finished: no more allocations to process.
12548  if(srcBlockIndex == srcBlockMinIndex)
12549  {
12550  return VK_SUCCESS;
12551  }
12552  else
12553  {
12554  --srcBlockIndex;
12555  srcAllocIndex = SIZE_MAX;
12556  }
12557  }
12558  else
12559  {
12560  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12561  }
12562  }
12563 
12564  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12565  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12566 
12567  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12568  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12569  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12570  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12571 
12572  // 2. Try to find new place for this allocation in preceding or current block.
12573  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12574  {
12575  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12576  VmaAllocationRequest dstAllocRequest;
12577  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12578  m_CurrentFrameIndex,
12579  m_pBlockVector->GetFrameInUseCount(),
12580  m_pBlockVector->GetBufferImageGranularity(),
12581  size,
12582  alignment,
12583  false, // upperAddress
12584  suballocType,
12585  false, // canMakeOtherLost
12586  strategy,
12587  &dstAllocRequest) &&
12588  MoveMakesSense(
12589  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12590  {
12591  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12592 
12593  // Reached limit on number of allocations or bytes to move.
12594  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12595  (m_BytesMoved + size > maxBytesToMove))
12596  {
12597  return VK_SUCCESS;
12598  }
12599 
12600  VmaDefragmentationMove move;
12601  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12602  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12603  move.srcOffset = srcOffset;
12604  move.dstOffset = dstAllocRequest.offset;
12605  move.size = size;
12606  moves.push_back(move);
12607 
12608  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12609  dstAllocRequest,
12610  suballocType,
12611  size,
12612  false, // upperAddress
12613  allocInfo.m_hAllocation);
12614  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12615 
12616  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12617 
12618  if(allocInfo.m_pChanged != VMA_NULL)
12619  {
12620  *allocInfo.m_pChanged = VK_TRUE;
12621  }
12622 
12623  ++m_AllocationsMoved;
12624  m_BytesMoved += size;
12625 
12626  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12627 
12628  break;
12629  }
12630  }
12631 
12632  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12633 
12634  if(srcAllocIndex > 0)
12635  {
12636  --srcAllocIndex;
12637  }
12638  else
12639  {
12640  if(srcBlockIndex > 0)
12641  {
12642  --srcBlockIndex;
12643  srcAllocIndex = SIZE_MAX;
12644  }
12645  else
12646  {
12647  return VK_SUCCESS;
12648  }
12649  }
12650  }
12651 }
12652 
12653 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12654 {
12655  size_t result = 0;
12656  for(size_t i = 0; i < m_Blocks.size(); ++i)
12657  {
12658  if(m_Blocks[i]->m_HasNonMovableAllocations)
12659  {
12660  ++result;
12661  }
12662  }
12663  return result;
12664 }
12665 
12666 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12667  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12668  VkDeviceSize maxBytesToMove,
12669  uint32_t maxAllocationsToMove)
12670 {
12671  if(!m_AllAllocations && m_AllocationCount == 0)
12672  {
12673  return VK_SUCCESS;
12674  }
12675 
12676  const size_t blockCount = m_Blocks.size();
12677  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12678  {
12679  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12680 
12681  if(m_AllAllocations)
12682  {
12683  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12684  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12685  it != pMetadata->m_Suballocations.end();
12686  ++it)
12687  {
12688  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12689  {
12690  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12691  pBlockInfo->m_Allocations.push_back(allocInfo);
12692  }
12693  }
12694  }
12695 
12696  pBlockInfo->CalcHasNonMovableAllocations();
12697 
12698  // This is a choice based on research.
12699  // Option 1:
12700  pBlockInfo->SortAllocationsByOffsetDescending();
12701  // Option 2:
12702  //pBlockInfo->SortAllocationsBySizeDescending();
12703  }
12704 
12705  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12706  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12707 
12708  // This is a choice based on research.
12709  const uint32_t roundCount = 2;
12710 
12711  // Execute defragmentation rounds (the main part).
12712  VkResult result = VK_SUCCESS;
12713  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12714  {
12715  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12716  }
12717 
12718  return result;
12719 }
12720 
12721 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12722  size_t dstBlockIndex, VkDeviceSize dstOffset,
12723  size_t srcBlockIndex, VkDeviceSize srcOffset)
12724 {
12725  if(dstBlockIndex < srcBlockIndex)
12726  {
12727  return true;
12728  }
12729  if(dstBlockIndex > srcBlockIndex)
12730  {
12731  return false;
12732  }
12733  if(dstOffset < srcOffset)
12734  {
12735  return true;
12736  }
12737  return false;
12738 }
12739 
12741 // VmaDefragmentationAlgorithm_Fast
12742 
12743 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12744  VmaAllocator hAllocator,
12745  VmaBlockVector* pBlockVector,
12746  uint32_t currentFrameIndex,
12747  bool overlappingMoveSupported) :
12748  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12749  m_OverlappingMoveSupported(overlappingMoveSupported),
12750  m_AllocationCount(0),
12751  m_AllAllocations(false),
12752  m_BytesMoved(0),
12753  m_AllocationsMoved(0),
12754  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12755 {
12756  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12757 
12758 }
12759 
12760 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12761 {
12762 }
12763 
12764 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12765  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12766  VkDeviceSize maxBytesToMove,
12767  uint32_t maxAllocationsToMove)
12768 {
12769  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12770 
12771  const size_t blockCount = m_pBlockVector->GetBlockCount();
12772  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12773  {
12774  return VK_SUCCESS;
12775  }
12776 
12777  PreprocessMetadata();
12778 
12779  // Sort blocks in order from most destination.
12780 
12781  m_BlockInfos.resize(blockCount);
12782  for(size_t i = 0; i < blockCount; ++i)
12783  {
12784  m_BlockInfos[i].origBlockIndex = i;
12785  }
12786 
12787  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12788  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12789  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12790  });
12791 
12792  // THE MAIN ALGORITHM
12793 
12794  FreeSpaceDatabase freeSpaceDb;
12795 
12796  size_t dstBlockInfoIndex = 0;
12797  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12798  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12799  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12800  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12801  VkDeviceSize dstOffset = 0;
12802 
12803  bool end = false;
12804  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12805  {
12806  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12807  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12808  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12809  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12810  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12811  {
12812  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12813  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12814  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12815  if(m_AllocationsMoved == maxAllocationsToMove ||
12816  m_BytesMoved + srcAllocSize > maxBytesToMove)
12817  {
12818  end = true;
12819  break;
12820  }
12821  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12822 
12823  // Try to place it in one of free spaces from the database.
12824  size_t freeSpaceInfoIndex;
12825  VkDeviceSize dstAllocOffset;
12826  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12827  freeSpaceInfoIndex, dstAllocOffset))
12828  {
12829  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12830  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12831  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12832  VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12833 
12834  // Same block
12835  if(freeSpaceInfoIndex == srcBlockInfoIndex)
12836  {
12837  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12838 
12839  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12840 
12841  VmaSuballocation suballoc = *srcSuballocIt;
12842  suballoc.offset = dstAllocOffset;
12843  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12844  m_BytesMoved += srcAllocSize;
12845  ++m_AllocationsMoved;
12846 
12847  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12848  ++nextSuballocIt;
12849  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12850  srcSuballocIt = nextSuballocIt;
12851 
12852  InsertSuballoc(pFreeSpaceMetadata, suballoc);
12853 
12854  VmaDefragmentationMove move = {
12855  srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12856  srcAllocOffset, dstAllocOffset,
12857  srcAllocSize };
12858  moves.push_back(move);
12859  }
12860  // Different block
12861  else
12862  {
12863  // MOVE OPTION 2: Move the allocation to a different block.
12864 
12865  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12866 
12867  VmaSuballocation suballoc = *srcSuballocIt;
12868  suballoc.offset = dstAllocOffset;
12869  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12870  m_BytesMoved += srcAllocSize;
12871  ++m_AllocationsMoved;
12872 
12873  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12874  ++nextSuballocIt;
12875  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12876  srcSuballocIt = nextSuballocIt;
12877 
12878  InsertSuballoc(pFreeSpaceMetadata, suballoc);
12879 
12880  VmaDefragmentationMove move = {
12881  srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12882  srcAllocOffset, dstAllocOffset,
12883  srcAllocSize };
12884  moves.push_back(move);
12885  }
12886  }
12887  else
12888  {
12889  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12890 
12891  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12892  while(dstBlockInfoIndex < srcBlockInfoIndex &&
12893  dstAllocOffset + srcAllocSize > dstBlockSize)
12894  {
12895  // But before that, register remaining free space at the end of dst block.
12896  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12897 
12898  ++dstBlockInfoIndex;
12899  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12900  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12901  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12902  dstBlockSize = pDstMetadata->GetSize();
12903  dstOffset = 0;
12904  dstAllocOffset = 0;
12905  }
12906 
12907  // Same block
12908  if(dstBlockInfoIndex == srcBlockInfoIndex)
12909  {
12910  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12911 
12912  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12913 
12914  bool skipOver = overlap;
12915  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12916  {
12917  // If destination and source place overlap, skip if it would move it
12918  // by only < 1/64 of its size.
12919  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12920  }
12921 
12922  if(skipOver)
12923  {
12924  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12925 
12926  dstOffset = srcAllocOffset + srcAllocSize;
12927  ++srcSuballocIt;
12928  }
12929  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12930  else
12931  {
12932  srcSuballocIt->offset = dstAllocOffset;
12933  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12934  dstOffset = dstAllocOffset + srcAllocSize;
12935  m_BytesMoved += srcAllocSize;
12936  ++m_AllocationsMoved;
12937  ++srcSuballocIt;
12938  VmaDefragmentationMove move = {
12939  srcOrigBlockIndex, dstOrigBlockIndex,
12940  srcAllocOffset, dstAllocOffset,
12941  srcAllocSize };
12942  moves.push_back(move);
12943  }
12944  }
12945  // Different block
12946  else
12947  {
12948  // MOVE OPTION 2: Move the allocation to a different block.
12949 
12950  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12951  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12952 
12953  VmaSuballocation suballoc = *srcSuballocIt;
12954  suballoc.offset = dstAllocOffset;
12955  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12956  dstOffset = dstAllocOffset + srcAllocSize;
12957  m_BytesMoved += srcAllocSize;
12958  ++m_AllocationsMoved;
12959 
12960  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12961  ++nextSuballocIt;
12962  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12963  srcSuballocIt = nextSuballocIt;
12964 
12965  pDstMetadata->m_Suballocations.push_back(suballoc);
12966 
12967  VmaDefragmentationMove move = {
12968  srcOrigBlockIndex, dstOrigBlockIndex,
12969  srcAllocOffset, dstAllocOffset,
12970  srcAllocSize };
12971  moves.push_back(move);
12972  }
12973  }
12974  }
12975  }
12976 
12977  m_BlockInfos.clear();
12978 
12979  PostprocessMetadata();
12980 
12981  return VK_SUCCESS;
12982 }
12983 
12984 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
12985 {
12986  const size_t blockCount = m_pBlockVector->GetBlockCount();
12987  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12988  {
12989  VmaBlockMetadata_Generic* const pMetadata =
12990  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
12991  pMetadata->m_FreeCount = 0;
12992  pMetadata->m_SumFreeSize = pMetadata->GetSize();
12993  pMetadata->m_FreeSuballocationsBySize.clear();
12994  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
12995  it != pMetadata->m_Suballocations.end(); )
12996  {
12997  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
12998  {
12999  VmaSuballocationList::iterator nextIt = it;
13000  ++nextIt;
13001  pMetadata->m_Suballocations.erase(it);
13002  it = nextIt;
13003  }
13004  else
13005  {
13006  ++it;
13007  }
13008  }
13009  }
13010 }
13011 
13012 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13013 {
13014  const size_t blockCount = m_pBlockVector->GetBlockCount();
13015  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13016  {
13017  VmaBlockMetadata_Generic* const pMetadata =
13018  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13019  const VkDeviceSize blockSize = pMetadata->GetSize();
13020 
13021  // No allocations in this block - entire area is free.
13022  if(pMetadata->m_Suballocations.empty())
13023  {
13024  pMetadata->m_FreeCount = 1;
13025  //pMetadata->m_SumFreeSize is already set to blockSize.
13026  VmaSuballocation suballoc = {
13027  0, // offset
13028  blockSize, // size
13029  VMA_NULL, // hAllocation
13030  VMA_SUBALLOCATION_TYPE_FREE };
13031  pMetadata->m_Suballocations.push_back(suballoc);
13032  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13033  }
13034  // There are some allocations in this block.
13035  else
13036  {
13037  VkDeviceSize offset = 0;
13038  VmaSuballocationList::iterator it;
13039  for(it = pMetadata->m_Suballocations.begin();
13040  it != pMetadata->m_Suballocations.end();
13041  ++it)
13042  {
13043  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13044  VMA_ASSERT(it->offset >= offset);
13045 
13046  // Need to insert preceding free space.
13047  if(it->offset > offset)
13048  {
13049  ++pMetadata->m_FreeCount;
13050  const VkDeviceSize freeSize = it->offset - offset;
13051  VmaSuballocation suballoc = {
13052  offset, // offset
13053  freeSize, // size
13054  VMA_NULL, // hAllocation
13055  VMA_SUBALLOCATION_TYPE_FREE };
13056  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13057  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13058  {
13059  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13060  }
13061  }
13062 
13063  pMetadata->m_SumFreeSize -= it->size;
13064  offset = it->offset + it->size;
13065  }
13066 
13067  // Need to insert trailing free space.
13068  if(offset < blockSize)
13069  {
13070  ++pMetadata->m_FreeCount;
13071  const VkDeviceSize freeSize = blockSize - offset;
13072  VmaSuballocation suballoc = {
13073  offset, // offset
13074  freeSize, // size
13075  VMA_NULL, // hAllocation
13076  VMA_SUBALLOCATION_TYPE_FREE };
13077  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13078  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13079  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13080  {
13081  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13082  }
13083  }
13084 
13085  VMA_SORT(
13086  pMetadata->m_FreeSuballocationsBySize.begin(),
13087  pMetadata->m_FreeSuballocationsBySize.end(),
13088  VmaSuballocationItemSizeLess());
13089  }
13090 
13091  VMA_HEAVY_ASSERT(pMetadata->Validate());
13092  }
13093 }
13094 
13095 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13096 {
13097  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13098  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13099  while(it != pMetadata->m_Suballocations.end())
13100  {
13101  if(it->offset < suballoc.offset)
13102  {
13103  ++it;
13104  }
13105  }
13106  pMetadata->m_Suballocations.insert(it, suballoc);
13107 }
13108 
13110 // VmaBlockVectorDefragmentationContext
13111 
13112 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13113  VmaAllocator hAllocator,
13114  VmaPool hCustomPool,
13115  VmaBlockVector* pBlockVector,
13116  uint32_t currFrameIndex,
13117  uint32_t algorithmFlags) :
13118  res(VK_SUCCESS),
13119  mutexLocked(false),
13120  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13121  m_hAllocator(hAllocator),
13122  m_hCustomPool(hCustomPool),
13123  m_pBlockVector(pBlockVector),
13124  m_CurrFrameIndex(currFrameIndex),
13125  m_AlgorithmFlags(algorithmFlags),
13126  m_pAlgorithm(VMA_NULL),
13127  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13128  m_AllAllocations(false)
13129 {
13130 }
13131 
13132 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13133 {
13134  vma_delete(m_hAllocator, m_pAlgorithm);
13135 }
13136 
13137 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13138 {
13139  AllocInfo info = { hAlloc, pChanged };
13140  m_Allocations.push_back(info);
13141 }
13142 
13143 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13144 {
13145  const bool allAllocations = m_AllAllocations ||
13146  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13147 
13148  /********************************
13149  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13150  ********************************/
13151 
13152  /*
13153  Fast algorithm is supported only when certain criteria are met:
13154  - VMA_DEBUG_MARGIN is 0.
13155  - All allocations in this block vector are moveable.
13156  - There is no possibility of image/buffer granularity conflict.
13157  */
13158  if(VMA_DEBUG_MARGIN == 0 &&
13159  allAllocations &&
13160  !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13161  {
13162  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13163  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13164  }
13165  else
13166  {
13167  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13168  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13169  }
13170 
13171  if(allAllocations)
13172  {
13173  m_pAlgorithm->AddAll();
13174  }
13175  else
13176  {
13177  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13178  {
13179  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13180  }
13181  }
13182 }
13183 
13185 // VmaDefragmentationContext
13186 
13187 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13188  VmaAllocator hAllocator,
13189  uint32_t currFrameIndex,
13190  uint32_t flags,
13191  VmaDefragmentationStats* pStats) :
13192  m_hAllocator(hAllocator),
13193  m_CurrFrameIndex(currFrameIndex),
13194  m_Flags(flags),
13195  m_pStats(pStats),
13196  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13197 {
13198  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13199 }
13200 
13201 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13202 {
13203  for(size_t i = m_CustomPoolContexts.size(); i--; )
13204  {
13205  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13206  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13207  vma_delete(m_hAllocator, pBlockVectorCtx);
13208  }
13209  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13210  {
13211  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13212  if(pBlockVectorCtx)
13213  {
13214  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13215  vma_delete(m_hAllocator, pBlockVectorCtx);
13216  }
13217  }
13218 }
13219 
13220 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13221 {
13222  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13223  {
13224  VmaPool pool = pPools[poolIndex];
13225  VMA_ASSERT(pool);
13226  // Pools with algorithm other than default are not defragmented.
13227  if(pool->m_BlockVector.GetAlgorithm() == 0)
13228  {
13229  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13230 
13231  for(size_t i = m_CustomPoolContexts.size(); i--; )
13232  {
13233  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13234  {
13235  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13236  break;
13237  }
13238  }
13239 
13240  if(!pBlockVectorDefragCtx)
13241  {
13242  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13243  m_hAllocator,
13244  pool,
13245  &pool->m_BlockVector,
13246  m_CurrFrameIndex,
13247  m_Flags);
13248  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13249  }
13250 
13251  pBlockVectorDefragCtx->AddAll();
13252  }
13253  }
13254 }
13255 
13256 void VmaDefragmentationContext_T::AddAllocations(
13257  uint32_t allocationCount,
13258  VmaAllocation* pAllocations,
13259  VkBool32* pAllocationsChanged)
13260 {
13261  // Dispatch pAllocations among defragmentators. Create them when necessary.
13262  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13263  {
13264  const VmaAllocation hAlloc = pAllocations[allocIndex];
13265  VMA_ASSERT(hAlloc);
13266  // DedicatedAlloc cannot be defragmented.
13267  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13268  // Lost allocation cannot be defragmented.
13269  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13270  {
13271  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13272 
13273  const VmaPool hAllocPool = hAlloc->GetPool();
13274  // This allocation belongs to custom pool.
13275  if(hAllocPool != VK_NULL_HANDLE)
13276  {
13277  // Pools with algorithm other than default are not defragmented.
13278  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13279  {
13280  for(size_t i = m_CustomPoolContexts.size(); i--; )
13281  {
13282  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13283  {
13284  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13285  break;
13286  }
13287  }
13288  if(!pBlockVectorDefragCtx)
13289  {
13290  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13291  m_hAllocator,
13292  hAllocPool,
13293  &hAllocPool->m_BlockVector,
13294  m_CurrFrameIndex,
13295  m_Flags);
13296  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13297  }
13298  }
13299  }
13300  // This allocation belongs to default pool.
13301  else
13302  {
13303  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13304  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13305  if(!pBlockVectorDefragCtx)
13306  {
13307  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13308  m_hAllocator,
13309  VMA_NULL, // hCustomPool
13310  m_hAllocator->m_pBlockVectors[memTypeIndex],
13311  m_CurrFrameIndex,
13312  m_Flags);
13313  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13314  }
13315  }
13316 
13317  if(pBlockVectorDefragCtx)
13318  {
13319  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13320  &pAllocationsChanged[allocIndex] : VMA_NULL;
13321  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13322  }
13323  }
13324  }
13325 }
13326 
13327 VkResult VmaDefragmentationContext_T::Defragment(
13328  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13329  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13330  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13331 {
13332  if(pStats)
13333  {
13334  memset(pStats, 0, sizeof(VmaDefragmentationStats));
13335  }
13336 
13337  if(commandBuffer == VK_NULL_HANDLE)
13338  {
13339  maxGpuBytesToMove = 0;
13340  maxGpuAllocationsToMove = 0;
13341  }
13342 
13343  VkResult res = VK_SUCCESS;
13344 
13345  // Process default pools.
13346  for(uint32_t memTypeIndex = 0;
13347  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13348  ++memTypeIndex)
13349  {
13350  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13351  if(pBlockVectorCtx)
13352  {
13353  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13354  pBlockVectorCtx->GetBlockVector()->Defragment(
13355  pBlockVectorCtx,
13356  pStats,
13357  maxCpuBytesToMove, maxCpuAllocationsToMove,
13358  maxGpuBytesToMove, maxGpuAllocationsToMove,
13359  commandBuffer);
13360  if(pBlockVectorCtx->res != VK_SUCCESS)
13361  {
13362  res = pBlockVectorCtx->res;
13363  }
13364  }
13365  }
13366 
13367  // Process custom pools.
13368  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13369  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13370  ++customCtxIndex)
13371  {
13372  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13373  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13374  pBlockVectorCtx->GetBlockVector()->Defragment(
13375  pBlockVectorCtx,
13376  pStats,
13377  maxCpuBytesToMove, maxCpuAllocationsToMove,
13378  maxGpuBytesToMove, maxGpuAllocationsToMove,
13379  commandBuffer);
13380  if(pBlockVectorCtx->res != VK_SUCCESS)
13381  {
13382  res = pBlockVectorCtx->res;
13383  }
13384  }
13385 
13386  return res;
13387 }
13388 
13390 // VmaRecorder
13391 
13392 #if VMA_RECORDING_ENABLED
13393 
13394 VmaRecorder::VmaRecorder() :
13395  m_UseMutex(true),
13396  m_Flags(0),
13397  m_File(VMA_NULL),
13398  m_Freq(INT64_MAX),
13399  m_StartCounter(INT64_MAX)
13400 {
13401 }
13402 
13403 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13404 {
13405  m_UseMutex = useMutex;
13406  m_Flags = settings.flags;
13407 
13408  QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13409  QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13410 
13411  // Open file for writing.
13412  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13413  if(err != 0)
13414  {
13415  return VK_ERROR_INITIALIZATION_FAILED;
13416  }
13417 
13418  // Write header.
13419  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13420  fprintf(m_File, "%s\n", "1,5");
13421 
13422  return VK_SUCCESS;
13423 }
13424 
13425 VmaRecorder::~VmaRecorder()
13426 {
13427  if(m_File != VMA_NULL)
13428  {
13429  fclose(m_File);
13430  }
13431 }
13432 
13433 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13434 {
13435  CallParams callParams;
13436  GetBasicParams(callParams);
13437 
13438  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13439  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13440  Flush();
13441 }
13442 
13443 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13444 {
13445  CallParams callParams;
13446  GetBasicParams(callParams);
13447 
13448  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13449  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13450  Flush();
13451 }
13452 
13453 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13454 {
13455  CallParams callParams;
13456  GetBasicParams(callParams);
13457 
13458  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13459  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13460  createInfo.memoryTypeIndex,
13461  createInfo.flags,
13462  createInfo.blockSize,
13463  (uint64_t)createInfo.minBlockCount,
13464  (uint64_t)createInfo.maxBlockCount,
13465  createInfo.frameInUseCount,
13466  pool);
13467  Flush();
13468 }
13469 
13470 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13471 {
13472  CallParams callParams;
13473  GetBasicParams(callParams);
13474 
13475  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13476  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13477  pool);
13478  Flush();
13479 }
13480 
13481 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13482  const VkMemoryRequirements& vkMemReq,
13483  const VmaAllocationCreateInfo& createInfo,
13484  VmaAllocation allocation)
13485 {
13486  CallParams callParams;
13487  GetBasicParams(callParams);
13488 
13489  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13490  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13491  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13492  vkMemReq.size,
13493  vkMemReq.alignment,
13494  vkMemReq.memoryTypeBits,
13495  createInfo.flags,
13496  createInfo.usage,
13497  createInfo.requiredFlags,
13498  createInfo.preferredFlags,
13499  createInfo.memoryTypeBits,
13500  createInfo.pool,
13501  allocation,
13502  userDataStr.GetString());
13503  Flush();
13504 }
13505 
13506 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13507  const VkMemoryRequirements& vkMemReq,
13508  const VmaAllocationCreateInfo& createInfo,
13509  uint64_t allocationCount,
13510  const VmaAllocation* pAllocations)
13511 {
13512  CallParams callParams;
13513  GetBasicParams(callParams);
13514 
13515  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13516  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13517  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13518  vkMemReq.size,
13519  vkMemReq.alignment,
13520  vkMemReq.memoryTypeBits,
13521  createInfo.flags,
13522  createInfo.usage,
13523  createInfo.requiredFlags,
13524  createInfo.preferredFlags,
13525  createInfo.memoryTypeBits,
13526  createInfo.pool);
13527  PrintPointerList(allocationCount, pAllocations);
13528  fprintf(m_File, ",%s\n", userDataStr.GetString());
13529  Flush();
13530 }
13531 
13532 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13533  const VkMemoryRequirements& vkMemReq,
13534  bool requiresDedicatedAllocation,
13535  bool prefersDedicatedAllocation,
13536  const VmaAllocationCreateInfo& createInfo,
13537  VmaAllocation allocation)
13538 {
13539  CallParams callParams;
13540  GetBasicParams(callParams);
13541 
13542  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13543  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13544  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,
13545  vkMemReq.size,
13546  vkMemReq.alignment,
13547  vkMemReq.memoryTypeBits,
13548  requiresDedicatedAllocation ? 1 : 0,
13549  prefersDedicatedAllocation ? 1 : 0,
13550  createInfo.flags,
13551  createInfo.usage,
13552  createInfo.requiredFlags,
13553  createInfo.preferredFlags,
13554  createInfo.memoryTypeBits,
13555  createInfo.pool,
13556  allocation,
13557  userDataStr.GetString());
13558  Flush();
13559 }
13560 
13561 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13562  const VkMemoryRequirements& vkMemReq,
13563  bool requiresDedicatedAllocation,
13564  bool prefersDedicatedAllocation,
13565  const VmaAllocationCreateInfo& createInfo,
13566  VmaAllocation allocation)
13567 {
13568  CallParams callParams;
13569  GetBasicParams(callParams);
13570 
13571  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13572  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13573  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,
13574  vkMemReq.size,
13575  vkMemReq.alignment,
13576  vkMemReq.memoryTypeBits,
13577  requiresDedicatedAllocation ? 1 : 0,
13578  prefersDedicatedAllocation ? 1 : 0,
13579  createInfo.flags,
13580  createInfo.usage,
13581  createInfo.requiredFlags,
13582  createInfo.preferredFlags,
13583  createInfo.memoryTypeBits,
13584  createInfo.pool,
13585  allocation,
13586  userDataStr.GetString());
13587  Flush();
13588 }
13589 
13590 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13591  VmaAllocation allocation)
13592 {
13593  CallParams callParams;
13594  GetBasicParams(callParams);
13595 
13596  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13597  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13598  allocation);
13599  Flush();
13600 }
13601 
13602 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13603  uint64_t allocationCount,
13604  const VmaAllocation* pAllocations)
13605 {
13606  CallParams callParams;
13607  GetBasicParams(callParams);
13608 
13609  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13610  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13611  PrintPointerList(allocationCount, pAllocations);
13612  fprintf(m_File, "\n");
13613  Flush();
13614 }
13615 
13616 void VmaRecorder::RecordResizeAllocation(
13617  uint32_t frameIndex,
13618  VmaAllocation allocation,
13619  VkDeviceSize newSize)
13620 {
13621  CallParams callParams;
13622  GetBasicParams(callParams);
13623 
13624  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13625  fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13626  allocation, newSize);
13627  Flush();
13628 }
13629 
13630 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13631  VmaAllocation allocation,
13632  const void* pUserData)
13633 {
13634  CallParams callParams;
13635  GetBasicParams(callParams);
13636 
13637  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13638  UserDataString userDataStr(
13639  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13640  pUserData);
13641  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13642  allocation,
13643  userDataStr.GetString());
13644  Flush();
13645 }
13646 
13647 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13648  VmaAllocation allocation)
13649 {
13650  CallParams callParams;
13651  GetBasicParams(callParams);
13652 
13653  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13654  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13655  allocation);
13656  Flush();
13657 }
13658 
13659 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13660  VmaAllocation allocation)
13661 {
13662  CallParams callParams;
13663  GetBasicParams(callParams);
13664 
13665  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13666  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13667  allocation);
13668  Flush();
13669 }
13670 
13671 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13672  VmaAllocation allocation)
13673 {
13674  CallParams callParams;
13675  GetBasicParams(callParams);
13676 
13677  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13678  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13679  allocation);
13680  Flush();
13681 }
13682 
13683 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13684  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13685 {
13686  CallParams callParams;
13687  GetBasicParams(callParams);
13688 
13689  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13690  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13691  allocation,
13692  offset,
13693  size);
13694  Flush();
13695 }
13696 
13697 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13698  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13699 {
13700  CallParams callParams;
13701  GetBasicParams(callParams);
13702 
13703  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13704  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13705  allocation,
13706  offset,
13707  size);
13708  Flush();
13709 }
13710 
13711 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13712  const VkBufferCreateInfo& bufCreateInfo,
13713  const VmaAllocationCreateInfo& allocCreateInfo,
13714  VmaAllocation allocation)
13715 {
13716  CallParams callParams;
13717  GetBasicParams(callParams);
13718 
13719  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13720  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13721  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,
13722  bufCreateInfo.flags,
13723  bufCreateInfo.size,
13724  bufCreateInfo.usage,
13725  bufCreateInfo.sharingMode,
13726  allocCreateInfo.flags,
13727  allocCreateInfo.usage,
13728  allocCreateInfo.requiredFlags,
13729  allocCreateInfo.preferredFlags,
13730  allocCreateInfo.memoryTypeBits,
13731  allocCreateInfo.pool,
13732  allocation,
13733  userDataStr.GetString());
13734  Flush();
13735 }
13736 
13737 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13738  const VkImageCreateInfo& imageCreateInfo,
13739  const VmaAllocationCreateInfo& allocCreateInfo,
13740  VmaAllocation allocation)
13741 {
13742  CallParams callParams;
13743  GetBasicParams(callParams);
13744 
13745  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13746  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13747  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,
13748  imageCreateInfo.flags,
13749  imageCreateInfo.imageType,
13750  imageCreateInfo.format,
13751  imageCreateInfo.extent.width,
13752  imageCreateInfo.extent.height,
13753  imageCreateInfo.extent.depth,
13754  imageCreateInfo.mipLevels,
13755  imageCreateInfo.arrayLayers,
13756  imageCreateInfo.samples,
13757  imageCreateInfo.tiling,
13758  imageCreateInfo.usage,
13759  imageCreateInfo.sharingMode,
13760  imageCreateInfo.initialLayout,
13761  allocCreateInfo.flags,
13762  allocCreateInfo.usage,
13763  allocCreateInfo.requiredFlags,
13764  allocCreateInfo.preferredFlags,
13765  allocCreateInfo.memoryTypeBits,
13766  allocCreateInfo.pool,
13767  allocation,
13768  userDataStr.GetString());
13769  Flush();
13770 }
13771 
13772 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13773  VmaAllocation allocation)
13774 {
13775  CallParams callParams;
13776  GetBasicParams(callParams);
13777 
13778  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13779  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13780  allocation);
13781  Flush();
13782 }
13783 
13784 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13785  VmaAllocation allocation)
13786 {
13787  CallParams callParams;
13788  GetBasicParams(callParams);
13789 
13790  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13791  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13792  allocation);
13793  Flush();
13794 }
13795 
13796 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13797  VmaAllocation allocation)
13798 {
13799  CallParams callParams;
13800  GetBasicParams(callParams);
13801 
13802  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13803  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13804  allocation);
13805  Flush();
13806 }
13807 
13808 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13809  VmaAllocation allocation)
13810 {
13811  CallParams callParams;
13812  GetBasicParams(callParams);
13813 
13814  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13815  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13816  allocation);
13817  Flush();
13818 }
13819 
13820 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13821  VmaPool pool)
13822 {
13823  CallParams callParams;
13824  GetBasicParams(callParams);
13825 
13826  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13827  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13828  pool);
13829  Flush();
13830 }
13831 
13832 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13833  const VmaDefragmentationInfo2& info,
13835 {
13836  CallParams callParams;
13837  GetBasicParams(callParams);
13838 
13839  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13840  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13841  info.flags);
13842  PrintPointerList(info.allocationCount, info.pAllocations);
13843  fprintf(m_File, ",");
13844  PrintPointerList(info.poolCount, info.pPools);
13845  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13846  info.maxCpuBytesToMove,
13848  info.maxGpuBytesToMove,
13850  info.commandBuffer,
13851  ctx);
13852  Flush();
13853 }
13854 
13855 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13857 {
13858  CallParams callParams;
13859  GetBasicParams(callParams);
13860 
13861  VmaMutexLock lock(m_FileMutex, m_UseMutex);
13862  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13863  ctx);
13864  Flush();
13865 }
13866 
13867 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13868 {
13869  if(pUserData != VMA_NULL)
13870  {
13871  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13872  {
13873  m_Str = (const char*)pUserData;
13874  }
13875  else
13876  {
13877  sprintf_s(m_PtrStr, "%p", pUserData);
13878  m_Str = m_PtrStr;
13879  }
13880  }
13881  else
13882  {
13883  m_Str = "";
13884  }
13885 }
13886 
13887 void VmaRecorder::WriteConfiguration(
13888  const VkPhysicalDeviceProperties& devProps,
13889  const VkPhysicalDeviceMemoryProperties& memProps,
13890  bool dedicatedAllocationExtensionEnabled)
13891 {
13892  fprintf(m_File, "Config,Begin\n");
13893 
13894  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13895  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13896  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13897  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13898  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13899  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13900 
13901  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13902  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13903  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13904 
13905  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13906  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13907  {
13908  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13909  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13910  }
13911  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13912  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13913  {
13914  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13915  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13916  }
13917 
13918  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13919 
13920  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13921  fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13922  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13923  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13924  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13925  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13926  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13927  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13928  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13929 
13930  fprintf(m_File, "Config,End\n");
13931 }
13932 
13933 void VmaRecorder::GetBasicParams(CallParams& outParams)
13934 {
13935  outParams.threadId = GetCurrentThreadId();
13936 
13937  LARGE_INTEGER counter;
13938  QueryPerformanceCounter(&counter);
13939  outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13940 }
13941 
13942 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13943 {
13944  if(count)
13945  {
13946  fprintf(m_File, "%p", pItems[0]);
13947  for(uint64_t i = 1; i < count; ++i)
13948  {
13949  fprintf(m_File, " %p", pItems[i]);
13950  }
13951  }
13952 }
13953 
13954 void VmaRecorder::Flush()
13955 {
13956  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13957  {
13958  fflush(m_File);
13959  }
13960 }
13961 
13962 #endif // #if VMA_RECORDING_ENABLED
13963 
13965 // VmaAllocator_T
13966 
13967 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13968  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13969  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13970  m_hDevice(pCreateInfo->device),
13971  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13972  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13973  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13974  m_PreferredLargeHeapBlockSize(0),
13975  m_PhysicalDevice(pCreateInfo->physicalDevice),
13976  m_CurrentFrameIndex(0),
13977  m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13978  m_NextPoolId(0)
13980  ,m_pRecorder(VMA_NULL)
13981 #endif
13982 {
13983  if(VMA_DEBUG_DETECT_CORRUPTION)
13984  {
13985  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
13986  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
13987  }
13988 
13989  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
13990 
13991 #if !(VMA_DEDICATED_ALLOCATION)
13993  {
13994  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
13995  }
13996 #endif
13997 
13998  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
13999  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14000  memset(&m_MemProps, 0, sizeof(m_MemProps));
14001 
14002  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14003  memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14004 
14005  for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14006  {
14007  m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14008  }
14009 
14010  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14011  {
14012  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14013  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14014  }
14015 
14016  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14017 
14018  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14019  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14020 
14021  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14022  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14023  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14024  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14025 
14026  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14027  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14028 
14029  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14030  {
14031  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14032  {
14033  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14034  if(limit != VK_WHOLE_SIZE)
14035  {
14036  m_HeapSizeLimit[heapIndex] = limit;
14037  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14038  {
14039  m_MemProps.memoryHeaps[heapIndex].size = limit;
14040  }
14041  }
14042  }
14043  }
14044 
14045  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14046  {
14047  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14048 
14049  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14050  this,
14051  memTypeIndex,
14052  preferredBlockSize,
14053  0,
14054  SIZE_MAX,
14055  GetBufferImageGranularity(),
14056  pCreateInfo->frameInUseCount,
14057  false, // isCustomPool
14058  false, // explicitBlockSize
14059  false); // linearAlgorithm
14060  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14061  // becase minBlockCount is 0.
14062  m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14063 
14064  }
14065 }
14066 
14067 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14068 {
14069  VkResult res = VK_SUCCESS;
14070 
14071  if(pCreateInfo->pRecordSettings != VMA_NULL &&
14072  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14073  {
14074 #if VMA_RECORDING_ENABLED
14075  m_pRecorder = vma_new(this, VmaRecorder)();
14076  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14077  if(res != VK_SUCCESS)
14078  {
14079  return res;
14080  }
14081  m_pRecorder->WriteConfiguration(
14082  m_PhysicalDeviceProperties,
14083  m_MemProps,
14084  m_UseKhrDedicatedAllocation);
14085  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14086 #else
14087  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14088  return VK_ERROR_FEATURE_NOT_PRESENT;
14089 #endif
14090  }
14091 
14092  return res;
14093 }
14094 
14095 VmaAllocator_T::~VmaAllocator_T()
14096 {
14097 #if VMA_RECORDING_ENABLED
14098  if(m_pRecorder != VMA_NULL)
14099  {
14100  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14101  vma_delete(this, m_pRecorder);
14102  }
14103 #endif
14104 
14105  VMA_ASSERT(m_Pools.empty());
14106 
14107  for(size_t i = GetMemoryTypeCount(); i--; )
14108  {
14109  vma_delete(this, m_pDedicatedAllocations[i]);
14110  vma_delete(this, m_pBlockVectors[i]);
14111  }
14112 }
14113 
14114 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14115 {
14116 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14117  m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14118  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14119  m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14120  m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14121  m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14122  m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14123  m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14124  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14125  m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14126  m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14127  m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14128  m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14129  m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14130  m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14131  m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14132  m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14133  m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14134 #if VMA_DEDICATED_ALLOCATION
14135  if(m_UseKhrDedicatedAllocation)
14136  {
14137  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14138  (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14139  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14140  (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14141  }
14142 #endif // #if VMA_DEDICATED_ALLOCATION
14143 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14144 
14145 #define VMA_COPY_IF_NOT_NULL(funcName) \
14146  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14147 
14148  if(pVulkanFunctions != VMA_NULL)
14149  {
14150  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14151  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14152  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14153  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14154  VMA_COPY_IF_NOT_NULL(vkMapMemory);
14155  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14156  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14157  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14158  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14159  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14160  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14161  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14162  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14163  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14164  VMA_COPY_IF_NOT_NULL(vkCreateImage);
14165  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14166  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14167 #if VMA_DEDICATED_ALLOCATION
14168  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14169  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14170 #endif
14171  }
14172 
14173 #undef VMA_COPY_IF_NOT_NULL
14174 
14175  // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14176  // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14177  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14178  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14179  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14180  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14181  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14182  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14183  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14184  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14185  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14186  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14187  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14188  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14189  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14190  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14191  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14192  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14193  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14194 #if VMA_DEDICATED_ALLOCATION
14195  if(m_UseKhrDedicatedAllocation)
14196  {
14197  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14198  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14199  }
14200 #endif
14201 }
14202 
14203 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14204 {
14205  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14206  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14207  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14208  return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14209 }
14210 
14211 VkResult VmaAllocator_T::AllocateMemoryOfType(
14212  VkDeviceSize size,
14213  VkDeviceSize alignment,
14214  bool dedicatedAllocation,
14215  VkBuffer dedicatedBuffer,
14216  VkImage dedicatedImage,
14217  const VmaAllocationCreateInfo& createInfo,
14218  uint32_t memTypeIndex,
14219  VmaSuballocationType suballocType,
14220  size_t allocationCount,
14221  VmaAllocation* pAllocations)
14222 {
14223  VMA_ASSERT(pAllocations != VMA_NULL);
14224  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14225 
14226  VmaAllocationCreateInfo finalCreateInfo = createInfo;
14227 
14228  // If memory type is not HOST_VISIBLE, disable MAPPED.
14229  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14230  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14231  {
14232  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14233  }
14234 
14235  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14236  VMA_ASSERT(blockVector);
14237 
14238  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14239  bool preferDedicatedMemory =
14240  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14241  dedicatedAllocation ||
14242  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14243  size > preferredBlockSize / 2;
14244 
14245  if(preferDedicatedMemory &&
14246  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14247  finalCreateInfo.pool == VK_NULL_HANDLE)
14248  {
14250  }
14251 
14252  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14253  {
14254  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14255  {
14256  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14257  }
14258  else
14259  {
14260  return AllocateDedicatedMemory(
14261  size,
14262  suballocType,
14263  memTypeIndex,
14264  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14265  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14266  finalCreateInfo.pUserData,
14267  dedicatedBuffer,
14268  dedicatedImage,
14269  allocationCount,
14270  pAllocations);
14271  }
14272  }
14273  else
14274  {
14275  VkResult res = blockVector->Allocate(
14276  VK_NULL_HANDLE, // hCurrentPool
14277  m_CurrentFrameIndex.load(),
14278  size,
14279  alignment,
14280  finalCreateInfo,
14281  suballocType,
14282  allocationCount,
14283  pAllocations);
14284  if(res == VK_SUCCESS)
14285  {
14286  return res;
14287  }
14288 
14289  // 5. Try dedicated memory.
14290  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14291  {
14292  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14293  }
14294  else
14295  {
14296  res = AllocateDedicatedMemory(
14297  size,
14298  suballocType,
14299  memTypeIndex,
14300  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14301  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14302  finalCreateInfo.pUserData,
14303  dedicatedBuffer,
14304  dedicatedImage,
14305  allocationCount,
14306  pAllocations);
14307  if(res == VK_SUCCESS)
14308  {
14309  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14310  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14311  return VK_SUCCESS;
14312  }
14313  else
14314  {
14315  // Everything failed: Return error code.
14316  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14317  return res;
14318  }
14319  }
14320  }
14321 }
14322 
14323 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14324  VkDeviceSize size,
14325  VmaSuballocationType suballocType,
14326  uint32_t memTypeIndex,
14327  bool map,
14328  bool isUserDataString,
14329  void* pUserData,
14330  VkBuffer dedicatedBuffer,
14331  VkImage dedicatedImage,
14332  size_t allocationCount,
14333  VmaAllocation* pAllocations)
14334 {
14335  VMA_ASSERT(allocationCount > 0 && pAllocations);
14336 
14337  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14338  allocInfo.memoryTypeIndex = memTypeIndex;
14339  allocInfo.allocationSize = size;
14340 
14341 #if VMA_DEDICATED_ALLOCATION
14342  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14343  if(m_UseKhrDedicatedAllocation)
14344  {
14345  if(dedicatedBuffer != VK_NULL_HANDLE)
14346  {
14347  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14348  dedicatedAllocInfo.buffer = dedicatedBuffer;
14349  allocInfo.pNext = &dedicatedAllocInfo;
14350  }
14351  else if(dedicatedImage != VK_NULL_HANDLE)
14352  {
14353  dedicatedAllocInfo.image = dedicatedImage;
14354  allocInfo.pNext = &dedicatedAllocInfo;
14355  }
14356  }
14357 #endif // #if VMA_DEDICATED_ALLOCATION
14358 
14359  size_t allocIndex;
14360  VkResult res;
14361  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14362  {
14363  res = AllocateDedicatedMemoryPage(
14364  size,
14365  suballocType,
14366  memTypeIndex,
14367  allocInfo,
14368  map,
14369  isUserDataString,
14370  pUserData,
14371  pAllocations + allocIndex);
14372  if(res != VK_SUCCESS)
14373  {
14374  break;
14375  }
14376  }
14377 
14378  if(res == VK_SUCCESS)
14379  {
14380  // Register them in m_pDedicatedAllocations.
14381  {
14382  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14383  AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14384  VMA_ASSERT(pDedicatedAllocations);
14385  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14386  {
14387  VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14388  }
14389  }
14390 
14391  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14392  }
14393  else
14394  {
14395  // Free all already created allocations.
14396  while(allocIndex--)
14397  {
14398  VmaAllocation currAlloc = pAllocations[allocIndex];
14399  VkDeviceMemory hMemory = currAlloc->GetMemory();
14400 
14401  /*
14402  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14403  before vkFreeMemory.
14404 
14405  if(currAlloc->GetMappedData() != VMA_NULL)
14406  {
14407  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14408  }
14409  */
14410 
14411  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14412 
14413  currAlloc->SetUserData(this, VMA_NULL);
14414  vma_delete(this, currAlloc);
14415  }
14416 
14417  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14418  }
14419 
14420  return res;
14421 }
14422 
14423 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14424  VkDeviceSize size,
14425  VmaSuballocationType suballocType,
14426  uint32_t memTypeIndex,
14427  const VkMemoryAllocateInfo& allocInfo,
14428  bool map,
14429  bool isUserDataString,
14430  void* pUserData,
14431  VmaAllocation* pAllocation)
14432 {
14433  VkDeviceMemory hMemory = VK_NULL_HANDLE;
14434  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14435  if(res < 0)
14436  {
14437  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14438  return res;
14439  }
14440 
14441  void* pMappedData = VMA_NULL;
14442  if(map)
14443  {
14444  res = (*m_VulkanFunctions.vkMapMemory)(
14445  m_hDevice,
14446  hMemory,
14447  0,
14448  VK_WHOLE_SIZE,
14449  0,
14450  &pMappedData);
14451  if(res < 0)
14452  {
14453  VMA_DEBUG_LOG(" vkMapMemory FAILED");
14454  FreeVulkanMemory(memTypeIndex, size, hMemory);
14455  return res;
14456  }
14457  }
14458 
14459  *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14460  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14461  (*pAllocation)->SetUserData(this, pUserData);
14462  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14463  {
14464  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14465  }
14466 
14467  return VK_SUCCESS;
14468 }
14469 
14470 void VmaAllocator_T::GetBufferMemoryRequirements(
14471  VkBuffer hBuffer,
14472  VkMemoryRequirements& memReq,
14473  bool& requiresDedicatedAllocation,
14474  bool& prefersDedicatedAllocation) const
14475 {
14476 #if VMA_DEDICATED_ALLOCATION
14477  if(m_UseKhrDedicatedAllocation)
14478  {
14479  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14480  memReqInfo.buffer = hBuffer;
14481 
14482  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14483 
14484  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14485  memReq2.pNext = &memDedicatedReq;
14486 
14487  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14488 
14489  memReq = memReq2.memoryRequirements;
14490  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14491  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14492  }
14493  else
14494 #endif // #if VMA_DEDICATED_ALLOCATION
14495  {
14496  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14497  requiresDedicatedAllocation = false;
14498  prefersDedicatedAllocation = false;
14499  }
14500 }
14501 
14502 void VmaAllocator_T::GetImageMemoryRequirements(
14503  VkImage hImage,
14504  VkMemoryRequirements& memReq,
14505  bool& requiresDedicatedAllocation,
14506  bool& prefersDedicatedAllocation) const
14507 {
14508 #if VMA_DEDICATED_ALLOCATION
14509  if(m_UseKhrDedicatedAllocation)
14510  {
14511  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14512  memReqInfo.image = hImage;
14513 
14514  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14515 
14516  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14517  memReq2.pNext = &memDedicatedReq;
14518 
14519  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14520 
14521  memReq = memReq2.memoryRequirements;
14522  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14523  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14524  }
14525  else
14526 #endif // #if VMA_DEDICATED_ALLOCATION
14527  {
14528  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14529  requiresDedicatedAllocation = false;
14530  prefersDedicatedAllocation = false;
14531  }
14532 }
14533 
14534 VkResult VmaAllocator_T::AllocateMemory(
14535  const VkMemoryRequirements& vkMemReq,
14536  bool requiresDedicatedAllocation,
14537  bool prefersDedicatedAllocation,
14538  VkBuffer dedicatedBuffer,
14539  VkImage dedicatedImage,
14540  const VmaAllocationCreateInfo& createInfo,
14541  VmaSuballocationType suballocType,
14542  size_t allocationCount,
14543  VmaAllocation* pAllocations)
14544 {
14545  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14546 
14547  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14548 
14549  if(vkMemReq.size == 0)
14550  {
14551  return VK_ERROR_VALIDATION_FAILED_EXT;
14552  }
14553  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14554  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14555  {
14556  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14557  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14558  }
14559  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14561  {
14562  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14563  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14564  }
14565  if(requiresDedicatedAllocation)
14566  {
14567  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14568  {
14569  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14570  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14571  }
14572  if(createInfo.pool != VK_NULL_HANDLE)
14573  {
14574  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14575  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14576  }
14577  }
14578  if((createInfo.pool != VK_NULL_HANDLE) &&
14579  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14580  {
14581  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14582  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14583  }
14584 
14585  if(createInfo.pool != VK_NULL_HANDLE)
14586  {
14587  const VkDeviceSize alignmentForPool = VMA_MAX(
14588  vkMemReq.alignment,
14589  GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14590  return createInfo.pool->m_BlockVector.Allocate(
14591  createInfo.pool,
14592  m_CurrentFrameIndex.load(),
14593  vkMemReq.size,
14594  alignmentForPool,
14595  createInfo,
14596  suballocType,
14597  allocationCount,
14598  pAllocations);
14599  }
14600  else
14601  {
14602  // Bit mask of memory Vulkan types acceptable for this allocation.
14603  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14604  uint32_t memTypeIndex = UINT32_MAX;
14605  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14606  if(res == VK_SUCCESS)
14607  {
14608  VkDeviceSize alignmentForMemType = VMA_MAX(
14609  vkMemReq.alignment,
14610  GetMemoryTypeMinAlignment(memTypeIndex));
14611 
14612  res = AllocateMemoryOfType(
14613  vkMemReq.size,
14614  alignmentForMemType,
14615  requiresDedicatedAllocation || prefersDedicatedAllocation,
14616  dedicatedBuffer,
14617  dedicatedImage,
14618  createInfo,
14619  memTypeIndex,
14620  suballocType,
14621  allocationCount,
14622  pAllocations);
14623  // Succeeded on first try.
14624  if(res == VK_SUCCESS)
14625  {
14626  return res;
14627  }
14628  // Allocation from this memory type failed. Try other compatible memory types.
14629  else
14630  {
14631  for(;;)
14632  {
14633  // Remove old memTypeIndex from list of possibilities.
14634  memoryTypeBits &= ~(1u << memTypeIndex);
14635  // Find alternative memTypeIndex.
14636  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14637  if(res == VK_SUCCESS)
14638  {
14639  alignmentForMemType = VMA_MAX(
14640  vkMemReq.alignment,
14641  GetMemoryTypeMinAlignment(memTypeIndex));
14642 
14643  res = AllocateMemoryOfType(
14644  vkMemReq.size,
14645  alignmentForMemType,
14646  requiresDedicatedAllocation || prefersDedicatedAllocation,
14647  dedicatedBuffer,
14648  dedicatedImage,
14649  createInfo,
14650  memTypeIndex,
14651  suballocType,
14652  allocationCount,
14653  pAllocations);
14654  // Allocation from this alternative memory type succeeded.
14655  if(res == VK_SUCCESS)
14656  {
14657  return res;
14658  }
14659  // else: Allocation from this memory type failed. Try next one - next loop iteration.
14660  }
14661  // No other matching memory type index could be found.
14662  else
14663  {
14664  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14665  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14666  }
14667  }
14668  }
14669  }
14670  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14671  else
14672  return res;
14673  }
14674 }
14675 
14676 void VmaAllocator_T::FreeMemory(
14677  size_t allocationCount,
14678  const VmaAllocation* pAllocations)
14679 {
14680  VMA_ASSERT(pAllocations);
14681 
14682  for(size_t allocIndex = allocationCount; allocIndex--; )
14683  {
14684  VmaAllocation allocation = pAllocations[allocIndex];
14685 
14686  if(allocation != VK_NULL_HANDLE)
14687  {
14688  if(TouchAllocation(allocation))
14689  {
14690  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14691  {
14692  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14693  }
14694 
14695  switch(allocation->GetType())
14696  {
14697  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14698  {
14699  VmaBlockVector* pBlockVector = VMA_NULL;
14700  VmaPool hPool = allocation->GetPool();
14701  if(hPool != VK_NULL_HANDLE)
14702  {
14703  pBlockVector = &hPool->m_BlockVector;
14704  }
14705  else
14706  {
14707  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14708  pBlockVector = m_pBlockVectors[memTypeIndex];
14709  }
14710  pBlockVector->Free(allocation);
14711  }
14712  break;
14713  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14714  FreeDedicatedMemory(allocation);
14715  break;
14716  default:
14717  VMA_ASSERT(0);
14718  }
14719  }
14720 
14721  allocation->SetUserData(this, VMA_NULL);
14722  vma_delete(this, allocation);
14723  }
14724  }
14725 }
14726 
14727 VkResult VmaAllocator_T::ResizeAllocation(
14728  const VmaAllocation alloc,
14729  VkDeviceSize newSize)
14730 {
14731  if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14732  {
14733  return VK_ERROR_VALIDATION_FAILED_EXT;
14734  }
14735  if(newSize == alloc->GetSize())
14736  {
14737  return VK_SUCCESS;
14738  }
14739 
14740  switch(alloc->GetType())
14741  {
14742  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14743  return VK_ERROR_FEATURE_NOT_PRESENT;
14744  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14745  if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14746  {
14747  alloc->ChangeSize(newSize);
14748  VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14749  return VK_SUCCESS;
14750  }
14751  else
14752  {
14753  return VK_ERROR_OUT_OF_POOL_MEMORY;
14754  }
14755  default:
14756  VMA_ASSERT(0);
14757  return VK_ERROR_VALIDATION_FAILED_EXT;
14758  }
14759 }
14760 
14761 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14762 {
14763  // Initialize.
14764  InitStatInfo(pStats->total);
14765  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14766  InitStatInfo(pStats->memoryType[i]);
14767  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14768  InitStatInfo(pStats->memoryHeap[i]);
14769 
14770  // Process default pools.
14771  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14772  {
14773  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14774  VMA_ASSERT(pBlockVector);
14775  pBlockVector->AddStats(pStats);
14776  }
14777 
14778  // Process custom pools.
14779  {
14780  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14781  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14782  {
14783  m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14784  }
14785  }
14786 
14787  // Process dedicated allocations.
14788  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14789  {
14790  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14791  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14792  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14793  VMA_ASSERT(pDedicatedAllocVector);
14794  for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14795  {
14796  VmaStatInfo allocationStatInfo;
14797  (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14798  VmaAddStatInfo(pStats->total, allocationStatInfo);
14799  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14800  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14801  }
14802  }
14803 
14804  // Postprocess.
14805  VmaPostprocessCalcStatInfo(pStats->total);
14806  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14807  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14808  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14809  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14810 }
14811 
14812 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14813 
14814 VkResult VmaAllocator_T::DefragmentationBegin(
14815  const VmaDefragmentationInfo2& info,
14816  VmaDefragmentationStats* pStats,
14817  VmaDefragmentationContext* pContext)
14818 {
14819  if(info.pAllocationsChanged != VMA_NULL)
14820  {
14821  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14822  }
14823 
14824  *pContext = vma_new(this, VmaDefragmentationContext_T)(
14825  this, m_CurrentFrameIndex.load(), info.flags, pStats);
14826 
14827  (*pContext)->AddPools(info.poolCount, info.pPools);
14828  (*pContext)->AddAllocations(
14830 
14831  VkResult res = (*pContext)->Defragment(
14834  info.commandBuffer, pStats);
14835 
14836  if(res != VK_NOT_READY)
14837  {
14838  vma_delete(this, *pContext);
14839  *pContext = VMA_NULL;
14840  }
14841 
14842  return res;
14843 }
14844 
14845 VkResult VmaAllocator_T::DefragmentationEnd(
14846  VmaDefragmentationContext context)
14847 {
14848  vma_delete(this, context);
14849  return VK_SUCCESS;
14850 }
14851 
14852 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14853 {
14854  if(hAllocation->CanBecomeLost())
14855  {
14856  /*
14857  Warning: This is a carefully designed algorithm.
14858  Do not modify unless you really know what you're doing :)
14859  */
14860  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14861  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14862  for(;;)
14863  {
14864  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14865  {
14866  pAllocationInfo->memoryType = UINT32_MAX;
14867  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14868  pAllocationInfo->offset = 0;
14869  pAllocationInfo->size = hAllocation->GetSize();
14870  pAllocationInfo->pMappedData = VMA_NULL;
14871  pAllocationInfo->pUserData = hAllocation->GetUserData();
14872  return;
14873  }
14874  else if(localLastUseFrameIndex == localCurrFrameIndex)
14875  {
14876  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14877  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14878  pAllocationInfo->offset = hAllocation->GetOffset();
14879  pAllocationInfo->size = hAllocation->GetSize();
14880  pAllocationInfo->pMappedData = VMA_NULL;
14881  pAllocationInfo->pUserData = hAllocation->GetUserData();
14882  return;
14883  }
14884  else // Last use time earlier than current time.
14885  {
14886  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14887  {
14888  localLastUseFrameIndex = localCurrFrameIndex;
14889  }
14890  }
14891  }
14892  }
14893  else
14894  {
14895 #if VMA_STATS_STRING_ENABLED
14896  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14897  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14898  for(;;)
14899  {
14900  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14901  if(localLastUseFrameIndex == localCurrFrameIndex)
14902  {
14903  break;
14904  }
14905  else // Last use time earlier than current time.
14906  {
14907  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14908  {
14909  localLastUseFrameIndex = localCurrFrameIndex;
14910  }
14911  }
14912  }
14913 #endif
14914 
14915  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14916  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14917  pAllocationInfo->offset = hAllocation->GetOffset();
14918  pAllocationInfo->size = hAllocation->GetSize();
14919  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14920  pAllocationInfo->pUserData = hAllocation->GetUserData();
14921  }
14922 }
14923 
14924 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14925 {
14926  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14927  if(hAllocation->CanBecomeLost())
14928  {
14929  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14930  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14931  for(;;)
14932  {
14933  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14934  {
14935  return false;
14936  }
14937  else if(localLastUseFrameIndex == localCurrFrameIndex)
14938  {
14939  return true;
14940  }
14941  else // Last use time earlier than current time.
14942  {
14943  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14944  {
14945  localLastUseFrameIndex = localCurrFrameIndex;
14946  }
14947  }
14948  }
14949  }
14950  else
14951  {
14952 #if VMA_STATS_STRING_ENABLED
14953  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14954  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14955  for(;;)
14956  {
14957  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14958  if(localLastUseFrameIndex == localCurrFrameIndex)
14959  {
14960  break;
14961  }
14962  else // Last use time earlier than current time.
14963  {
14964  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14965  {
14966  localLastUseFrameIndex = localCurrFrameIndex;
14967  }
14968  }
14969  }
14970 #endif
14971 
14972  return true;
14973  }
14974 }
14975 
14976 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14977 {
14978  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
14979 
14980  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
14981 
14982  if(newCreateInfo.maxBlockCount == 0)
14983  {
14984  newCreateInfo.maxBlockCount = SIZE_MAX;
14985  }
14986  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
14987  {
14988  return VK_ERROR_INITIALIZATION_FAILED;
14989  }
14990 
14991  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
14992 
14993  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
14994 
14995  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
14996  if(res != VK_SUCCESS)
14997  {
14998  vma_delete(this, *pPool);
14999  *pPool = VMA_NULL;
15000  return res;
15001  }
15002 
15003  // Add to m_Pools.
15004  {
15005  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15006  (*pPool)->SetId(m_NextPoolId++);
15007  VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15008  }
15009 
15010  return VK_SUCCESS;
15011 }
15012 
15013 void VmaAllocator_T::DestroyPool(VmaPool pool)
15014 {
15015  // Remove from m_Pools.
15016  {
15017  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15018  bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15019  VMA_ASSERT(success && "Pool not found in Allocator.");
15020  }
15021 
15022  vma_delete(this, pool);
15023 }
15024 
15025 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15026 {
15027  pool->m_BlockVector.GetPoolStats(pPoolStats);
15028 }
15029 
15030 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15031 {
15032  m_CurrentFrameIndex.store(frameIndex);
15033 }
15034 
15035 void VmaAllocator_T::MakePoolAllocationsLost(
15036  VmaPool hPool,
15037  size_t* pLostAllocationCount)
15038 {
15039  hPool->m_BlockVector.MakePoolAllocationsLost(
15040  m_CurrentFrameIndex.load(),
15041  pLostAllocationCount);
15042 }
15043 
15044 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15045 {
15046  return hPool->m_BlockVector.CheckCorruption();
15047 }
15048 
15049 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15050 {
15051  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15052 
15053  // Process default pools.
15054  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15055  {
15056  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15057  {
15058  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15059  VMA_ASSERT(pBlockVector);
15060  VkResult localRes = pBlockVector->CheckCorruption();
15061  switch(localRes)
15062  {
15063  case VK_ERROR_FEATURE_NOT_PRESENT:
15064  break;
15065  case VK_SUCCESS:
15066  finalRes = VK_SUCCESS;
15067  break;
15068  default:
15069  return localRes;
15070  }
15071  }
15072  }
15073 
15074  // Process custom pools.
15075  {
15076  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15077  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15078  {
15079  if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15080  {
15081  VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15082  switch(localRes)
15083  {
15084  case VK_ERROR_FEATURE_NOT_PRESENT:
15085  break;
15086  case VK_SUCCESS:
15087  finalRes = VK_SUCCESS;
15088  break;
15089  default:
15090  return localRes;
15091  }
15092  }
15093  }
15094  }
15095 
15096  return finalRes;
15097 }
15098 
15099 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15100 {
15101  *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15102  (*pAllocation)->InitLost();
15103 }
15104 
15105 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15106 {
15107  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15108 
15109  VkResult res;
15110  if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15111  {
15112  VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15113  if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15114  {
15115  res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15116  if(res == VK_SUCCESS)
15117  {
15118  m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15119  }
15120  }
15121  else
15122  {
15123  res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15124  }
15125  }
15126  else
15127  {
15128  res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15129  }
15130 
15131  if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15132  {
15133  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15134  }
15135 
15136  return res;
15137 }
15138 
15139 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15140 {
15141  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15142  {
15143  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15144  }
15145 
15146  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15147 
15148  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15149  if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15150  {
15151  VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15152  m_HeapSizeLimit[heapIndex] += size;
15153  }
15154 }
15155 
15156 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15157 {
15158  if(hAllocation->CanBecomeLost())
15159  {
15160  return VK_ERROR_MEMORY_MAP_FAILED;
15161  }
15162 
15163  switch(hAllocation->GetType())
15164  {
15165  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15166  {
15167  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15168  char *pBytes = VMA_NULL;
15169  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15170  if(res == VK_SUCCESS)
15171  {
15172  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15173  hAllocation->BlockAllocMap();
15174  }
15175  return res;
15176  }
15177  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15178  return hAllocation->DedicatedAllocMap(this, ppData);
15179  default:
15180  VMA_ASSERT(0);
15181  return VK_ERROR_MEMORY_MAP_FAILED;
15182  }
15183 }
15184 
15185 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15186 {
15187  switch(hAllocation->GetType())
15188  {
15189  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15190  {
15191  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15192  hAllocation->BlockAllocUnmap();
15193  pBlock->Unmap(this, 1);
15194  }
15195  break;
15196  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15197  hAllocation->DedicatedAllocUnmap(this);
15198  break;
15199  default:
15200  VMA_ASSERT(0);
15201  }
15202 }
15203 
15204 VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15205 {
15206  VkResult res = VK_SUCCESS;
15207  switch(hAllocation->GetType())
15208  {
15209  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15210  res = GetVulkanFunctions().vkBindBufferMemory(
15211  m_hDevice,
15212  hBuffer,
15213  hAllocation->GetMemory(),
15214  0); //memoryOffset
15215  break;
15216  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15217  {
15218  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15219  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15220  res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15221  break;
15222  }
15223  default:
15224  VMA_ASSERT(0);
15225  }
15226  return res;
15227 }
15228 
15229 VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15230 {
15231  VkResult res = VK_SUCCESS;
15232  switch(hAllocation->GetType())
15233  {
15234  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15235  res = GetVulkanFunctions().vkBindImageMemory(
15236  m_hDevice,
15237  hImage,
15238  hAllocation->GetMemory(),
15239  0); //memoryOffset
15240  break;
15241  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15242  {
15243  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15244  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15245  res = pBlock->BindImageMemory(this, hAllocation, hImage);
15246  break;
15247  }
15248  default:
15249  VMA_ASSERT(0);
15250  }
15251  return res;
15252 }
15253 
15254 void VmaAllocator_T::FlushOrInvalidateAllocation(
15255  VmaAllocation hAllocation,
15256  VkDeviceSize offset, VkDeviceSize size,
15257  VMA_CACHE_OPERATION op)
15258 {
15259  const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15260  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15261  {
15262  const VkDeviceSize allocationSize = hAllocation->GetSize();
15263  VMA_ASSERT(offset <= allocationSize);
15264 
15265  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15266 
15267  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15268  memRange.memory = hAllocation->GetMemory();
15269 
15270  switch(hAllocation->GetType())
15271  {
15272  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15273  memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15274  if(size == VK_WHOLE_SIZE)
15275  {
15276  memRange.size = allocationSize - memRange.offset;
15277  }
15278  else
15279  {
15280  VMA_ASSERT(offset + size <= allocationSize);
15281  memRange.size = VMA_MIN(
15282  VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15283  allocationSize - memRange.offset);
15284  }
15285  break;
15286 
15287  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15288  {
15289  // 1. Still within this allocation.
15290  memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15291  if(size == VK_WHOLE_SIZE)
15292  {
15293  size = allocationSize - offset;
15294  }
15295  else
15296  {
15297  VMA_ASSERT(offset + size <= allocationSize);
15298  }
15299  memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15300 
15301  // 2. Adjust to whole block.
15302  const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15303  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15304  const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15305  memRange.offset += allocationOffset;
15306  memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15307 
15308  break;
15309  }
15310 
15311  default:
15312  VMA_ASSERT(0);
15313  }
15314 
15315  switch(op)
15316  {
15317  case VMA_CACHE_FLUSH:
15318  (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15319  break;
15320  case VMA_CACHE_INVALIDATE:
15321  (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15322  break;
15323  default:
15324  VMA_ASSERT(0);
15325  }
15326  }
15327  // else: Just ignore this call.
15328 }
15329 
15330 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15331 {
15332  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15333 
15334  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15335  {
15336  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15337  AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15338  VMA_ASSERT(pDedicatedAllocations);
15339  bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15340  VMA_ASSERT(success);
15341  }
15342 
15343  VkDeviceMemory hMemory = allocation->GetMemory();
15344 
15345  /*
15346  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15347  before vkFreeMemory.
15348 
15349  if(allocation->GetMappedData() != VMA_NULL)
15350  {
15351  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15352  }
15353  */
15354 
15355  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15356 
15357  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15358 }
15359 
15360 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15361 {
15362  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15363  !hAllocation->CanBecomeLost() &&
15364  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15365  {
15366  void* pData = VMA_NULL;
15367  VkResult res = Map(hAllocation, &pData);
15368  if(res == VK_SUCCESS)
15369  {
15370  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15371  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15372  Unmap(hAllocation);
15373  }
15374  else
15375  {
15376  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15377  }
15378  }
15379 }
15380 
15381 #if VMA_STATS_STRING_ENABLED
15382 
15383 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15384 {
15385  bool dedicatedAllocationsStarted = false;
15386  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15387  {
15388  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15389  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15390  VMA_ASSERT(pDedicatedAllocVector);
15391  if(pDedicatedAllocVector->empty() == false)
15392  {
15393  if(dedicatedAllocationsStarted == false)
15394  {
15395  dedicatedAllocationsStarted = true;
15396  json.WriteString("DedicatedAllocations");
15397  json.BeginObject();
15398  }
15399 
15400  json.BeginString("Type ");
15401  json.ContinueString(memTypeIndex);
15402  json.EndString();
15403 
15404  json.BeginArray();
15405 
15406  for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15407  {
15408  json.BeginObject(true);
15409  const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15410  hAlloc->PrintParameters(json);
15411  json.EndObject();
15412  }
15413 
15414  json.EndArray();
15415  }
15416  }
15417  if(dedicatedAllocationsStarted)
15418  {
15419  json.EndObject();
15420  }
15421 
15422  {
15423  bool allocationsStarted = false;
15424  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15425  {
15426  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15427  {
15428  if(allocationsStarted == false)
15429  {
15430  allocationsStarted = true;
15431  json.WriteString("DefaultPools");
15432  json.BeginObject();
15433  }
15434 
15435  json.BeginString("Type ");
15436  json.ContinueString(memTypeIndex);
15437  json.EndString();
15438 
15439  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15440  }
15441  }
15442  if(allocationsStarted)
15443  {
15444  json.EndObject();
15445  }
15446  }
15447 
15448  // Custom pools
15449  {
15450  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15451  const size_t poolCount = m_Pools.size();
15452  if(poolCount > 0)
15453  {
15454  json.WriteString("Pools");
15455  json.BeginObject();
15456  for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15457  {
15458  json.BeginString();
15459  json.ContinueString(m_Pools[poolIndex]->GetId());
15460  json.EndString();
15461 
15462  m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15463  }
15464  json.EndObject();
15465  }
15466  }
15467 }
15468 
15469 #endif // #if VMA_STATS_STRING_ENABLED
15470 
15472 // Public interface
15473 
15474 VkResult vmaCreateAllocator(
15475  const VmaAllocatorCreateInfo* pCreateInfo,
15476  VmaAllocator* pAllocator)
15477 {
15478  VMA_ASSERT(pCreateInfo && pAllocator);
15479  VMA_DEBUG_LOG("vmaCreateAllocator");
15480  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15481  return (*pAllocator)->Init(pCreateInfo);
15482 }
15483 
15484 void vmaDestroyAllocator(
15485  VmaAllocator allocator)
15486 {
15487  if(allocator != VK_NULL_HANDLE)
15488  {
15489  VMA_DEBUG_LOG("vmaDestroyAllocator");
15490  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15491  vma_delete(&allocationCallbacks, allocator);
15492  }
15493 }
15494 
15496  VmaAllocator allocator,
15497  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15498 {
15499  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15500  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15501 }
15502 
15504  VmaAllocator allocator,
15505  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15506 {
15507  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15508  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15509 }
15510 
15512  VmaAllocator allocator,
15513  uint32_t memoryTypeIndex,
15514  VkMemoryPropertyFlags* pFlags)
15515 {
15516  VMA_ASSERT(allocator && pFlags);
15517  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15518  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15519 }
15520 
15522  VmaAllocator allocator,
15523  uint32_t frameIndex)
15524 {
15525  VMA_ASSERT(allocator);
15526  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15527 
15528  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15529 
15530  allocator->SetCurrentFrameIndex(frameIndex);
15531 }
15532 
15533 void vmaCalculateStats(
15534  VmaAllocator allocator,
15535  VmaStats* pStats)
15536 {
15537  VMA_ASSERT(allocator && pStats);
15538  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15539  allocator->CalculateStats(pStats);
15540 }
15541 
15542 #if VMA_STATS_STRING_ENABLED
15543 
15544 void vmaBuildStatsString(
15545  VmaAllocator allocator,
15546  char** ppStatsString,
15547  VkBool32 detailedMap)
15548 {
15549  VMA_ASSERT(allocator && ppStatsString);
15550  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15551 
15552  VmaStringBuilder sb(allocator);
15553  {
15554  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15555  json.BeginObject();
15556 
15557  VmaStats stats;
15558  allocator->CalculateStats(&stats);
15559 
15560  json.WriteString("Total");
15561  VmaPrintStatInfo(json, stats.total);
15562 
15563  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15564  {
15565  json.BeginString("Heap ");
15566  json.ContinueString(heapIndex);
15567  json.EndString();
15568  json.BeginObject();
15569 
15570  json.WriteString("Size");
15571  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15572 
15573  json.WriteString("Flags");
15574  json.BeginArray(true);
15575  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15576  {
15577  json.WriteString("DEVICE_LOCAL");
15578  }
15579  json.EndArray();
15580 
15581  if(stats.memoryHeap[heapIndex].blockCount > 0)
15582  {
15583  json.WriteString("Stats");
15584  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15585  }
15586 
15587  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15588  {
15589  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15590  {
15591  json.BeginString("Type ");
15592  json.ContinueString(typeIndex);
15593  json.EndString();
15594 
15595  json.BeginObject();
15596 
15597  json.WriteString("Flags");
15598  json.BeginArray(true);
15599  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15600  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15601  {
15602  json.WriteString("DEVICE_LOCAL");
15603  }
15604  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15605  {
15606  json.WriteString("HOST_VISIBLE");
15607  }
15608  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15609  {
15610  json.WriteString("HOST_COHERENT");
15611  }
15612  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15613  {
15614  json.WriteString("HOST_CACHED");
15615  }
15616  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15617  {
15618  json.WriteString("LAZILY_ALLOCATED");
15619  }
15620  json.EndArray();
15621 
15622  if(stats.memoryType[typeIndex].blockCount > 0)
15623  {
15624  json.WriteString("Stats");
15625  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15626  }
15627 
15628  json.EndObject();
15629  }
15630  }
15631 
15632  json.EndObject();
15633  }
15634  if(detailedMap == VK_TRUE)
15635  {
15636  allocator->PrintDetailedMap(json);
15637  }
15638 
15639  json.EndObject();
15640  }
15641 
15642  const size_t len = sb.GetLength();
15643  char* const pChars = vma_new_array(allocator, char, len + 1);
15644  if(len > 0)
15645  {
15646  memcpy(pChars, sb.GetData(), len);
15647  }
15648  pChars[len] = '\0';
15649  *ppStatsString = pChars;
15650 }
15651 
15652 void vmaFreeStatsString(
15653  VmaAllocator allocator,
15654  char* pStatsString)
15655 {
15656  if(pStatsString != VMA_NULL)
15657  {
15658  VMA_ASSERT(allocator);
15659  size_t len = strlen(pStatsString);
15660  vma_delete_array(allocator, pStatsString, len + 1);
15661  }
15662 }
15663 
15664 #endif // #if VMA_STATS_STRING_ENABLED
15665 
15666 /*
15667 This function is not protected by any mutex because it just reads immutable data.
15668 */
15669 VkResult vmaFindMemoryTypeIndex(
15670  VmaAllocator allocator,
15671  uint32_t memoryTypeBits,
15672  const VmaAllocationCreateInfo* pAllocationCreateInfo,
15673  uint32_t* pMemoryTypeIndex)
15674 {
15675  VMA_ASSERT(allocator != VK_NULL_HANDLE);
15676  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15677  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15678 
15679  if(pAllocationCreateInfo->memoryTypeBits != 0)
15680  {
15681  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15682  }
15683 
15684  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15685  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15686 
15687  const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15688  if(mapped)
15689  {
15690  preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15691  }
15692 
15693  // Convert usage to requiredFlags and preferredFlags.
15694  switch(pAllocationCreateInfo->usage)
15695  {
15697  break;
15699  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15700  {
15701  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15702  }
15703  break;
15705  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15706  break;
15708  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15709  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15710  {
15711  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15712  }
15713  break;
15715  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15716  preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15717  break;
15718  default:
15719  break;
15720  }
15721 
15722  *pMemoryTypeIndex = UINT32_MAX;
15723  uint32_t minCost = UINT32_MAX;
15724  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15725  memTypeIndex < allocator->GetMemoryTypeCount();
15726  ++memTypeIndex, memTypeBit <<= 1)
15727  {
15728  // This memory type is acceptable according to memoryTypeBits bitmask.
15729  if((memTypeBit & memoryTypeBits) != 0)
15730  {
15731  const VkMemoryPropertyFlags currFlags =
15732  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15733  // This memory type contains requiredFlags.
15734  if((requiredFlags & ~currFlags) == 0)
15735  {
15736  // Calculate cost as number of bits from preferredFlags not present in this memory type.
15737  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15738  // Remember memory type with lowest cost.
15739  if(currCost < minCost)
15740  {
15741  *pMemoryTypeIndex = memTypeIndex;
15742  if(currCost == 0)
15743  {
15744  return VK_SUCCESS;
15745  }
15746  minCost = currCost;
15747  }
15748  }
15749  }
15750  }
15751  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15752 }
15753 
15755  VmaAllocator allocator,
15756  const VkBufferCreateInfo* pBufferCreateInfo,
15757  const VmaAllocationCreateInfo* pAllocationCreateInfo,
15758  uint32_t* pMemoryTypeIndex)
15759 {
15760  VMA_ASSERT(allocator != VK_NULL_HANDLE);
15761  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15762  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15763  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15764 
15765  const VkDevice hDev = allocator->m_hDevice;
15766  VkBuffer hBuffer = VK_NULL_HANDLE;
15767  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15768  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15769  if(res == VK_SUCCESS)
15770  {
15771  VkMemoryRequirements memReq = {};
15772  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15773  hDev, hBuffer, &memReq);
15774 
15775  res = vmaFindMemoryTypeIndex(
15776  allocator,
15777  memReq.memoryTypeBits,
15778  pAllocationCreateInfo,
15779  pMemoryTypeIndex);
15780 
15781  allocator->GetVulkanFunctions().vkDestroyBuffer(
15782  hDev, hBuffer, allocator->GetAllocationCallbacks());
15783  }
15784  return res;
15785 }
15786 
15788  VmaAllocator allocator,
15789  const VkImageCreateInfo* pImageCreateInfo,
15790  const VmaAllocationCreateInfo* pAllocationCreateInfo,
15791  uint32_t* pMemoryTypeIndex)
15792 {
15793  VMA_ASSERT(allocator != VK_NULL_HANDLE);
15794  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15795  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15796  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15797 
15798  const VkDevice hDev = allocator->m_hDevice;
15799  VkImage hImage = VK_NULL_HANDLE;
15800  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15801  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15802  if(res == VK_SUCCESS)
15803  {
15804  VkMemoryRequirements memReq = {};
15805  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15806  hDev, hImage, &memReq);
15807 
15808  res = vmaFindMemoryTypeIndex(
15809  allocator,
15810  memReq.memoryTypeBits,
15811  pAllocationCreateInfo,
15812  pMemoryTypeIndex);
15813 
15814  allocator->GetVulkanFunctions().vkDestroyImage(
15815  hDev, hImage, allocator->GetAllocationCallbacks());
15816  }
15817  return res;
15818 }
15819 
15820 VkResult vmaCreatePool(
15821  VmaAllocator allocator,
15822  const VmaPoolCreateInfo* pCreateInfo,
15823  VmaPool* pPool)
15824 {
15825  VMA_ASSERT(allocator && pCreateInfo && pPool);
15826 
15827  VMA_DEBUG_LOG("vmaCreatePool");
15828 
15829  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15830 
15831  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15832 
15833 #if VMA_RECORDING_ENABLED
15834  if(allocator->GetRecorder() != VMA_NULL)
15835  {
15836  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15837  }
15838 #endif
15839 
15840  return res;
15841 }
15842 
15843 void vmaDestroyPool(
15844  VmaAllocator allocator,
15845  VmaPool pool)
15846 {
15847  VMA_ASSERT(allocator);
15848 
15849  if(pool == VK_NULL_HANDLE)
15850  {
15851  return;
15852  }
15853 
15854  VMA_DEBUG_LOG("vmaDestroyPool");
15855 
15856  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15857 
15858 #if VMA_RECORDING_ENABLED
15859  if(allocator->GetRecorder() != VMA_NULL)
15860  {
15861  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15862  }
15863 #endif
15864 
15865  allocator->DestroyPool(pool);
15866 }
15867 
15868 void vmaGetPoolStats(
15869  VmaAllocator allocator,
15870  VmaPool pool,
15871  VmaPoolStats* pPoolStats)
15872 {
15873  VMA_ASSERT(allocator && pool && pPoolStats);
15874 
15875  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15876 
15877  allocator->GetPoolStats(pool, pPoolStats);
15878 }
15879 
15881  VmaAllocator allocator,
15882  VmaPool pool,
15883  size_t* pLostAllocationCount)
15884 {
15885  VMA_ASSERT(allocator && pool);
15886 
15887  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15888 
15889 #if VMA_RECORDING_ENABLED
15890  if(allocator->GetRecorder() != VMA_NULL)
15891  {
15892  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15893  }
15894 #endif
15895 
15896  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15897 }
15898 
15899 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15900 {
15901  VMA_ASSERT(allocator && pool);
15902 
15903  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15904 
15905  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15906 
15907  return allocator->CheckPoolCorruption(pool);
15908 }
15909 
15910 VkResult vmaAllocateMemory(
15911  VmaAllocator allocator,
15912  const VkMemoryRequirements* pVkMemoryRequirements,
15913  const VmaAllocationCreateInfo* pCreateInfo,
15914  VmaAllocation* pAllocation,
15915  VmaAllocationInfo* pAllocationInfo)
15916 {
15917  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15918 
15919  VMA_DEBUG_LOG("vmaAllocateMemory");
15920 
15921  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15922 
15923  VkResult result = allocator->AllocateMemory(
15924  *pVkMemoryRequirements,
15925  false, // requiresDedicatedAllocation
15926  false, // prefersDedicatedAllocation
15927  VK_NULL_HANDLE, // dedicatedBuffer
15928  VK_NULL_HANDLE, // dedicatedImage
15929  *pCreateInfo,
15930  VMA_SUBALLOCATION_TYPE_UNKNOWN,
15931  1, // allocationCount
15932  pAllocation);
15933 
15934 #if VMA_RECORDING_ENABLED
15935  if(allocator->GetRecorder() != VMA_NULL)
15936  {
15937  allocator->GetRecorder()->RecordAllocateMemory(
15938  allocator->GetCurrentFrameIndex(),
15939  *pVkMemoryRequirements,
15940  *pCreateInfo,
15941  *pAllocation);
15942  }
15943 #endif
15944 
15945  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15946  {
15947  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15948  }
15949 
15950  return result;
15951 }
15952 
15953 VkResult vmaAllocateMemoryPages(
15954  VmaAllocator allocator,
15955  const VkMemoryRequirements* pVkMemoryRequirements,
15956  const VmaAllocationCreateInfo* pCreateInfo,
15957  size_t allocationCount,
15958  VmaAllocation* pAllocations,
15959  VmaAllocationInfo* pAllocationInfo)
15960 {
15961  if(allocationCount == 0)
15962  {
15963  return VK_SUCCESS;
15964  }
15965 
15966  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15967 
15968  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15969 
15970  VMA_DEBUG_GLOBAL_MUTEX_LOCK
15971 
15972  VkResult result = allocator->AllocateMemory(
15973  *pVkMemoryRequirements,
15974  false, // requiresDedicatedAllocation
15975  false, // prefersDedicatedAllocation
15976  VK_NULL_HANDLE, // dedicatedBuffer
15977  VK_NULL_HANDLE, // dedicatedImage
15978  *pCreateInfo,
15979  VMA_SUBALLOCATION_TYPE_UNKNOWN,
15980  allocationCount,
15981  pAllocations);
15982 
15983 #if VMA_RECORDING_ENABLED
15984  if(allocator->GetRecorder() != VMA_NULL)
15985  {
15986  allocator->GetRecorder()->RecordAllocateMemoryPages(
15987  allocator->GetCurrentFrameIndex(),
15988  *pVkMemoryRequirements,
15989  *pCreateInfo,
15990  (uint64_t)allocationCount,
15991  pAllocations);
15992  }
15993 #endif
15994 
15995  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15996  {
15997  for(size_t i = 0; i < allocationCount; ++i)
15998  {
15999  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16000  }
16001  }
16002 
16003  return result;
16004 }
16005 
16007  VmaAllocator allocator,
16008  VkBuffer buffer,
16009  const VmaAllocationCreateInfo* pCreateInfo,
16010  VmaAllocation* pAllocation,
16011  VmaAllocationInfo* pAllocationInfo)
16012 {
16013  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16014 
16015  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16016 
16017  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16018 
16019  VkMemoryRequirements vkMemReq = {};
16020  bool requiresDedicatedAllocation = false;
16021  bool prefersDedicatedAllocation = false;
16022  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16023  requiresDedicatedAllocation,
16024  prefersDedicatedAllocation);
16025 
16026  VkResult result = allocator->AllocateMemory(
16027  vkMemReq,
16028  requiresDedicatedAllocation,
16029  prefersDedicatedAllocation,
16030  buffer, // dedicatedBuffer
16031  VK_NULL_HANDLE, // dedicatedImage
16032  *pCreateInfo,
16033  VMA_SUBALLOCATION_TYPE_BUFFER,
16034  1, // allocationCount
16035  pAllocation);
16036 
16037 #if VMA_RECORDING_ENABLED
16038  if(allocator->GetRecorder() != VMA_NULL)
16039  {
16040  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16041  allocator->GetCurrentFrameIndex(),
16042  vkMemReq,
16043  requiresDedicatedAllocation,
16044  prefersDedicatedAllocation,
16045  *pCreateInfo,
16046  *pAllocation);
16047  }
16048 #endif
16049 
16050  if(pAllocationInfo && result == VK_SUCCESS)
16051  {
16052  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16053  }
16054 
16055  return result;
16056 }
16057 
16058 VkResult vmaAllocateMemoryForImage(
16059  VmaAllocator allocator,
16060  VkImage image,
16061  const VmaAllocationCreateInfo* pCreateInfo,
16062  VmaAllocation* pAllocation,
16063  VmaAllocationInfo* pAllocationInfo)
16064 {
16065  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16066 
16067  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16068 
16069  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16070 
16071  VkMemoryRequirements vkMemReq = {};
16072  bool requiresDedicatedAllocation = false;
16073  bool prefersDedicatedAllocation = false;
16074  allocator->GetImageMemoryRequirements(image, vkMemReq,
16075  requiresDedicatedAllocation, prefersDedicatedAllocation);
16076 
16077  VkResult result = allocator->AllocateMemory(
16078  vkMemReq,
16079  requiresDedicatedAllocation,
16080  prefersDedicatedAllocation,
16081  VK_NULL_HANDLE, // dedicatedBuffer
16082  image, // dedicatedImage
16083  *pCreateInfo,
16084  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16085  1, // allocationCount
16086  pAllocation);
16087 
16088 #if VMA_RECORDING_ENABLED
16089  if(allocator->GetRecorder() != VMA_NULL)
16090  {
16091  allocator->GetRecorder()->RecordAllocateMemoryForImage(
16092  allocator->GetCurrentFrameIndex(),
16093  vkMemReq,
16094  requiresDedicatedAllocation,
16095  prefersDedicatedAllocation,
16096  *pCreateInfo,
16097  *pAllocation);
16098  }
16099 #endif
16100 
16101  if(pAllocationInfo && result == VK_SUCCESS)
16102  {
16103  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16104  }
16105 
16106  return result;
16107 }
16108 
16109 void vmaFreeMemory(
16110  VmaAllocator allocator,
16111  VmaAllocation allocation)
16112 {
16113  VMA_ASSERT(allocator);
16114 
16115  if(allocation == VK_NULL_HANDLE)
16116  {
16117  return;
16118  }
16119 
16120  VMA_DEBUG_LOG("vmaFreeMemory");
16121 
16122  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16123 
16124 #if VMA_RECORDING_ENABLED
16125  if(allocator->GetRecorder() != VMA_NULL)
16126  {
16127  allocator->GetRecorder()->RecordFreeMemory(
16128  allocator->GetCurrentFrameIndex(),
16129  allocation);
16130  }
16131 #endif
16132 
16133  allocator->FreeMemory(
16134  1, // allocationCount
16135  &allocation);
16136 }
16137 
16138 void vmaFreeMemoryPages(
16139  VmaAllocator allocator,
16140  size_t allocationCount,
16141  VmaAllocation* pAllocations)
16142 {
16143  if(allocationCount == 0)
16144  {
16145  return;
16146  }
16147 
16148  VMA_ASSERT(allocator);
16149 
16150  VMA_DEBUG_LOG("vmaFreeMemoryPages");
16151 
16152  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16153 
16154 #if VMA_RECORDING_ENABLED
16155  if(allocator->GetRecorder() != VMA_NULL)
16156  {
16157  allocator->GetRecorder()->RecordFreeMemoryPages(
16158  allocator->GetCurrentFrameIndex(),
16159  (uint64_t)allocationCount,
16160  pAllocations);
16161  }
16162 #endif
16163 
16164  allocator->FreeMemory(allocationCount, pAllocations);
16165 }
16166 
16167 VkResult vmaResizeAllocation(
16168  VmaAllocator allocator,
16169  VmaAllocation allocation,
16170  VkDeviceSize newSize)
16171 {
16172  VMA_ASSERT(allocator && allocation);
16173 
16174  VMA_DEBUG_LOG("vmaResizeAllocation");
16175 
16176  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16177 
16178 #if VMA_RECORDING_ENABLED
16179  if(allocator->GetRecorder() != VMA_NULL)
16180  {
16181  allocator->GetRecorder()->RecordResizeAllocation(
16182  allocator->GetCurrentFrameIndex(),
16183  allocation,
16184  newSize);
16185  }
16186 #endif
16187 
16188  return allocator->ResizeAllocation(allocation, newSize);
16189 }
16190 
16192  VmaAllocator allocator,
16193  VmaAllocation allocation,
16194  VmaAllocationInfo* pAllocationInfo)
16195 {
16196  VMA_ASSERT(allocator && allocation && pAllocationInfo);
16197 
16198  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16199 
16200 #if VMA_RECORDING_ENABLED
16201  if(allocator->GetRecorder() != VMA_NULL)
16202  {
16203  allocator->GetRecorder()->RecordGetAllocationInfo(
16204  allocator->GetCurrentFrameIndex(),
16205  allocation);
16206  }
16207 #endif
16208 
16209  allocator->GetAllocationInfo(allocation, pAllocationInfo);
16210 }
16211 
16212 VkBool32 vmaTouchAllocation(
16213  VmaAllocator allocator,
16214  VmaAllocation allocation)
16215 {
16216  VMA_ASSERT(allocator && allocation);
16217 
16218  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16219 
16220 #if VMA_RECORDING_ENABLED
16221  if(allocator->GetRecorder() != VMA_NULL)
16222  {
16223  allocator->GetRecorder()->RecordTouchAllocation(
16224  allocator->GetCurrentFrameIndex(),
16225  allocation);
16226  }
16227 #endif
16228 
16229  return allocator->TouchAllocation(allocation);
16230 }
16231 
16233  VmaAllocator allocator,
16234  VmaAllocation allocation,
16235  void* pUserData)
16236 {
16237  VMA_ASSERT(allocator && allocation);
16238 
16239  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16240 
16241  allocation->SetUserData(allocator, pUserData);
16242 
16243 #if VMA_RECORDING_ENABLED
16244  if(allocator->GetRecorder() != VMA_NULL)
16245  {
16246  allocator->GetRecorder()->RecordSetAllocationUserData(
16247  allocator->GetCurrentFrameIndex(),
16248  allocation,
16249  pUserData);
16250  }
16251 #endif
16252 }
16253 
16255  VmaAllocator allocator,
16256  VmaAllocation* pAllocation)
16257 {
16258  VMA_ASSERT(allocator && pAllocation);
16259 
16260  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16261 
16262  allocator->CreateLostAllocation(pAllocation);
16263 
16264 #if VMA_RECORDING_ENABLED
16265  if(allocator->GetRecorder() != VMA_NULL)
16266  {
16267  allocator->GetRecorder()->RecordCreateLostAllocation(
16268  allocator->GetCurrentFrameIndex(),
16269  *pAllocation);
16270  }
16271 #endif
16272 }
16273 
16274 VkResult vmaMapMemory(
16275  VmaAllocator allocator,
16276  VmaAllocation allocation,
16277  void** ppData)
16278 {
16279  VMA_ASSERT(allocator && allocation && ppData);
16280 
16281  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16282 
16283  VkResult res = allocator->Map(allocation, ppData);
16284 
16285 #if VMA_RECORDING_ENABLED
16286  if(allocator->GetRecorder() != VMA_NULL)
16287  {
16288  allocator->GetRecorder()->RecordMapMemory(
16289  allocator->GetCurrentFrameIndex(),
16290  allocation);
16291  }
16292 #endif
16293 
16294  return res;
16295 }
16296 
16297 void vmaUnmapMemory(
16298  VmaAllocator allocator,
16299  VmaAllocation allocation)
16300 {
16301  VMA_ASSERT(allocator && allocation);
16302 
16303  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16304 
16305 #if VMA_RECORDING_ENABLED
16306  if(allocator->GetRecorder() != VMA_NULL)
16307  {
16308  allocator->GetRecorder()->RecordUnmapMemory(
16309  allocator->GetCurrentFrameIndex(),
16310  allocation);
16311  }
16312 #endif
16313 
16314  allocator->Unmap(allocation);
16315 }
16316 
16317 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16318 {
16319  VMA_ASSERT(allocator && allocation);
16320 
16321  VMA_DEBUG_LOG("vmaFlushAllocation");
16322 
16323  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16324 
16325  allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16326 
16327 #if VMA_RECORDING_ENABLED
16328  if(allocator->GetRecorder() != VMA_NULL)
16329  {
16330  allocator->GetRecorder()->RecordFlushAllocation(
16331  allocator->GetCurrentFrameIndex(),
16332  allocation, offset, size);
16333  }
16334 #endif
16335 }
16336 
16337 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16338 {
16339  VMA_ASSERT(allocator && allocation);
16340 
16341  VMA_DEBUG_LOG("vmaInvalidateAllocation");
16342 
16343  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16344 
16345  allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16346 
16347 #if VMA_RECORDING_ENABLED
16348  if(allocator->GetRecorder() != VMA_NULL)
16349  {
16350  allocator->GetRecorder()->RecordInvalidateAllocation(
16351  allocator->GetCurrentFrameIndex(),
16352  allocation, offset, size);
16353  }
16354 #endif
16355 }
16356 
16357 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16358 {
16359  VMA_ASSERT(allocator);
16360 
16361  VMA_DEBUG_LOG("vmaCheckCorruption");
16362 
16363  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16364 
16365  return allocator->CheckCorruption(memoryTypeBits);
16366 }
16367 
16368 VkResult vmaDefragment(
16369  VmaAllocator allocator,
16370  VmaAllocation* pAllocations,
16371  size_t allocationCount,
16372  VkBool32* pAllocationsChanged,
16373  const VmaDefragmentationInfo *pDefragmentationInfo,
16374  VmaDefragmentationStats* pDefragmentationStats)
16375 {
16376  // Deprecated interface, reimplemented using new one.
16377 
16378  VmaDefragmentationInfo2 info2 = {};
16379  info2.allocationCount = (uint32_t)allocationCount;
16380  info2.pAllocations = pAllocations;
16381  info2.pAllocationsChanged = pAllocationsChanged;
16382  if(pDefragmentationInfo != VMA_NULL)
16383  {
16384  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16385  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16386  }
16387  else
16388  {
16389  info2.maxCpuAllocationsToMove = UINT32_MAX;
16390  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16391  }
16392  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16393 
16395  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16396  if(res == VK_NOT_READY)
16397  {
16398  res = vmaDefragmentationEnd( allocator, ctx);
16399  }
16400  return res;
16401 }
16402 
16403 VkResult vmaDefragmentationBegin(
16404  VmaAllocator allocator,
16405  const VmaDefragmentationInfo2* pInfo,
16406  VmaDefragmentationStats* pStats,
16407  VmaDefragmentationContext *pContext)
16408 {
16409  VMA_ASSERT(allocator && pInfo && pContext);
16410 
16411  // Degenerate case: Nothing to defragment.
16412  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16413  {
16414  return VK_SUCCESS;
16415  }
16416 
16417  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16418  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16419  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16420  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16421 
16422  VMA_DEBUG_LOG("vmaDefragmentationBegin");
16423 
16424  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16425 
16426  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16427 
16428 #if VMA_RECORDING_ENABLED
16429  if(allocator->GetRecorder() != VMA_NULL)
16430  {
16431  allocator->GetRecorder()->RecordDefragmentationBegin(
16432  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16433  }
16434 #endif
16435 
16436  return res;
16437 }
16438 
16439 VkResult vmaDefragmentationEnd(
16440  VmaAllocator allocator,
16441  VmaDefragmentationContext context)
16442 {
16443  VMA_ASSERT(allocator);
16444 
16445  VMA_DEBUG_LOG("vmaDefragmentationEnd");
16446 
16447  if(context != VK_NULL_HANDLE)
16448  {
16449  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16450 
16451 #if VMA_RECORDING_ENABLED
16452  if(allocator->GetRecorder() != VMA_NULL)
16453  {
16454  allocator->GetRecorder()->RecordDefragmentationEnd(
16455  allocator->GetCurrentFrameIndex(), context);
16456  }
16457 #endif
16458 
16459  return allocator->DefragmentationEnd(context);
16460  }
16461  else
16462  {
16463  return VK_SUCCESS;
16464  }
16465 }
16466 
16467 VkResult vmaBindBufferMemory(
16468  VmaAllocator allocator,
16469  VmaAllocation allocation,
16470  VkBuffer buffer)
16471 {
16472  VMA_ASSERT(allocator && allocation && buffer);
16473 
16474  VMA_DEBUG_LOG("vmaBindBufferMemory");
16475 
16476  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16477 
16478  return allocator->BindBufferMemory(allocation, buffer);
16479 }
16480 
16481 VkResult vmaBindImageMemory(
16482  VmaAllocator allocator,
16483  VmaAllocation allocation,
16484  VkImage image)
16485 {
16486  VMA_ASSERT(allocator && allocation && image);
16487 
16488  VMA_DEBUG_LOG("vmaBindImageMemory");
16489 
16490  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16491 
16492  return allocator->BindImageMemory(allocation, image);
16493 }
16494 
16495 VkResult vmaCreateBuffer(
16496  VmaAllocator allocator,
16497  const VkBufferCreateInfo* pBufferCreateInfo,
16498  const VmaAllocationCreateInfo* pAllocationCreateInfo,
16499  VkBuffer* pBuffer,
16500  VmaAllocation* pAllocation,
16501  VmaAllocationInfo* pAllocationInfo)
16502 {
16503  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16504 
16505  if(pBufferCreateInfo->size == 0)
16506  {
16507  return VK_ERROR_VALIDATION_FAILED_EXT;
16508  }
16509 
16510  VMA_DEBUG_LOG("vmaCreateBuffer");
16511 
16512  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16513 
16514  *pBuffer = VK_NULL_HANDLE;
16515  *pAllocation = VK_NULL_HANDLE;
16516 
16517  // 1. Create VkBuffer.
16518  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16519  allocator->m_hDevice,
16520  pBufferCreateInfo,
16521  allocator->GetAllocationCallbacks(),
16522  pBuffer);
16523  if(res >= 0)
16524  {
16525  // 2. vkGetBufferMemoryRequirements.
16526  VkMemoryRequirements vkMemReq = {};
16527  bool requiresDedicatedAllocation = false;
16528  bool prefersDedicatedAllocation = false;
16529  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16530  requiresDedicatedAllocation, prefersDedicatedAllocation);
16531 
16532  // Make sure alignment requirements for specific buffer usages reported
16533  // in Physical Device Properties are included in alignment reported by memory requirements.
16534  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16535  {
16536  VMA_ASSERT(vkMemReq.alignment %
16537  allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16538  }
16539  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16540  {
16541  VMA_ASSERT(vkMemReq.alignment %
16542  allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16543  }
16544  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16545  {
16546  VMA_ASSERT(vkMemReq.alignment %
16547  allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16548  }
16549 
16550  // 3. Allocate memory using allocator.
16551  res = allocator->AllocateMemory(
16552  vkMemReq,
16553  requiresDedicatedAllocation,
16554  prefersDedicatedAllocation,
16555  *pBuffer, // dedicatedBuffer
16556  VK_NULL_HANDLE, // dedicatedImage
16557  *pAllocationCreateInfo,
16558  VMA_SUBALLOCATION_TYPE_BUFFER,
16559  1, // allocationCount
16560  pAllocation);
16561 
16562 #if VMA_RECORDING_ENABLED
16563  if(allocator->GetRecorder() != VMA_NULL)
16564  {
16565  allocator->GetRecorder()->RecordCreateBuffer(
16566  allocator->GetCurrentFrameIndex(),
16567  *pBufferCreateInfo,
16568  *pAllocationCreateInfo,
16569  *pAllocation);
16570  }
16571 #endif
16572 
16573  if(res >= 0)
16574  {
16575  // 3. Bind buffer with memory.
16576  res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16577  if(res >= 0)
16578  {
16579  // All steps succeeded.
16580  #if VMA_STATS_STRING_ENABLED
16581  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16582  #endif
16583  if(pAllocationInfo != VMA_NULL)
16584  {
16585  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16586  }
16587 
16588  return VK_SUCCESS;
16589  }
16590  allocator->FreeMemory(
16591  1, // allocationCount
16592  pAllocation);
16593  *pAllocation = VK_NULL_HANDLE;
16594  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16595  *pBuffer = VK_NULL_HANDLE;
16596  return res;
16597  }
16598  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16599  *pBuffer = VK_NULL_HANDLE;
16600  return res;
16601  }
16602  return res;
16603 }
16604 
16605 void vmaDestroyBuffer(
16606  VmaAllocator allocator,
16607  VkBuffer buffer,
16608  VmaAllocation allocation)
16609 {
16610  VMA_ASSERT(allocator);
16611 
16612  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16613  {
16614  return;
16615  }
16616 
16617  VMA_DEBUG_LOG("vmaDestroyBuffer");
16618 
16619  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16620 
16621 #if VMA_RECORDING_ENABLED
16622  if(allocator->GetRecorder() != VMA_NULL)
16623  {
16624  allocator->GetRecorder()->RecordDestroyBuffer(
16625  allocator->GetCurrentFrameIndex(),
16626  allocation);
16627  }
16628 #endif
16629 
16630  if(buffer != VK_NULL_HANDLE)
16631  {
16632  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16633  }
16634 
16635  if(allocation != VK_NULL_HANDLE)
16636  {
16637  allocator->FreeMemory(
16638  1, // allocationCount
16639  &allocation);
16640  }
16641 }
16642 
16643 VkResult vmaCreateImage(
16644  VmaAllocator allocator,
16645  const VkImageCreateInfo* pImageCreateInfo,
16646  const VmaAllocationCreateInfo* pAllocationCreateInfo,
16647  VkImage* pImage,
16648  VmaAllocation* pAllocation,
16649  VmaAllocationInfo* pAllocationInfo)
16650 {
16651  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16652 
16653  if(pImageCreateInfo->extent.width == 0 ||
16654  pImageCreateInfo->extent.height == 0 ||
16655  pImageCreateInfo->extent.depth == 0 ||
16656  pImageCreateInfo->mipLevels == 0 ||
16657  pImageCreateInfo->arrayLayers == 0)
16658  {
16659  return VK_ERROR_VALIDATION_FAILED_EXT;
16660  }
16661 
16662  VMA_DEBUG_LOG("vmaCreateImage");
16663 
16664  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16665 
16666  *pImage = VK_NULL_HANDLE;
16667  *pAllocation = VK_NULL_HANDLE;
16668 
16669  // 1. Create VkImage.
16670  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16671  allocator->m_hDevice,
16672  pImageCreateInfo,
16673  allocator->GetAllocationCallbacks(),
16674  pImage);
16675  if(res >= 0)
16676  {
16677  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16678  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16679  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16680 
16681  // 2. Allocate memory using allocator.
16682  VkMemoryRequirements vkMemReq = {};
16683  bool requiresDedicatedAllocation = false;
16684  bool prefersDedicatedAllocation = false;
16685  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16686  requiresDedicatedAllocation, prefersDedicatedAllocation);
16687 
16688  res = allocator->AllocateMemory(
16689  vkMemReq,
16690  requiresDedicatedAllocation,
16691  prefersDedicatedAllocation,
16692  VK_NULL_HANDLE, // dedicatedBuffer
16693  *pImage, // dedicatedImage
16694  *pAllocationCreateInfo,
16695  suballocType,
16696  1, // allocationCount
16697  pAllocation);
16698 
16699 #if VMA_RECORDING_ENABLED
16700  if(allocator->GetRecorder() != VMA_NULL)
16701  {
16702  allocator->GetRecorder()->RecordCreateImage(
16703  allocator->GetCurrentFrameIndex(),
16704  *pImageCreateInfo,
16705  *pAllocationCreateInfo,
16706  *pAllocation);
16707  }
16708 #endif
16709 
16710  if(res >= 0)
16711  {
16712  // 3. Bind image with memory.
16713  res = allocator->BindImageMemory(*pAllocation, *pImage);
16714  if(res >= 0)
16715  {
16716  // All steps succeeded.
16717  #if VMA_STATS_STRING_ENABLED
16718  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16719  #endif
16720  if(pAllocationInfo != VMA_NULL)
16721  {
16722  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16723  }
16724 
16725  return VK_SUCCESS;
16726  }
16727  allocator->FreeMemory(
16728  1, // allocationCount
16729  pAllocation);
16730  *pAllocation = VK_NULL_HANDLE;
16731  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16732  *pImage = VK_NULL_HANDLE;
16733  return res;
16734  }
16735  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16736  *pImage = VK_NULL_HANDLE;
16737  return res;
16738  }
16739  return res;
16740 }
16741 
16742 void vmaDestroyImage(
16743  VmaAllocator allocator,
16744  VkImage image,
16745  VmaAllocation allocation)
16746 {
16747  VMA_ASSERT(allocator);
16748 
16749  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16750  {
16751  return;
16752  }
16753 
16754  VMA_DEBUG_LOG("vmaDestroyImage");
16755 
16756  VMA_DEBUG_GLOBAL_MUTEX_LOCK
16757 
16758 #if VMA_RECORDING_ENABLED
16759  if(allocator->GetRecorder() != VMA_NULL)
16760  {
16761  allocator->GetRecorder()->RecordDestroyImage(
16762  allocator->GetCurrentFrameIndex(),
16763  allocation);
16764  }
16765 #endif
16766 
16767  if(image != VK_NULL_HANDLE)
16768  {
16769  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16770  }
16771  if(allocation != VK_NULL_HANDLE)
16772  {
16773  allocator->FreeMemory(
16774  1, // allocationCount
16775  &allocation);
16776  }
16777 }
16778 
16779 #endif // #ifdef VMA_IMPLEMENTATION
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties
Definition: vk_mem_alloc.h:1744
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2042
void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
VkPhysicalDevice physicalDevice
Vulkan physical device.
Definition: vk_mem_alloc.h:1802
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:2839
VkResult vmaDefragment(VmaAllocator allocator, VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
Deprecated. Compacts memory by moving allocations.
void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Invalidates memory of given allocation.
Represents single memory allocation.
Definition: vk_mem_alloc.h:1776
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:2367
PFN_vkCreateBuffer vkCreateBuffer
Definition: vk_mem_alloc.h:1756
void vmaFreeStatsString(VmaAllocator allocator, char *pStatsString)
struct VmaStats VmaStats
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:2004
Definition: vk_mem_alloc.h:2102
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:2792
PFN_vkMapMemory vkMapMemory
Definition: vk_mem_alloc.h:1748
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:2467
VmaAllocatorCreateFlags flags
Flags for created allocator. Use VmaAllocatorCreateFlagBits enum.
Definition: vk_mem_alloc.h:1799
uint32_t maxAllocationsToMove
Maximum number of allocations that can be moved to different place.
Definition: vk_mem_alloc.h:2875
Use this flag if you always allocate only buffers and linear images or only optimal images out of thi...
Definition: vk_mem_alloc.h:2256
#define VMA_RECORDING_ENABLED
Definition: vk_mem_alloc.h:1643
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...
VkDeviceSize size
Total amount of VkDeviceMemory allocated from Vulkan for this pool, in bytes.
Definition: vk_mem_alloc.h:2348
Definition: vk_mem_alloc.h:2079
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:2795
VkFlags VmaAllocatorCreateFlags
Definition: vk_mem_alloc.h:1737
VkMemoryPropertyFlags preferredFlags
Flags that preferably should be set in a memory type chosen for an allocation.
Definition: vk_mem_alloc.h:2155
Definition: vk_mem_alloc.h:2031
const VkAllocationCallbacks * pAllocationCallbacks
Custom CPU memory allocation callbacks. Optional.
Definition: vk_mem_alloc.h:1811
Enables alternative, buddy allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2284
void vmaCalculateStats(VmaAllocator allocator, VmaStats *pStats)
Retrieves statistics from current state of the Allocator.
const VmaVulkanFunctions * pVulkanFunctions
Pointers to Vulkan functions. Can be null if you leave define VMA_STATIC_VULKAN_FUNCTIONS 1...
Definition: vk_mem_alloc.h:1865
Description of a Allocator to be created.
Definition: vk_mem_alloc.h:1796
void vmaDestroyAllocator(VmaAllocator allocator)
Destroys allocator object.
VmaAllocationCreateFlagBits
Flags to be passed as VmaAllocationCreateInfo::flags.
Definition: vk_mem_alloc.h:2035
void vmaGetAllocationInfo(VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
Returns current information about specified allocation and atomically marks it as used in current fra...
VkDeviceSize allocationSizeMax
Definition: vk_mem_alloc.h:1937
PFN_vkBindImageMemory vkBindImageMemory
Definition: vk_mem_alloc.h:1753
VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:2829
VkDeviceSize unusedBytes
Total number of bytes occupied by unused ranges.
Definition: vk_mem_alloc.h:1936
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:2879
void vmaFreeMemory(VmaAllocator allocator, VmaAllocation allocation)
Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:1828
VmaStatInfo total
Definition: vk_mem_alloc.h:1946
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:2887
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2139
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places...
Definition: vk_mem_alloc.h:2870
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements
Definition: vk_mem_alloc.h:1754
void(VKAPI_PTR * PFN_vmaAllocateDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
Callback function called after successful vkAllocateMemory.
Definition: vk_mem_alloc.h:1679
Represents main object of this library initialized.
VkDevice device
Vulkan device.
Definition: vk_mem_alloc.h:1805
void vmaFreeMemoryPages(VmaAllocator allocator, size_t allocationCount, VmaAllocation *pAllocations)
Frees memory and destroys multiple allocations.
VkResult vmaBindBufferMemory(VmaAllocator allocator, VmaAllocation allocation, VkBuffer buffer)
Binds buffer to allocation.
Describes parameter of created VmaPool.
Definition: vk_mem_alloc.h:2298
Definition: vk_mem_alloc.h:2292
PFN_vkCmdCopyBuffer vkCmdCopyBuffer
Definition: vk_mem_alloc.h:1760
const VmaRecordSettings * pRecordSettings
Parameters for recording of VMA calls. Can be null.
Definition: vk_mem_alloc.h:1872
VkDeviceSize size
Size of this allocation, in bytes.
Definition: vk_mem_alloc.h:2477
void vmaGetMemoryTypeProperties(VmaAllocator allocator, uint32_t memoryTypeIndex, VkMemoryPropertyFlags *pFlags)
Given Memory Type Index, returns Property Flags of this memory type.
PFN_vkUnmapMemory vkUnmapMemory
Definition: vk_mem_alloc.h:1749
VkResult vmaDefragmentationBegin(VmaAllocator allocator, const VmaDefragmentationInfo2 *pInfo, VmaDefragmentationStats *pStats, VmaDefragmentationContext *pContext)
Begins defragmentation process.
Enables flush after recording every function call.
Definition: vk_mem_alloc.h:1774
void * pUserData
Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
Definition: vk_mem_alloc.h:2176
size_t minBlockCount
Minimum number of blocks to be always allocated in this pool, even if they stay empty.
Definition: vk_mem_alloc.h:2318
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost...
Definition: vk_mem_alloc.h:2354
struct VmaVulkanFunctions VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:1735
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition: vk_mem_alloc.h:2301
VkResult vmaFindMemoryTypeIndex(VmaAllocator allocator, uint32_t memoryTypeBits, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
VkDeviceSize maxGpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:2844
VmaMemoryUsage
Definition: vk_mem_alloc.h:1982
struct VmaAllocationInfo VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:2804
void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Flushes memory of given allocation.
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:2865
struct VmaPoolCreateInfo VmaPoolCreateInfo
Describes parameter of created VmaPool.
void vmaDestroyPool(VmaAllocator allocator, VmaPool pool)
Destroys VmaPool object and frees Vulkan device memory.
VkDeviceSize bytesFreed
Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects...
Definition: vk_mem_alloc.h:2883
Definition: vk_mem_alloc.h:2021
uint32_t memoryTypeBits
Bitmask containing one bit set for every memory type acceptable for this allocation.
Definition: vk_mem_alloc.h:2163
PFN_vkBindBufferMemory vkBindBufferMemory
Definition: vk_mem_alloc.h:1752
Represents custom memory pool.
void vmaGetPoolStats(VmaAllocator allocator, VmaPool pool, VmaPoolStats *pPoolStats)
Retrieves statistics of existing VmaPool object.
struct VmaDefragmentationInfo VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
VkResult vmaDefragmentationEnd(VmaAllocator allocator, VmaDefragmentationContext context)
Ends defragmentation process.
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:1942
void(VKAPI_PTR * PFN_vmaFreeDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
Callback function called before vkFreeMemory.
Definition: vk_mem_alloc.h:1685
VkFlags VmaDefragmentationFlags
Definition: vk_mem_alloc.h:2783
void vmaSetAllocationUserData(VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
Sets pUserData in given allocation to new value.
Definition: vk_mem_alloc.h:2781
VkBool32 * pAllocationsChanged
Optional, output. Pointer to array that will be filled with information whether the allocation at cer...
Definition: vk_mem_alloc.h:2810
VkResult vmaCreatePool(VmaAllocator allocator, const VmaPoolCreateInfo *pCreateInfo, VmaPool *pPool)
Allocates Vulkan device memory and creates VmaPool object.
VmaAllocatorCreateFlagBits
Flags for created VmaAllocator.
Definition: vk_mem_alloc.h:1706
VkResult vmaBindImageMemory(VmaAllocator allocator, VmaAllocation allocation, VkImage image)
Binds image to allocation.
struct VmaStatInfo VmaStatInfo
Calculated statistics of memory usage in entire allocator.
VkFlags VmaRecordFlags
Definition: vk_mem_alloc.h:1778
Allocator and all objects created from it will not be synchronized internally, so you must guarantee ...
Definition: vk_mem_alloc.h:1711
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:2885
void vmaCreateLostAllocation(VmaAllocator allocator, VmaAllocation *pAllocation)
Creates new allocation that is in lost state from the beginning.
VkMemoryPropertyFlags requiredFlags
Flags that must be set in a Memory Type chosen for an allocation.
Definition: vk_mem_alloc.h:2150
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:2364
void vmaBuildStatsString(VmaAllocator allocator, char **ppStatsString, VkBool32 detailedMap)
Builds and returns statistics as string in JSON format.
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties
Definition: vk_mem_alloc.h:1745
Calculated statistics of memory usage in entire allocator.
Definition: vk_mem_alloc.h:1925
VkDeviceSize blockSize
Size of a single VkDeviceMemory block to be allocated as part of this pool, in bytes. Optional.
Definition: vk_mem_alloc.h:2313
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
Definition: vk_mem_alloc.h:1698
Definition: vk_mem_alloc.h:2288
VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Definition: vk_mem_alloc.h:2086
Represents Opaque object that represents started defragmentation process.
VkDeviceSize unusedRangeSizeMin
Definition: vk_mem_alloc.h:1938
PFN_vmaFreeDeviceMemoryFunction pfnFree
Optional, can be null.
Definition: vk_mem_alloc.h:1702
Definition: vk_mem_alloc.h:2113
VmaPoolCreateFlags flags
Use combination of VmaPoolCreateFlagBits.
Definition: vk_mem_alloc.h:2304
Definition: vk_mem_alloc.h:2030
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges
Definition: vk_mem_alloc.h:1751
struct VmaPoolStats VmaPoolStats
Describes parameter of existing VmaPool.
VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaCreateBuffer().
VmaMemoryUsage usage
Intended usage of memory.
Definition: vk_mem_alloc.h:2145
Definition: vk_mem_alloc.h:2136
VkResult vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
uint32_t blockCount
Number of VkDeviceMemory Vulkan memory blocks allocated.
Definition: vk_mem_alloc.h:1928
PFN_vkFreeMemory vkFreeMemory
Definition: vk_mem_alloc.h:1747
size_t maxBlockCount
Maximum number of blocks that can be allocated in this pool. Optional.
Definition: vk_mem_alloc.h:2326
const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
Informative callbacks for vkAllocateMemory, vkFreeMemory. Optional.
Definition: vk_mem_alloc.h:1814
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:2357
VkFlags VmaAllocationCreateFlags
Definition: vk_mem_alloc.h:2134
VkDeviceSize maxCpuBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places using ...
Definition: vk_mem_alloc.h:2834
VmaPool pool
Pool that this allocation should be created in.
Definition: vk_mem_alloc.h:2169
void vmaGetMemoryProperties(VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
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:1853
VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
Definition: vk_mem_alloc.h:1944
Set this flag to use a memory that will be persistently mapped and retrieve pointer to it...
Definition: vk_mem_alloc.h:2066
VkDeviceSize allocationSizeMin
Definition: vk_mem_alloc.h:1937
VkResult vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator, const VkBufferCreateInfo *pBufferCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
PFN_vkCreateImage vkCreateImage
Definition: vk_mem_alloc.h:1758
VmaRecordFlags flags
Flags for recording. Use VmaRecordFlagBits enum.
Definition: vk_mem_alloc.h:1784
VmaDefragmentationFlagBits
Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use...
Definition: vk_mem_alloc.h:2780
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:2858
PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
Optional, can be null.
Definition: vk_mem_alloc.h:1700
PFN_vkDestroyBuffer vkDestroyBuffer
Definition: vk_mem_alloc.h:1757
VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void **ppData)
Maps memory represented by given allocation and returns pointer to it.
uint32_t frameInUseCount
Maximum number of additional frames that are in use at the same time as current frame.
Definition: vk_mem_alloc.h:2340
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges
Definition: vk_mem_alloc.h:1750
Definition: vk_mem_alloc.h:2097
VkResult vmaAllocateMemoryForImage(VmaAllocator allocator, VkImage image, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Function similar to vmaAllocateMemoryForBuffer().
struct VmaAllocatorCreateInfo VmaAllocatorCreateInfo
Description of a Allocator to be created.
const char * pFilePath
Path to the file that should be written by the recording.
Definition: vk_mem_alloc.h:1792
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:2491
VkDeviceSize preferredLargeHeapBlockSize
Preferred size of a single VkDeviceMemory block to be allocated from large heaps > 1 GiB...
Definition: vk_mem_alloc.h:1808
VkDeviceSize allocationSizeAvg
Definition: vk_mem_alloc.h:1937
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.
VkDeviceSize usedBytes
Total number of bytes occupied by all allocations.
Definition: vk_mem_alloc.h:1934
struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
Checks magic number in margins around all allocations in given memory types (in both default and cust...
Describes parameter of existing VmaPool.
Definition: vk_mem_alloc.h:2345
Parameters for defragmentation.
Definition: vk_mem_alloc.h:2789
VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
Checks magic number in margins around all allocations in given memory pool in search for corruptions...
Definition: vk_mem_alloc.h:2106
VkDeviceSize offset
Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
Definition: vk_mem_alloc.h:2472
Definition: vk_mem_alloc.h:2120
Definition: vk_mem_alloc.h:2132
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places...
Definition: vk_mem_alloc.h:2881
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:1743
VkResult vmaCreateAllocator(const VmaAllocatorCreateInfo *pCreateInfo, VmaAllocator *pAllocator)
Creates Allocator object.
uint32_t unusedRangeCount
Number of free ranges of memory between allocations.
Definition: vk_mem_alloc.h:1932
Definition: vk_mem_alloc.h:1987
VkFlags VmaPoolCreateFlags
Definition: vk_mem_alloc.h:2294
void vmaGetPhysicalDeviceProperties(VmaAllocator allocator, const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
Definition: vk_mem_alloc.h:1781
uint32_t allocationCount
Number of VmaAllocation allocation objects allocated.
Definition: vk_mem_alloc.h:1930
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements
Definition: vk_mem_alloc.h:1755
PFN_vkDestroyImage vkDestroyImage
Definition: vk_mem_alloc.h:1759
Set this flag to only try to allocate from existing VkDeviceMemory blocks and never create new such b...
Definition: vk_mem_alloc.h:2053
Definition: vk_mem_alloc.h:2127
Definition: vk_mem_alloc.h:2014
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:2486
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation)
Destroys Vulkan image and frees allocated memory.
Enables usage of VK_KHR_dedicated_allocation extension.
Definition: vk_mem_alloc.h:1733
struct VmaDefragmentationStats VmaDefragmentationStats
Statistics returned by function vmaDefragment().
PFN_vkAllocateMemory vkAllocateMemory
Definition: vk_mem_alloc.h:1746
Enables alternative, linear allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2273
VkResult vmaResizeAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize newSize)
Tries to resize an allocation in place, if there is enough free memory after it.
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
Definition: vk_mem_alloc.h:2453
VkResult vmaAllocateMemory(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
General purpose memory allocation.
void vmaSetCurrentFrameIndex(VmaAllocator allocator, uint32_t frameIndex)
Sets index of the current frame.
struct VmaAllocationCreateInfo VmaAllocationCreateInfo
VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
Definition: vk_mem_alloc.h:2117
VmaPoolCreateFlagBits
Flags to be passed as VmaPoolCreateInfo::flags.
Definition: vk_mem_alloc.h:2238
VkDeviceSize unusedRangeSizeAvg
Definition: vk_mem_alloc.h:1938
VkBool32 vmaTouchAllocation(VmaAllocator allocator, VmaAllocation allocation)
Returns VK_TRUE if allocation is not lost and atomically marks it as used in current frame...
VmaRecordFlagBits
Flags to be used in VmaRecordSettings::flags.
Definition: vk_mem_alloc.h:1768
VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
Definition: vk_mem_alloc.h:1945
void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
Destroys Vulkan buffer and frees allocated memory.
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:2351
VkDeviceSize unusedRangeSizeMax
Definition: vk_mem_alloc.h:1938
struct VmaDefragmentationInfo2 VmaDefragmentationInfo2
Parameters for defragmentation.
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:2849
struct VmaRecordSettings VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
uint32_t memoryType
Memory type index that this allocation was allocated from.
Definition: vk_mem_alloc.h:2458
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:2813