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