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