Vulkan Memory Allocator
vk_mem_alloc.h
Go to the documentation of this file.
1 //
2 // Copyright (c) 2017-2020 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 
1893 #if VMA_RECORDING_ENABLED
1894  #include <chrono>
1895  #if defined(_WIN32)
1896  #include <windows.h>
1897  #else
1898  #include <sstream>
1899  #include <thread>
1900  #endif
1901 #endif
1902 
1903 #ifdef __cplusplus
1904 extern "C" {
1905 #endif
1906 
1907 /*
1908 Define this macro to 0/1 to disable/enable support for recording functionality,
1909 available through VmaAllocatorCreateInfo::pRecordSettings.
1910 */
1911 #ifndef VMA_RECORDING_ENABLED
1912  #define VMA_RECORDING_ENABLED 0
1913 #endif
1914 
1915 #ifndef NOMINMAX
1916  #define NOMINMAX // For windows.h
1917 #endif
1918 
1919 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
1920  extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
1921  extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
1922  extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1923  extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1924  extern PFN_vkAllocateMemory vkAllocateMemory;
1925  extern PFN_vkFreeMemory vkFreeMemory;
1926  extern PFN_vkMapMemory vkMapMemory;
1927  extern PFN_vkUnmapMemory vkUnmapMemory;
1928  extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1929  extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1930  extern PFN_vkBindBufferMemory vkBindBufferMemory;
1931  extern PFN_vkBindImageMemory vkBindImageMemory;
1932  extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1933  extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1934  extern PFN_vkCreateBuffer vkCreateBuffer;
1935  extern PFN_vkDestroyBuffer vkDestroyBuffer;
1936  extern PFN_vkCreateImage vkCreateImage;
1937  extern PFN_vkDestroyImage vkDestroyImage;
1938  extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1939  #if VMA_VULKAN_VERSION >= 1001000
1940  extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
1941  extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
1942  extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
1943  extern PFN_vkBindImageMemory2 vkBindImageMemory2;
1944  extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
1945  #endif // #if VMA_VULKAN_VERSION >= 1001000
1946 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
1947 
1948 #ifndef VULKAN_H_
1949  #include <vulkan/vulkan.h>
1950 #endif
1951 
1952 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
1953 // where AAA = major, BBB = minor, CCC = patch.
1954 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
1955 #if !defined(VMA_VULKAN_VERSION)
1956  #if defined(VK_VERSION_1_2)
1957  #define VMA_VULKAN_VERSION 1002000
1958  #elif defined(VK_VERSION_1_1)
1959  #define VMA_VULKAN_VERSION 1001000
1960  #else
1961  #define VMA_VULKAN_VERSION 1000000
1962  #endif
1963 #endif
1964 
1965 #if !defined(VMA_DEDICATED_ALLOCATION)
1966  #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1967  #define VMA_DEDICATED_ALLOCATION 1
1968  #else
1969  #define VMA_DEDICATED_ALLOCATION 0
1970  #endif
1971 #endif
1972 
1973 #if !defined(VMA_BIND_MEMORY2)
1974  #if VK_KHR_bind_memory2
1975  #define VMA_BIND_MEMORY2 1
1976  #else
1977  #define VMA_BIND_MEMORY2 0
1978  #endif
1979 #endif
1980 
1981 #if !defined(VMA_MEMORY_BUDGET)
1982  #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
1983  #define VMA_MEMORY_BUDGET 1
1984  #else
1985  #define VMA_MEMORY_BUDGET 0
1986  #endif
1987 #endif
1988 
1989 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
1990 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
1991  #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
1992  #define VMA_BUFFER_DEVICE_ADDRESS 1
1993  #else
1994  #define VMA_BUFFER_DEVICE_ADDRESS 0
1995  #endif
1996 #endif
1997 
1998 // Define these macros to decorate all public functions with additional code,
1999 // before and after returned type, appropriately. This may be useful for
2000 // exporing the functions when compiling VMA as a separate library. Example:
2001 // #define VMA_CALL_PRE __declspec(dllexport)
2002 // #define VMA_CALL_POST __cdecl
2003 #ifndef VMA_CALL_PRE
2004  #define VMA_CALL_PRE
2005 #endif
2006 #ifndef VMA_CALL_POST
2007  #define VMA_CALL_POST
2008 #endif
2009 
2010 // Define this macro to decorate pointers with an attribute specifying the
2011 // length of the array they point to if they are not null.
2012 //
2013 // The length may be one of
2014 // - The name of another parameter in the argument list where the pointer is declared
2015 // - The name of another member in the struct where the pointer is declared
2016 // - The name of a member of a struct type, meaning the value of that member in
2017 // the context of the call. For example
2018 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2019 // this means the number of memory heaps available in the device associated
2020 // with the VmaAllocator being dealt with.
2021 #ifndef VMA_LEN_IF_NOT_NULL
2022  #define VMA_LEN_IF_NOT_NULL(len)
2023 #endif
2024 
2025 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2026 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2027 #ifndef VMA_NULLABLE
2028  #ifdef __clang__
2029  #define VMA_NULLABLE _Nullable
2030  #else
2031  #define VMA_NULLABLE
2032  #endif
2033 #endif
2034 
2035 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2036 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2037 #ifndef VMA_NOT_NULL
2038  #ifdef __clang__
2039  #define VMA_NOT_NULL _Nonnull
2040  #else
2041  #define VMA_NOT_NULL
2042  #endif
2043 #endif
2044 
2045 // If non-dispatchable handles are represented as pointers then we can give
2046 // then nullability annotations
2047 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2048  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2049  #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2050  #else
2051  #define VMA_NOT_NULL_NON_DISPATCHABLE
2052  #endif
2053 #endif
2054 
2055 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2056  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2057  #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2058  #else
2059  #define VMA_NULLABLE_NON_DISPATCHABLE
2060  #endif
2061 #endif
2062 
2072 VK_DEFINE_HANDLE(VmaAllocator)
2073 
2074 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2076  VmaAllocator VMA_NOT_NULL allocator,
2077  uint32_t memoryType,
2078  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2079  VkDeviceSize size,
2080  void* VMA_NULLABLE pUserData);
2082 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2083  VmaAllocator VMA_NOT_NULL allocator,
2084  uint32_t memoryType,
2085  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2086  VkDeviceSize size,
2087  void* VMA_NULLABLE pUserData);
2088 
2102  void* VMA_NULLABLE pUserData;
2104 
2200 
2203 typedef VkFlags VmaAllocatorCreateFlags;
2204 
2209 typedef struct VmaVulkanFunctions {
2210  PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2211  PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2212  PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2213  PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2214  PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2215  PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2216  PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2217  PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2218  PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2219  PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2220  PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2221  PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2222  PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2223  PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2224  PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2225  PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2226  PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2227 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2228  PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2229  PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2230 #endif
2231 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2232  PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2233  PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2234 #endif
2235 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2236  PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2237 #endif
2239 
2241 typedef enum VmaRecordFlagBits {
2248 
2249  VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2251 typedef VkFlags VmaRecordFlags;
2252 
2254 typedef struct VmaRecordSettings
2255 {
2265  const char* VMA_NOT_NULL pFilePath;
2267 
2270 {
2274 
2275  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2277 
2278  VkDevice VMA_NOT_NULL device;
2280 
2283 
2284  const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2286 
2326  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2327 
2339  const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2344  VkInstance VMA_NOT_NULL instance;
2355 
2357 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2358  const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2359  VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2360 
2362 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2363  VmaAllocator VMA_NULLABLE allocator);
2364 
2367 typedef struct VmaAllocatorInfo
2368 {
2373  VkInstance VMA_NOT_NULL instance;
2378  VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2383  VkDevice VMA_NOT_NULL device;
2385 
2391 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2392 
2397 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2398  VmaAllocator VMA_NOT_NULL allocator,
2399  const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2400 
2405 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2406  VmaAllocator VMA_NOT_NULL allocator,
2407  const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2408 
2415 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2416  VmaAllocator VMA_NOT_NULL allocator,
2417  uint32_t memoryTypeIndex,
2418  VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2419 
2428 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2429  VmaAllocator VMA_NOT_NULL allocator,
2430  uint32_t frameIndex);
2431 
2434 typedef struct VmaStatInfo
2435 {
2437  uint32_t blockCount;
2443  VkDeviceSize usedBytes;
2445  VkDeviceSize unusedBytes;
2446  VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2447  VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2449 
2451 typedef struct VmaStats
2452 {
2453  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2454  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2457 
2467 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2468  VmaAllocator VMA_NOT_NULL allocator,
2469  VmaStats* VMA_NOT_NULL pStats);
2470 
2473 typedef struct VmaBudget
2474 {
2477  VkDeviceSize blockBytes;
2478 
2488  VkDeviceSize allocationBytes;
2489 
2498  VkDeviceSize usage;
2499 
2509  VkDeviceSize budget;
2511 
2522 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2523  VmaAllocator VMA_NOT_NULL allocator,
2524  VmaBudget* VMA_NOT_NULL pBudget);
2525 
2526 #ifndef VMA_STATS_STRING_ENABLED
2527 #define VMA_STATS_STRING_ENABLED 1
2528 #endif
2529 
2530 #if VMA_STATS_STRING_ENABLED
2531 
2533 
2535 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2536  VmaAllocator VMA_NOT_NULL allocator,
2537  char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2538  VkBool32 detailedMap);
2539 
2540 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2541  VmaAllocator VMA_NOT_NULL allocator,
2542  char* VMA_NULLABLE pStatsString);
2543 
2544 #endif // #if VMA_STATS_STRING_ENABLED
2545 
2554 VK_DEFINE_HANDLE(VmaPool)
2555 
2556 typedef enum VmaMemoryUsage
2557 {
2619 
2620  VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2622 
2632 
2697 
2713 
2723 
2730 
2734 
2736 {
2749  VkMemoryPropertyFlags requiredFlags;
2754  VkMemoryPropertyFlags preferredFlags;
2762  uint32_t memoryTypeBits;
2768  VmaPool VMA_NULLABLE pool;
2775  void* VMA_NULLABLE pUserData;
2777 
2794 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2795  VmaAllocator VMA_NOT_NULL allocator,
2796  uint32_t memoryTypeBits,
2797  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2798  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2799 
2812 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2813  VmaAllocator VMA_NOT_NULL allocator,
2814  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2815  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2816  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2817 
2830 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2831  VmaAllocator VMA_NOT_NULL allocator,
2832  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2833  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2834  uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2835 
2856 
2873 
2884 
2890 
2893 typedef VkFlags VmaPoolCreateFlags;
2894 
2897 typedef struct VmaPoolCreateInfo {
2912  VkDeviceSize blockSize;
2941 
2944 typedef struct VmaPoolStats {
2947  VkDeviceSize size;
2950  VkDeviceSize unusedSize;
2963  VkDeviceSize unusedRangeSizeMax;
2966  size_t blockCount;
2968 
2975 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2976  VmaAllocator VMA_NOT_NULL allocator,
2977  const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2978  VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
2979 
2982 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2983  VmaAllocator VMA_NOT_NULL allocator,
2984  VmaPool VMA_NULLABLE pool);
2985 
2992 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
2993  VmaAllocator VMA_NOT_NULL allocator,
2994  VmaPool VMA_NOT_NULL pool,
2995  VmaPoolStats* VMA_NOT_NULL pPoolStats);
2996 
3003 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3004  VmaAllocator VMA_NOT_NULL allocator,
3005  VmaPool VMA_NOT_NULL pool,
3006  size_t* VMA_NULLABLE pLostAllocationCount);
3007 
3022 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3023 
3030 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3031  VmaAllocator VMA_NOT_NULL allocator,
3032  VmaPool VMA_NOT_NULL pool,
3033  const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3034 
3040 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3041  VmaAllocator VMA_NOT_NULL allocator,
3042  VmaPool VMA_NOT_NULL pool,
3043  const char* VMA_NULLABLE pName);
3044 
3069 VK_DEFINE_HANDLE(VmaAllocation)
3070 
3071 
3073 typedef struct VmaAllocationInfo {
3078  uint32_t memoryType;
3087  VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3092  VkDeviceSize offset;
3103  VkDeviceSize size;
3112  void* VMA_NULLABLE pMappedData;
3117  void* VMA_NULLABLE pUserData;
3119 
3130 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3131  VmaAllocator VMA_NOT_NULL allocator,
3132  const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3133  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3134  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3135  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3136 
3156 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3157  VmaAllocator VMA_NOT_NULL allocator,
3158  const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3159  const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3160  size_t allocationCount,
3161  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3162  VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3163 
3170 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3171  VmaAllocator VMA_NOT_NULL allocator,
3172  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3173  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3174  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3175  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3176 
3178 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3179  VmaAllocator VMA_NOT_NULL allocator,
3180  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3181  const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3182  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3183  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3184 
3189 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3190  VmaAllocator VMA_NOT_NULL allocator,
3191  const VmaAllocation VMA_NULLABLE allocation);
3192 
3203 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3204  VmaAllocator VMA_NOT_NULL allocator,
3205  size_t allocationCount,
3206  const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3207 
3215 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3216  VmaAllocator VMA_NOT_NULL allocator,
3217  VmaAllocation VMA_NOT_NULL allocation,
3218  VkDeviceSize newSize);
3219 
3236 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3237  VmaAllocator VMA_NOT_NULL allocator,
3238  VmaAllocation VMA_NOT_NULL allocation,
3239  VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3240 
3255 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3256  VmaAllocator VMA_NOT_NULL allocator,
3257  VmaAllocation VMA_NOT_NULL allocation);
3258 
3272 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3273  VmaAllocator VMA_NOT_NULL allocator,
3274  VmaAllocation VMA_NOT_NULL allocation,
3275  void* VMA_NULLABLE pUserData);
3276 
3287 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3288  VmaAllocator VMA_NOT_NULL allocator,
3289  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3290 
3329 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3330  VmaAllocator VMA_NOT_NULL allocator,
3331  VmaAllocation VMA_NOT_NULL allocation,
3332  void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3333 
3342 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3343  VmaAllocator VMA_NOT_NULL allocator,
3344  VmaAllocation VMA_NOT_NULL allocation);
3345 
3367 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3368  VmaAllocator VMA_NOT_NULL allocator,
3369  VmaAllocation VMA_NOT_NULL allocation,
3370  VkDeviceSize offset,
3371  VkDeviceSize size);
3372 
3394 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3395  VmaAllocator VMA_NOT_NULL allocator,
3396  VmaAllocation VMA_NOT_NULL allocation,
3397  VkDeviceSize offset,
3398  VkDeviceSize size);
3399 
3414 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3415  VmaAllocator VMA_NOT_NULL allocator,
3416  uint32_t allocationCount,
3417  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3418  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3419  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3420 
3435 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3436  VmaAllocator VMA_NOT_NULL allocator,
3437  uint32_t allocationCount,
3438  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3439  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3440  const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3441 
3458 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3459 
3466 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3467 
3468 typedef enum VmaDefragmentationFlagBits {
3473 typedef VkFlags VmaDefragmentationFlags;
3474 
3479 typedef struct VmaDefragmentationInfo2 {
3494  const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3500  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3503  uint32_t poolCount;
3519  const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3524  VkDeviceSize maxCpuBytesToMove;
3534  VkDeviceSize maxGpuBytesToMove;
3548  VkCommandBuffer VMA_NULLABLE commandBuffer;
3550 
3553  VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3554  VkDeviceSize offset;
3556 
3562  uint32_t moveCount;
3563  VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3565 
3570 typedef struct VmaDefragmentationInfo {
3575  VkDeviceSize maxBytesToMove;
3582 
3584 typedef struct VmaDefragmentationStats {
3586  VkDeviceSize bytesMoved;
3588  VkDeviceSize bytesFreed;
3594 
3624 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3625  VmaAllocator VMA_NOT_NULL allocator,
3626  const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3627  VmaDefragmentationStats* VMA_NULLABLE pStats,
3628  VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3629 
3635 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3636  VmaAllocator VMA_NOT_NULL allocator,
3637  VmaDefragmentationContext VMA_NULLABLE context);
3638 
3639 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3640  VmaAllocator VMA_NOT_NULL allocator,
3641  VmaDefragmentationContext VMA_NULLABLE context,
3642  VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3643 );
3644 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3645  VmaAllocator VMA_NOT_NULL allocator,
3646  VmaDefragmentationContext VMA_NULLABLE context
3647 );
3648 
3689 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3690  VmaAllocator VMA_NOT_NULL allocator,
3691  const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3692  size_t allocationCount,
3693  VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3694  const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3695  VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3696 
3709 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3710  VmaAllocator VMA_NOT_NULL allocator,
3711  VmaAllocation VMA_NOT_NULL allocation,
3712  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3713 
3724 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3725  VmaAllocator VMA_NOT_NULL allocator,
3726  VmaAllocation VMA_NOT_NULL allocation,
3727  VkDeviceSize allocationLocalOffset,
3728  VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3729  const void* VMA_NULLABLE pNext);
3730 
3743 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3744  VmaAllocator VMA_NOT_NULL allocator,
3745  VmaAllocation VMA_NOT_NULL allocation,
3746  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3747 
3758 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3759  VmaAllocator VMA_NOT_NULL allocator,
3760  VmaAllocation VMA_NOT_NULL allocation,
3761  VkDeviceSize allocationLocalOffset,
3762  VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3763  const void* VMA_NULLABLE pNext);
3764 
3791 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3792  VmaAllocator VMA_NOT_NULL allocator,
3793  const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3794  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3795  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3796  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3797  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3798 
3810 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3811  VmaAllocator VMA_NOT_NULL allocator,
3812  VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3813  VmaAllocation VMA_NULLABLE allocation);
3814 
3816 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3817  VmaAllocator VMA_NOT_NULL allocator,
3818  const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3819  const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3820  VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3821  VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3822  VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3823 
3835 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3836  VmaAllocator VMA_NOT_NULL allocator,
3837  VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3838  VmaAllocation VMA_NULLABLE allocation);
3839 
3840 #ifdef __cplusplus
3841 }
3842 #endif
3843 
3844 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3845 
3846 // For Visual Studio IntelliSense.
3847 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3848 #define VMA_IMPLEMENTATION
3849 #endif
3850 
3851 #ifdef VMA_IMPLEMENTATION
3852 #undef VMA_IMPLEMENTATION
3853 
3854 #include <cstdint>
3855 #include <cstdlib>
3856 #include <cstring>
3857 #include <utility>
3858 
3859 /*******************************************************************************
3860 CONFIGURATION SECTION
3861 
3862 Define some of these macros before each #include of this header or change them
3863 here if you need other then default behavior depending on your environment.
3864 */
3865 
3866 /*
3867 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3868 internally, like:
3869 
3870  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3871 */
3872 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3873  #define VMA_STATIC_VULKAN_FUNCTIONS 1
3874 #endif
3875 
3876 /*
3877 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3878 internally, like:
3879 
3880  vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3881 */
3882 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3883  #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3884 #endif
3885 
3886 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3887 //#define VMA_USE_STL_CONTAINERS 1
3888 
3889 /* Set this macro to 1 to make the library including and using STL containers:
3890 std::pair, std::vector, std::list, std::unordered_map.
3891 
3892 Set it to 0 or undefined to make the library using its own implementation of
3893 the containers.
3894 */
3895 #if VMA_USE_STL_CONTAINERS
3896  #define VMA_USE_STL_VECTOR 1
3897  #define VMA_USE_STL_UNORDERED_MAP 1
3898  #define VMA_USE_STL_LIST 1
3899 #endif
3900 
3901 #ifndef VMA_USE_STL_SHARED_MUTEX
3902  // Compiler conforms to C++17.
3903  #if __cplusplus >= 201703L
3904  #define VMA_USE_STL_SHARED_MUTEX 1
3905  // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3906  // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3907  // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3908  #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3909  #define VMA_USE_STL_SHARED_MUTEX 1
3910  #else
3911  #define VMA_USE_STL_SHARED_MUTEX 0
3912  #endif
3913 #endif
3914 
3915 /*
3916 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3917 Library has its own container implementation.
3918 */
3919 #if VMA_USE_STL_VECTOR
3920  #include <vector>
3921 #endif
3922 
3923 #if VMA_USE_STL_UNORDERED_MAP
3924  #include <unordered_map>
3925 #endif
3926 
3927 #if VMA_USE_STL_LIST
3928  #include <list>
3929 #endif
3930 
3931 /*
3932 Following headers are used in this CONFIGURATION section only, so feel free to
3933 remove them if not needed.
3934 */
3935 #include <cassert> // for assert
3936 #include <algorithm> // for min, max
3937 #include <mutex>
3938 
3939 #ifndef VMA_NULL
3940  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3941  #define VMA_NULL nullptr
3942 #endif
3943 
3944 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3945 #include <cstdlib>
3946 void *aligned_alloc(size_t alignment, size_t size)
3947 {
3948  // alignment must be >= sizeof(void*)
3949  if(alignment < sizeof(void*))
3950  {
3951  alignment = sizeof(void*);
3952  }
3953 
3954  return memalign(alignment, size);
3955 }
3956 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3957 #include <cstdlib>
3958 void *aligned_alloc(size_t alignment, size_t size)
3959 {
3960  // alignment must be >= sizeof(void*)
3961  if(alignment < sizeof(void*))
3962  {
3963  alignment = sizeof(void*);
3964  }
3965 
3966  void *pointer;
3967  if(posix_memalign(&pointer, alignment, size) == 0)
3968  return pointer;
3969  return VMA_NULL;
3970 }
3971 #endif
3972 
3973 // If your compiler is not compatible with C++11 and definition of
3974 // aligned_alloc() function is missing, uncommeting following line may help:
3975 
3976 //#include <malloc.h>
3977 
3978 // Normal assert to check for programmer's errors, especially in Debug configuration.
3979 #ifndef VMA_ASSERT
3980  #ifdef NDEBUG
3981  #define VMA_ASSERT(expr)
3982  #else
3983  #define VMA_ASSERT(expr) assert(expr)
3984  #endif
3985 #endif
3986 
3987 // Assert that will be called very often, like inside data structures e.g. operator[].
3988 // Making it non-empty can make program slow.
3989 #ifndef VMA_HEAVY_ASSERT
3990  #ifdef NDEBUG
3991  #define VMA_HEAVY_ASSERT(expr)
3992  #else
3993  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3994  #endif
3995 #endif
3996 
3997 #ifndef VMA_ALIGN_OF
3998  #define VMA_ALIGN_OF(type) (__alignof(type))
3999 #endif
4000 
4001 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4002  #if defined(_WIN32)
4003  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
4004  #else
4005  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
4006  #endif
4007 #endif
4008 
4009 #ifndef VMA_SYSTEM_FREE
4010  #if defined(_WIN32)
4011  #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
4012  #else
4013  #define VMA_SYSTEM_FREE(ptr) free(ptr)
4014  #endif
4015 #endif
4016 
4017 #ifndef VMA_MIN
4018  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4019 #endif
4020 
4021 #ifndef VMA_MAX
4022  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4023 #endif
4024 
4025 #ifndef VMA_SWAP
4026  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4027 #endif
4028 
4029 #ifndef VMA_SORT
4030  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4031 #endif
4032 
4033 #ifndef VMA_DEBUG_LOG
4034  #define VMA_DEBUG_LOG(format, ...)
4035  /*
4036  #define VMA_DEBUG_LOG(format, ...) do { \
4037  printf(format, __VA_ARGS__); \
4038  printf("\n"); \
4039  } while(false)
4040  */
4041 #endif
4042 
4043 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4044 #if VMA_STATS_STRING_ENABLED
4045  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4046  {
4047  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4048  }
4049  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4050  {
4051  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4052  }
4053  static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4054  {
4055  snprintf(outStr, strLen, "%p", ptr);
4056  }
4057 #endif
4058 
4059 #ifndef VMA_MUTEX
4060  class VmaMutex
4061  {
4062  public:
4063  void Lock() { m_Mutex.lock(); }
4064  void Unlock() { m_Mutex.unlock(); }
4065  bool TryLock() { return m_Mutex.try_lock(); }
4066  private:
4067  std::mutex m_Mutex;
4068  };
4069  #define VMA_MUTEX VmaMutex
4070 #endif
4071 
4072 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4073 #ifndef VMA_RW_MUTEX
4074  #if VMA_USE_STL_SHARED_MUTEX
4075  // Use std::shared_mutex from C++17.
4076  #include <shared_mutex>
4077  class VmaRWMutex
4078  {
4079  public:
4080  void LockRead() { m_Mutex.lock_shared(); }
4081  void UnlockRead() { m_Mutex.unlock_shared(); }
4082  bool TryLockRead() { return m_Mutex.try_lock_shared(); }
4083  void LockWrite() { m_Mutex.lock(); }
4084  void UnlockWrite() { m_Mutex.unlock(); }
4085  bool TryLockWrite() { return m_Mutex.try_lock(); }
4086  private:
4087  std::shared_mutex m_Mutex;
4088  };
4089  #define VMA_RW_MUTEX VmaRWMutex
4090  #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4091  // Use SRWLOCK from WinAPI.
4092  // Minimum supported client = Windows Vista, server = Windows Server 2008.
4093  class VmaRWMutex
4094  {
4095  public:
4096  VmaRWMutex() { InitializeSRWLock(&m_Lock); }
4097  void LockRead() { AcquireSRWLockShared(&m_Lock); }
4098  void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
4099  bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
4100  void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
4101  void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
4102  bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4103  private:
4104  SRWLOCK m_Lock;
4105  };
4106  #define VMA_RW_MUTEX VmaRWMutex
4107  #else
4108  // Less efficient fallback: Use normal mutex.
4109  class VmaRWMutex
4110  {
4111  public:
4112  void LockRead() { m_Mutex.Lock(); }
4113  void UnlockRead() { m_Mutex.Unlock(); }
4114  bool TryLockRead() { return m_Mutex.TryLock(); }
4115  void LockWrite() { m_Mutex.Lock(); }
4116  void UnlockWrite() { m_Mutex.Unlock(); }
4117  bool TryLockWrite() { return m_Mutex.TryLock(); }
4118  private:
4119  VMA_MUTEX m_Mutex;
4120  };
4121  #define VMA_RW_MUTEX VmaRWMutex
4122  #endif // #if VMA_USE_STL_SHARED_MUTEX
4123 #endif // #ifndef VMA_RW_MUTEX
4124 
4125 /*
4126 If providing your own implementation, you need to implement a subset of std::atomic.
4127 */
4128 #ifndef VMA_ATOMIC_UINT32
4129  #include <atomic>
4130  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4131 #endif
4132 
4133 #ifndef VMA_ATOMIC_UINT64
4134  #include <atomic>
4135  #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4136 #endif
4137 
4138 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4139 
4143  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4144 #endif
4145 
4146 #ifndef VMA_DEBUG_ALIGNMENT
4147 
4151  #define VMA_DEBUG_ALIGNMENT (1)
4152 #endif
4153 
4154 #ifndef VMA_DEBUG_MARGIN
4155 
4159  #define VMA_DEBUG_MARGIN (0)
4160 #endif
4161 
4162 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4163 
4167  #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4168 #endif
4169 
4170 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4171 
4176  #define VMA_DEBUG_DETECT_CORRUPTION (0)
4177 #endif
4178 
4179 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4180 
4184  #define VMA_DEBUG_GLOBAL_MUTEX (0)
4185 #endif
4186 
4187 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4188 
4192  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4193 #endif
4194 
4195 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4196  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4198 #endif
4199 
4200 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4201  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4203 #endif
4204 
4205 #ifndef VMA_CLASS_NO_COPY
4206  #define VMA_CLASS_NO_COPY(className) \
4207  private: \
4208  className(const className&) = delete; \
4209  className& operator=(const className&) = delete;
4210 #endif
4211 
4212 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4213 
4214 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4215 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4216 
4217 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4218 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4219 
4220 /*******************************************************************************
4221 END OF CONFIGURATION
4222 */
4223 
4224 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4225 
4226 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4227 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4228 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4229 
4230 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4231 
4232 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4233  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4234 
4235 // Returns number of bits set to 1 in (v).
4236 static inline uint32_t VmaCountBitsSet(uint32_t v)
4237 {
4238  uint32_t c = v - ((v >> 1) & 0x55555555);
4239  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4240  c = ((c >> 4) + c) & 0x0F0F0F0F;
4241  c = ((c >> 8) + c) & 0x00FF00FF;
4242  c = ((c >> 16) + c) & 0x0000FFFF;
4243  return c;
4244 }
4245 
4246 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4247 // Use types like uint32_t, uint64_t as T.
4248 template <typename T>
4249 static inline T VmaAlignUp(T val, T align)
4250 {
4251  return (val + align - 1) / align * align;
4252 }
4253 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4254 // Use types like uint32_t, uint64_t as T.
4255 template <typename T>
4256 static inline T VmaAlignDown(T val, T align)
4257 {
4258  return val / align * align;
4259 }
4260 
4261 // Division with mathematical rounding to nearest number.
4262 template <typename T>
4263 static inline T VmaRoundDiv(T x, T y)
4264 {
4265  return (x + (y / (T)2)) / y;
4266 }
4267 
4268 /*
4269 Returns true if given number is a power of two.
4270 T must be unsigned integer number or signed integer but always nonnegative.
4271 For 0 returns true.
4272 */
4273 template <typename T>
4274 inline bool VmaIsPow2(T x)
4275 {
4276  return (x & (x-1)) == 0;
4277 }
4278 
4279 // Returns smallest power of 2 greater or equal to v.
4280 static inline uint32_t VmaNextPow2(uint32_t v)
4281 {
4282  v--;
4283  v |= v >> 1;
4284  v |= v >> 2;
4285  v |= v >> 4;
4286  v |= v >> 8;
4287  v |= v >> 16;
4288  v++;
4289  return v;
4290 }
4291 static inline uint64_t VmaNextPow2(uint64_t v)
4292 {
4293  v--;
4294  v |= v >> 1;
4295  v |= v >> 2;
4296  v |= v >> 4;
4297  v |= v >> 8;
4298  v |= v >> 16;
4299  v |= v >> 32;
4300  v++;
4301  return v;
4302 }
4303 
4304 // Returns largest power of 2 less or equal to v.
4305 static inline uint32_t VmaPrevPow2(uint32_t v)
4306 {
4307  v |= v >> 1;
4308  v |= v >> 2;
4309  v |= v >> 4;
4310  v |= v >> 8;
4311  v |= v >> 16;
4312  v = v ^ (v >> 1);
4313  return v;
4314 }
4315 static inline uint64_t VmaPrevPow2(uint64_t v)
4316 {
4317  v |= v >> 1;
4318  v |= v >> 2;
4319  v |= v >> 4;
4320  v |= v >> 8;
4321  v |= v >> 16;
4322  v |= v >> 32;
4323  v = v ^ (v >> 1);
4324  return v;
4325 }
4326 
4327 static inline bool VmaStrIsEmpty(const char* pStr)
4328 {
4329  return pStr == VMA_NULL || *pStr == '\0';
4330 }
4331 
4332 #if VMA_STATS_STRING_ENABLED
4333 
4334 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4335 {
4336  switch(algorithm)
4337  {
4339  return "Linear";
4341  return "Buddy";
4342  case 0:
4343  return "Default";
4344  default:
4345  VMA_ASSERT(0);
4346  return "";
4347  }
4348 }
4349 
4350 #endif // #if VMA_STATS_STRING_ENABLED
4351 
4352 #ifndef VMA_SORT
4353 
4354 template<typename Iterator, typename Compare>
4355 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4356 {
4357  Iterator centerValue = end; --centerValue;
4358  Iterator insertIndex = beg;
4359  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4360  {
4361  if(cmp(*memTypeIndex, *centerValue))
4362  {
4363  if(insertIndex != memTypeIndex)
4364  {
4365  VMA_SWAP(*memTypeIndex, *insertIndex);
4366  }
4367  ++insertIndex;
4368  }
4369  }
4370  if(insertIndex != centerValue)
4371  {
4372  VMA_SWAP(*insertIndex, *centerValue);
4373  }
4374  return insertIndex;
4375 }
4376 
4377 template<typename Iterator, typename Compare>
4378 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4379 {
4380  if(beg < end)
4381  {
4382  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4383  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4384  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4385  }
4386 }
4387 
4388 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4389 
4390 #endif // #ifndef VMA_SORT
4391 
4392 /*
4393 Returns true if two memory blocks occupy overlapping pages.
4394 ResourceA must be in less memory offset than ResourceB.
4395 
4396 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4397 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4398 */
4399 static inline bool VmaBlocksOnSamePage(
4400  VkDeviceSize resourceAOffset,
4401  VkDeviceSize resourceASize,
4402  VkDeviceSize resourceBOffset,
4403  VkDeviceSize pageSize)
4404 {
4405  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4406  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4407  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4408  VkDeviceSize resourceBStart = resourceBOffset;
4409  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4410  return resourceAEndPage == resourceBStartPage;
4411 }
4412 
4413 enum VmaSuballocationType
4414 {
4415  VMA_SUBALLOCATION_TYPE_FREE = 0,
4416  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4417  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4418  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4419  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4420  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4421  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4422 };
4423 
4424 /*
4425 Returns true if given suballocation types could conflict and must respect
4426 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4427 or linear image and another one is optimal image. If type is unknown, behave
4428 conservatively.
4429 */
4430 static inline bool VmaIsBufferImageGranularityConflict(
4431  VmaSuballocationType suballocType1,
4432  VmaSuballocationType suballocType2)
4433 {
4434  if(suballocType1 > suballocType2)
4435  {
4436  VMA_SWAP(suballocType1, suballocType2);
4437  }
4438 
4439  switch(suballocType1)
4440  {
4441  case VMA_SUBALLOCATION_TYPE_FREE:
4442  return false;
4443  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4444  return true;
4445  case VMA_SUBALLOCATION_TYPE_BUFFER:
4446  return
4447  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4448  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4449  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4450  return
4451  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4452  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4453  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4454  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4455  return
4456  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4457  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4458  return false;
4459  default:
4460  VMA_ASSERT(0);
4461  return true;
4462  }
4463 }
4464 
4465 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4466 {
4467 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4468  uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4469  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4470  for(size_t i = 0; i < numberCount; ++i, ++pDst)
4471  {
4472  *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4473  }
4474 #else
4475  // no-op
4476 #endif
4477 }
4478 
4479 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4480 {
4481 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4482  const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4483  const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4484  for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4485  {
4486  if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4487  {
4488  return false;
4489  }
4490  }
4491 #endif
4492  return true;
4493 }
4494 
4495 /*
4496 Fills structure with parameters of an example buffer to be used for transfers
4497 during GPU memory defragmentation.
4498 */
4499 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4500 {
4501  memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4502  outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4503  outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4504  outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4505 }
4506 
4507 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4508 struct VmaMutexLock
4509 {
4510  VMA_CLASS_NO_COPY(VmaMutexLock)
4511 public:
4512  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4513  m_pMutex(useMutex ? &mutex : VMA_NULL)
4514  { if(m_pMutex) { m_pMutex->Lock(); } }
4515  ~VmaMutexLock()
4516  { if(m_pMutex) { m_pMutex->Unlock(); } }
4517 private:
4518  VMA_MUTEX* m_pMutex;
4519 };
4520 
4521 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4522 struct VmaMutexLockRead
4523 {
4524  VMA_CLASS_NO_COPY(VmaMutexLockRead)
4525 public:
4526  VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4527  m_pMutex(useMutex ? &mutex : VMA_NULL)
4528  { if(m_pMutex) { m_pMutex->LockRead(); } }
4529  ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4530 private:
4531  VMA_RW_MUTEX* m_pMutex;
4532 };
4533 
4534 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4535 struct VmaMutexLockWrite
4536 {
4537  VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4538 public:
4539  VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4540  m_pMutex(useMutex ? &mutex : VMA_NULL)
4541  { if(m_pMutex) { m_pMutex->LockWrite(); } }
4542  ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4543 private:
4544  VMA_RW_MUTEX* m_pMutex;
4545 };
4546 
4547 #if VMA_DEBUG_GLOBAL_MUTEX
4548  static VMA_MUTEX gDebugGlobalMutex;
4549  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4550 #else
4551  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4552 #endif
4553 
4554 // Minimum size of a free suballocation to register it in the free suballocation collection.
4555 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4556 
4557 /*
4558 Performs binary search and returns iterator to first element that is greater or
4559 equal to (key), according to comparison (cmp).
4560 
4561 Cmp should return true if first argument is less than second argument.
4562 
4563 Returned value is the found element, if present in the collection or place where
4564 new element with value (key) should be inserted.
4565 */
4566 template <typename CmpLess, typename IterT, typename KeyT>
4567 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4568 {
4569  size_t down = 0, up = (end - beg);
4570  while(down < up)
4571  {
4572  const size_t mid = (down + up) / 2;
4573  if(cmp(*(beg+mid), key))
4574  {
4575  down = mid + 1;
4576  }
4577  else
4578  {
4579  up = mid;
4580  }
4581  }
4582  return beg + down;
4583 }
4584 
4585 template<typename CmpLess, typename IterT, typename KeyT>
4586 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4587 {
4588  IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4589  beg, end, value, cmp);
4590  if(it == end ||
4591  (!cmp(*it, value) && !cmp(value, *it)))
4592  {
4593  return it;
4594  }
4595  return end;
4596 }
4597 
4598 /*
4599 Returns true if all pointers in the array are not-null and unique.
4600 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4601 T must be pointer type, e.g. VmaAllocation, VmaPool.
4602 */
4603 template<typename T>
4604 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4605 {
4606  for(uint32_t i = 0; i < count; ++i)
4607  {
4608  const T iPtr = arr[i];
4609  if(iPtr == VMA_NULL)
4610  {
4611  return false;
4612  }
4613  for(uint32_t j = i + 1; j < count; ++j)
4614  {
4615  if(iPtr == arr[j])
4616  {
4617  return false;
4618  }
4619  }
4620  }
4621  return true;
4622 }
4623 
4624 template<typename MainT, typename NewT>
4625 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4626 {
4627  newStruct->pNext = mainStruct->pNext;
4628  mainStruct->pNext = newStruct;
4629 }
4630 
4632 // Memory allocation
4633 
4634 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4635 {
4636  void* result = VMA_NULL;
4637  if((pAllocationCallbacks != VMA_NULL) &&
4638  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4639  {
4640  result = (*pAllocationCallbacks->pfnAllocation)(
4641  pAllocationCallbacks->pUserData,
4642  size,
4643  alignment,
4644  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4645  }
4646  else
4647  {
4648  result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4649  }
4650  VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
4651  return result;
4652 }
4653 
4654 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4655 {
4656  if((pAllocationCallbacks != VMA_NULL) &&
4657  (pAllocationCallbacks->pfnFree != VMA_NULL))
4658  {
4659  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4660  }
4661  else
4662  {
4663  VMA_SYSTEM_FREE(ptr);
4664  }
4665 }
4666 
4667 template<typename T>
4668 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4669 {
4670  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4671 }
4672 
4673 template<typename T>
4674 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4675 {
4676  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4677 }
4678 
4679 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4680 
4681 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4682 
4683 template<typename T>
4684 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4685 {
4686  ptr->~T();
4687  VmaFree(pAllocationCallbacks, ptr);
4688 }
4689 
4690 template<typename T>
4691 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4692 {
4693  if(ptr != VMA_NULL)
4694  {
4695  for(size_t i = count; i--; )
4696  {
4697  ptr[i].~T();
4698  }
4699  VmaFree(pAllocationCallbacks, ptr);
4700  }
4701 }
4702 
4703 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4704 {
4705  if(srcStr != VMA_NULL)
4706  {
4707  const size_t len = strlen(srcStr);
4708  char* const result = vma_new_array(allocs, char, len + 1);
4709  memcpy(result, srcStr, len + 1);
4710  return result;
4711  }
4712  else
4713  {
4714  return VMA_NULL;
4715  }
4716 }
4717 
4718 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4719 {
4720  if(str != VMA_NULL)
4721  {
4722  const size_t len = strlen(str);
4723  vma_delete_array(allocs, str, len + 1);
4724  }
4725 }
4726 
4727 // STL-compatible allocator.
4728 template<typename T>
4729 class VmaStlAllocator
4730 {
4731 public:
4732  const VkAllocationCallbacks* const m_pCallbacks;
4733  typedef T value_type;
4734 
4735  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
4736  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4737 
4738  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
4739  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4740 
4741  template<typename U>
4742  bool operator==(const VmaStlAllocator<U>& rhs) const
4743  {
4744  return m_pCallbacks == rhs.m_pCallbacks;
4745  }
4746  template<typename U>
4747  bool operator!=(const VmaStlAllocator<U>& rhs) const
4748  {
4749  return m_pCallbacks != rhs.m_pCallbacks;
4750  }
4751 
4752  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4753 };
4754 
4755 #if VMA_USE_STL_VECTOR
4756 
4757 #define VmaVector std::vector
4758 
4759 template<typename T, typename allocatorT>
4760 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4761 {
4762  vec.insert(vec.begin() + index, item);
4763 }
4764 
4765 template<typename T, typename allocatorT>
4766 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4767 {
4768  vec.erase(vec.begin() + index);
4769 }
4770 
4771 #else // #if VMA_USE_STL_VECTOR
4772 
4773 /* Class with interface compatible with subset of std::vector.
4774 T must be POD because constructors and destructors are not called and memcpy is
4775 used for these objects. */
4776 template<typename T, typename AllocatorT>
4777 class VmaVector
4778 {
4779 public:
4780  typedef T value_type;
4781 
4782  VmaVector(const AllocatorT& allocator) :
4783  m_Allocator(allocator),
4784  m_pArray(VMA_NULL),
4785  m_Count(0),
4786  m_Capacity(0)
4787  {
4788  }
4789 
4790  VmaVector(size_t count, const AllocatorT& allocator) :
4791  m_Allocator(allocator),
4792  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4793  m_Count(count),
4794  m_Capacity(count)
4795  {
4796  }
4797 
4798  // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4799  // value is unused.
4800  VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4801  : VmaVector(count, allocator) {}
4802 
4803  VmaVector(const VmaVector<T, AllocatorT>& src) :
4804  m_Allocator(src.m_Allocator),
4805  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4806  m_Count(src.m_Count),
4807  m_Capacity(src.m_Count)
4808  {
4809  if(m_Count != 0)
4810  {
4811  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4812  }
4813  }
4814 
4815  ~VmaVector()
4816  {
4817  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4818  }
4819 
4820  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4821  {
4822  if(&rhs != this)
4823  {
4824  resize(rhs.m_Count);
4825  if(m_Count != 0)
4826  {
4827  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4828  }
4829  }
4830  return *this;
4831  }
4832 
4833  bool empty() const { return m_Count == 0; }
4834  size_t size() const { return m_Count; }
4835  T* data() { return m_pArray; }
4836  const T* data() const { return m_pArray; }
4837 
4838  T& operator[](size_t index)
4839  {
4840  VMA_HEAVY_ASSERT(index < m_Count);
4841  return m_pArray[index];
4842  }
4843  const T& operator[](size_t index) const
4844  {
4845  VMA_HEAVY_ASSERT(index < m_Count);
4846  return m_pArray[index];
4847  }
4848 
4849  T& front()
4850  {
4851  VMA_HEAVY_ASSERT(m_Count > 0);
4852  return m_pArray[0];
4853  }
4854  const T& front() const
4855  {
4856  VMA_HEAVY_ASSERT(m_Count > 0);
4857  return m_pArray[0];
4858  }
4859  T& back()
4860  {
4861  VMA_HEAVY_ASSERT(m_Count > 0);
4862  return m_pArray[m_Count - 1];
4863  }
4864  const T& back() const
4865  {
4866  VMA_HEAVY_ASSERT(m_Count > 0);
4867  return m_pArray[m_Count - 1];
4868  }
4869 
4870  void reserve(size_t newCapacity, bool freeMemory = false)
4871  {
4872  newCapacity = VMA_MAX(newCapacity, m_Count);
4873 
4874  if((newCapacity < m_Capacity) && !freeMemory)
4875  {
4876  newCapacity = m_Capacity;
4877  }
4878 
4879  if(newCapacity != m_Capacity)
4880  {
4881  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4882  if(m_Count != 0)
4883  {
4884  memcpy(newArray, m_pArray, m_Count * sizeof(T));
4885  }
4886  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4887  m_Capacity = newCapacity;
4888  m_pArray = newArray;
4889  }
4890  }
4891 
4892  void resize(size_t newCount, bool freeMemory = false)
4893  {
4894  size_t newCapacity = m_Capacity;
4895  if(newCount > m_Capacity)
4896  {
4897  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4898  }
4899  else if(freeMemory)
4900  {
4901  newCapacity = newCount;
4902  }
4903 
4904  if(newCapacity != m_Capacity)
4905  {
4906  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4907  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4908  if(elementsToCopy != 0)
4909  {
4910  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4911  }
4912  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4913  m_Capacity = newCapacity;
4914  m_pArray = newArray;
4915  }
4916 
4917  m_Count = newCount;
4918  }
4919 
4920  void clear(bool freeMemory = false)
4921  {
4922  resize(0, freeMemory);
4923  }
4924 
4925  void insert(size_t index, const T& src)
4926  {
4927  VMA_HEAVY_ASSERT(index <= m_Count);
4928  const size_t oldCount = size();
4929  resize(oldCount + 1);
4930  if(index < oldCount)
4931  {
4932  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4933  }
4934  m_pArray[index] = src;
4935  }
4936 
4937  void remove(size_t index)
4938  {
4939  VMA_HEAVY_ASSERT(index < m_Count);
4940  const size_t oldCount = size();
4941  if(index < oldCount - 1)
4942  {
4943  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4944  }
4945  resize(oldCount - 1);
4946  }
4947 
4948  void push_back(const T& src)
4949  {
4950  const size_t newIndex = size();
4951  resize(newIndex + 1);
4952  m_pArray[newIndex] = src;
4953  }
4954 
4955  void pop_back()
4956  {
4957  VMA_HEAVY_ASSERT(m_Count > 0);
4958  resize(size() - 1);
4959  }
4960 
4961  void push_front(const T& src)
4962  {
4963  insert(0, src);
4964  }
4965 
4966  void pop_front()
4967  {
4968  VMA_HEAVY_ASSERT(m_Count > 0);
4969  remove(0);
4970  }
4971 
4972  typedef T* iterator;
4973 
4974  iterator begin() { return m_pArray; }
4975  iterator end() { return m_pArray + m_Count; }
4976 
4977 private:
4978  AllocatorT m_Allocator;
4979  T* m_pArray;
4980  size_t m_Count;
4981  size_t m_Capacity;
4982 };
4983 
4984 template<typename T, typename allocatorT>
4985 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4986 {
4987  vec.insert(index, item);
4988 }
4989 
4990 template<typename T, typename allocatorT>
4991 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4992 {
4993  vec.remove(index);
4994 }
4995 
4996 #endif // #if VMA_USE_STL_VECTOR
4997 
4998 template<typename CmpLess, typename VectorT>
4999 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5000 {
5001  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5002  vector.data(),
5003  vector.data() + vector.size(),
5004  value,
5005  CmpLess()) - vector.data();
5006  VmaVectorInsert(vector, indexToInsert, value);
5007  return indexToInsert;
5008 }
5009 
5010 template<typename CmpLess, typename VectorT>
5011 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5012 {
5013  CmpLess comparator;
5014  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5015  vector.begin(),
5016  vector.end(),
5017  value,
5018  comparator);
5019  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5020  {
5021  size_t indexToRemove = it - vector.begin();
5022  VmaVectorRemove(vector, indexToRemove);
5023  return true;
5024  }
5025  return false;
5026 }
5027 
5029 // class VmaSmallVector
5030 
5031 /*
5032 This is a vector (a variable-sized array), optimized for the case when the array is small.
5033 
5034 It contains some number of elements in-place, which allows it to avoid heap allocation
5035 when the actual number of elements is below that threshold. This allows normal "small"
5036 cases to be fast without losing generality for large inputs.
5037 */
5038 
5039 template<typename T, typename AllocatorT, size_t N>
5040 class VmaSmallVector
5041 {
5042 public:
5043  typedef T value_type;
5044 
5045  VmaSmallVector(const AllocatorT& allocator) :
5046  m_Count(0),
5047  m_DynamicArray(allocator)
5048  {
5049  }
5050  VmaSmallVector(size_t count, const AllocatorT& allocator) :
5051  m_Count(count),
5052  m_DynamicArray(count > N ? count : 0, allocator)
5053  {
5054  }
5055  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5056  VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5057  template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5058  VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5059 
5060  bool empty() const { return m_Count == 0; }
5061  size_t size() const { return m_Count; }
5062  T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5063  const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5064 
5065  T& operator[](size_t index)
5066  {
5067  VMA_HEAVY_ASSERT(index < m_Count);
5068  return data()[index];
5069  }
5070  const T& operator[](size_t index) const
5071  {
5072  VMA_HEAVY_ASSERT(index < m_Count);
5073  return data()[index];
5074  }
5075 
5076  T& front()
5077  {
5078  VMA_HEAVY_ASSERT(m_Count > 0);
5079  return data()[0];
5080  }
5081  const T& front() const
5082  {
5083  VMA_HEAVY_ASSERT(m_Count > 0);
5084  return data()[0];
5085  }
5086  T& back()
5087  {
5088  VMA_HEAVY_ASSERT(m_Count > 0);
5089  return data()[m_Count - 1];
5090  }
5091  const T& back() const
5092  {
5093  VMA_HEAVY_ASSERT(m_Count > 0);
5094  return data()[m_Count - 1];
5095  }
5096 
5097  void resize(size_t newCount, bool freeMemory = false)
5098  {
5099  if(newCount > N && m_Count > N)
5100  {
5101  // Any direction, staying in m_DynamicArray
5102  m_DynamicArray.resize(newCount, freeMemory);
5103  }
5104  else if(newCount > N && m_Count <= N)
5105  {
5106  // Growing, moving from m_StaticArray to m_DynamicArray
5107  m_DynamicArray.resize(newCount, freeMemory);
5108  if(m_Count > 0)
5109  {
5110  memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5111  }
5112  }
5113  else if(newCount <= N && m_Count > N)
5114  {
5115  // Shrinking, moving from m_DynamicArray to m_StaticArray
5116  if(newCount > 0)
5117  {
5118  memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5119  }
5120  m_DynamicArray.resize(0, freeMemory);
5121  }
5122  else
5123  {
5124  // Any direction, staying in m_StaticArray - nothing to do here
5125  }
5126  m_Count = newCount;
5127  }
5128 
5129  void clear(bool freeMemory = false)
5130  {
5131  m_DynamicArray.clear(freeMemory);
5132  m_Count = 0;
5133  }
5134 
5135  void insert(size_t index, const T& src)
5136  {
5137  VMA_HEAVY_ASSERT(index <= m_Count);
5138  const size_t oldCount = size();
5139  resize(oldCount + 1);
5140  T* const dataPtr = data();
5141  if(index < oldCount)
5142  {
5143  // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5144  memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5145  }
5146  dataPtr[index] = src;
5147  }
5148 
5149  void remove(size_t index)
5150  {
5151  VMA_HEAVY_ASSERT(index < m_Count);
5152  const size_t oldCount = size();
5153  if(index < oldCount - 1)
5154  {
5155  // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5156  T* const dataPtr = data();
5157  memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5158  }
5159  resize(oldCount - 1);
5160  }
5161 
5162  void push_back(const T& src)
5163  {
5164  const size_t newIndex = size();
5165  resize(newIndex + 1);
5166  data()[newIndex] = src;
5167  }
5168 
5169  void pop_back()
5170  {
5171  VMA_HEAVY_ASSERT(m_Count > 0);
5172  resize(size() - 1);
5173  }
5174 
5175  void push_front(const T& src)
5176  {
5177  insert(0, src);
5178  }
5179 
5180  void pop_front()
5181  {
5182  VMA_HEAVY_ASSERT(m_Count > 0);
5183  remove(0);
5184  }
5185 
5186  typedef T* iterator;
5187 
5188  iterator begin() { return data(); }
5189  iterator end() { return data() + m_Count; }
5190 
5191 private:
5192  size_t m_Count;
5193  T m_StaticArray[N]; // Used when m_Size <= N
5194  VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5195 };
5196 
5198 // class VmaPoolAllocator
5199 
5200 /*
5201 Allocator for objects of type T using a list of arrays (pools) to speed up
5202 allocation. Number of elements that can be allocated is not bounded because
5203 allocator can create multiple blocks.
5204 */
5205 template<typename T>
5206 class VmaPoolAllocator
5207 {
5208  VMA_CLASS_NO_COPY(VmaPoolAllocator)
5209 public:
5210  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5211  ~VmaPoolAllocator();
5212  template<typename... Types> T* Alloc(Types... args);
5213  void Free(T* ptr);
5214 
5215 private:
5216  union Item
5217  {
5218  uint32_t NextFreeIndex;
5219  alignas(T) char Value[sizeof(T)];
5220  };
5221 
5222  struct ItemBlock
5223  {
5224  Item* pItems;
5225  uint32_t Capacity;
5226  uint32_t FirstFreeIndex;
5227  };
5228 
5229  const VkAllocationCallbacks* m_pAllocationCallbacks;
5230  const uint32_t m_FirstBlockCapacity;
5231  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5232 
5233  ItemBlock& CreateNewBlock();
5234 };
5235 
5236 template<typename T>
5237 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5238  m_pAllocationCallbacks(pAllocationCallbacks),
5239  m_FirstBlockCapacity(firstBlockCapacity),
5240  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5241 {
5242  VMA_ASSERT(m_FirstBlockCapacity > 1);
5243 }
5244 
5245 template<typename T>
5246 VmaPoolAllocator<T>::~VmaPoolAllocator()
5247 {
5248  for(size_t i = m_ItemBlocks.size(); i--; )
5249  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5250  m_ItemBlocks.clear();
5251 }
5252 
5253 template<typename T>
5254 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5255 {
5256  for(size_t i = m_ItemBlocks.size(); i--; )
5257  {
5258  ItemBlock& block = m_ItemBlocks[i];
5259  // This block has some free items: Use first one.
5260  if(block.FirstFreeIndex != UINT32_MAX)
5261  {
5262  Item* const pItem = &block.pItems[block.FirstFreeIndex];
5263  block.FirstFreeIndex = pItem->NextFreeIndex;
5264  T* result = (T*)&pItem->Value;
5265  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5266  return result;
5267  }
5268  }
5269 
5270  // No block has free item: Create new one and use it.
5271  ItemBlock& newBlock = CreateNewBlock();
5272  Item* const pItem = &newBlock.pItems[0];
5273  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5274  T* result = (T*)&pItem->Value;
5275  new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5276  return result;
5277 }
5278 
5279 template<typename T>
5280 void VmaPoolAllocator<T>::Free(T* ptr)
5281 {
5282  // Search all memory blocks to find ptr.
5283  for(size_t i = m_ItemBlocks.size(); i--; )
5284  {
5285  ItemBlock& block = m_ItemBlocks[i];
5286 
5287  // Casting to union.
5288  Item* pItemPtr;
5289  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5290 
5291  // Check if pItemPtr is in address range of this block.
5292  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5293  {
5294  ptr->~T(); // Explicit destructor call.
5295  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5296  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5297  block.FirstFreeIndex = index;
5298  return;
5299  }
5300  }
5301  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5302 }
5303 
5304 template<typename T>
5305 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5306 {
5307  const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5308  m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5309 
5310  const ItemBlock newBlock = {
5311  vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5312  newBlockCapacity,
5313  0 };
5314 
5315  m_ItemBlocks.push_back(newBlock);
5316 
5317  // Setup singly-linked list of all free items in this block.
5318  for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5319  newBlock.pItems[i].NextFreeIndex = i + 1;
5320  newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5321  return m_ItemBlocks.back();
5322 }
5323 
5325 // class VmaRawList, VmaList
5326 
5327 #if VMA_USE_STL_LIST
5328 
5329 #define VmaList std::list
5330 
5331 #else // #if VMA_USE_STL_LIST
5332 
5333 template<typename T>
5334 struct VmaListItem
5335 {
5336  VmaListItem* pPrev;
5337  VmaListItem* pNext;
5338  T Value;
5339 };
5340 
5341 // Doubly linked list.
5342 template<typename T>
5343 class VmaRawList
5344 {
5345  VMA_CLASS_NO_COPY(VmaRawList)
5346 public:
5347  typedef VmaListItem<T> ItemType;
5348 
5349  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5350  ~VmaRawList();
5351  void Clear();
5352 
5353  size_t GetCount() const { return m_Count; }
5354  bool IsEmpty() const { return m_Count == 0; }
5355 
5356  ItemType* Front() { return m_pFront; }
5357  const ItemType* Front() const { return m_pFront; }
5358  ItemType* Back() { return m_pBack; }
5359  const ItemType* Back() const { return m_pBack; }
5360 
5361  ItemType* PushBack();
5362  ItemType* PushFront();
5363  ItemType* PushBack(const T& value);
5364  ItemType* PushFront(const T& value);
5365  void PopBack();
5366  void PopFront();
5367 
5368  // Item can be null - it means PushBack.
5369  ItemType* InsertBefore(ItemType* pItem);
5370  // Item can be null - it means PushFront.
5371  ItemType* InsertAfter(ItemType* pItem);
5372 
5373  ItemType* InsertBefore(ItemType* pItem, const T& value);
5374  ItemType* InsertAfter(ItemType* pItem, const T& value);
5375 
5376  void Remove(ItemType* pItem);
5377 
5378 private:
5379  const VkAllocationCallbacks* const m_pAllocationCallbacks;
5380  VmaPoolAllocator<ItemType> m_ItemAllocator;
5381  ItemType* m_pFront;
5382  ItemType* m_pBack;
5383  size_t m_Count;
5384 };
5385 
5386 template<typename T>
5387 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5388  m_pAllocationCallbacks(pAllocationCallbacks),
5389  m_ItemAllocator(pAllocationCallbacks, 128),
5390  m_pFront(VMA_NULL),
5391  m_pBack(VMA_NULL),
5392  m_Count(0)
5393 {
5394 }
5395 
5396 template<typename T>
5397 VmaRawList<T>::~VmaRawList()
5398 {
5399  // Intentionally not calling Clear, because that would be unnecessary
5400  // computations to return all items to m_ItemAllocator as free.
5401 }
5402 
5403 template<typename T>
5404 void VmaRawList<T>::Clear()
5405 {
5406  if(IsEmpty() == false)
5407  {
5408  ItemType* pItem = m_pBack;
5409  while(pItem != VMA_NULL)
5410  {
5411  ItemType* const pPrevItem = pItem->pPrev;
5412  m_ItemAllocator.Free(pItem);
5413  pItem = pPrevItem;
5414  }
5415  m_pFront = VMA_NULL;
5416  m_pBack = VMA_NULL;
5417  m_Count = 0;
5418  }
5419 }
5420 
5421 template<typename T>
5422 VmaListItem<T>* VmaRawList<T>::PushBack()
5423 {
5424  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5425  pNewItem->pNext = VMA_NULL;
5426  if(IsEmpty())
5427  {
5428  pNewItem->pPrev = VMA_NULL;
5429  m_pFront = pNewItem;
5430  m_pBack = pNewItem;
5431  m_Count = 1;
5432  }
5433  else
5434  {
5435  pNewItem->pPrev = m_pBack;
5436  m_pBack->pNext = pNewItem;
5437  m_pBack = pNewItem;
5438  ++m_Count;
5439  }
5440  return pNewItem;
5441 }
5442 
5443 template<typename T>
5444 VmaListItem<T>* VmaRawList<T>::PushFront()
5445 {
5446  ItemType* const pNewItem = m_ItemAllocator.Alloc();
5447  pNewItem->pPrev = VMA_NULL;
5448  if(IsEmpty())
5449  {
5450  pNewItem->pNext = VMA_NULL;
5451  m_pFront = pNewItem;
5452  m_pBack = pNewItem;
5453  m_Count = 1;
5454  }
5455  else
5456  {
5457  pNewItem->pNext = m_pFront;
5458  m_pFront->pPrev = pNewItem;
5459  m_pFront = pNewItem;
5460  ++m_Count;
5461  }
5462  return pNewItem;
5463 }
5464 
5465 template<typename T>
5466 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5467 {
5468  ItemType* const pNewItem = PushBack();
5469  pNewItem->Value = value;
5470  return pNewItem;
5471 }
5472 
5473 template<typename T>
5474 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5475 {
5476  ItemType* const pNewItem = PushFront();
5477  pNewItem->Value = value;
5478  return pNewItem;
5479 }
5480 
5481 template<typename T>
5482 void VmaRawList<T>::PopBack()
5483 {
5484  VMA_HEAVY_ASSERT(m_Count > 0);
5485  ItemType* const pBackItem = m_pBack;
5486  ItemType* const pPrevItem = pBackItem->pPrev;
5487  if(pPrevItem != VMA_NULL)
5488  {
5489  pPrevItem->pNext = VMA_NULL;
5490  }
5491  m_pBack = pPrevItem;
5492  m_ItemAllocator.Free(pBackItem);
5493  --m_Count;
5494 }
5495 
5496 template<typename T>
5497 void VmaRawList<T>::PopFront()
5498 {
5499  VMA_HEAVY_ASSERT(m_Count > 0);
5500  ItemType* const pFrontItem = m_pFront;
5501  ItemType* const pNextItem = pFrontItem->pNext;
5502  if(pNextItem != VMA_NULL)
5503  {
5504  pNextItem->pPrev = VMA_NULL;
5505  }
5506  m_pFront = pNextItem;
5507  m_ItemAllocator.Free(pFrontItem);
5508  --m_Count;
5509 }
5510 
5511 template<typename T>
5512 void VmaRawList<T>::Remove(ItemType* pItem)
5513 {
5514  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5515  VMA_HEAVY_ASSERT(m_Count > 0);
5516 
5517  if(pItem->pPrev != VMA_NULL)
5518  {
5519  pItem->pPrev->pNext = pItem->pNext;
5520  }
5521  else
5522  {
5523  VMA_HEAVY_ASSERT(m_pFront == pItem);
5524  m_pFront = pItem->pNext;
5525  }
5526 
5527  if(pItem->pNext != VMA_NULL)
5528  {
5529  pItem->pNext->pPrev = pItem->pPrev;
5530  }
5531  else
5532  {
5533  VMA_HEAVY_ASSERT(m_pBack == pItem);
5534  m_pBack = pItem->pPrev;
5535  }
5536 
5537  m_ItemAllocator.Free(pItem);
5538  --m_Count;
5539 }
5540 
5541 template<typename T>
5542 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5543 {
5544  if(pItem != VMA_NULL)
5545  {
5546  ItemType* const prevItem = pItem->pPrev;
5547  ItemType* const newItem = m_ItemAllocator.Alloc();
5548  newItem->pPrev = prevItem;
5549  newItem->pNext = pItem;
5550  pItem->pPrev = newItem;
5551  if(prevItem != VMA_NULL)
5552  {
5553  prevItem->pNext = newItem;
5554  }
5555  else
5556  {
5557  VMA_HEAVY_ASSERT(m_pFront == pItem);
5558  m_pFront = newItem;
5559  }
5560  ++m_Count;
5561  return newItem;
5562  }
5563  else
5564  return PushBack();
5565 }
5566 
5567 template<typename T>
5568 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5569 {
5570  if(pItem != VMA_NULL)
5571  {
5572  ItemType* const nextItem = pItem->pNext;
5573  ItemType* const newItem = m_ItemAllocator.Alloc();
5574  newItem->pNext = nextItem;
5575  newItem->pPrev = pItem;
5576  pItem->pNext = newItem;
5577  if(nextItem != VMA_NULL)
5578  {
5579  nextItem->pPrev = newItem;
5580  }
5581  else
5582  {
5583  VMA_HEAVY_ASSERT(m_pBack == pItem);
5584  m_pBack = newItem;
5585  }
5586  ++m_Count;
5587  return newItem;
5588  }
5589  else
5590  return PushFront();
5591 }
5592 
5593 template<typename T>
5594 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5595 {
5596  ItemType* const newItem = InsertBefore(pItem);
5597  newItem->Value = value;
5598  return newItem;
5599 }
5600 
5601 template<typename T>
5602 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5603 {
5604  ItemType* const newItem = InsertAfter(pItem);
5605  newItem->Value = value;
5606  return newItem;
5607 }
5608 
5609 template<typename T, typename AllocatorT>
5610 class VmaList
5611 {
5612  VMA_CLASS_NO_COPY(VmaList)
5613 public:
5614  class iterator
5615  {
5616  public:
5617  iterator() :
5618  m_pList(VMA_NULL),
5619  m_pItem(VMA_NULL)
5620  {
5621  }
5622 
5623  T& operator*() const
5624  {
5625  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5626  return m_pItem->Value;
5627  }
5628  T* operator->() const
5629  {
5630  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5631  return &m_pItem->Value;
5632  }
5633 
5634  iterator& operator++()
5635  {
5636  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5637  m_pItem = m_pItem->pNext;
5638  return *this;
5639  }
5640  iterator& operator--()
5641  {
5642  if(m_pItem != VMA_NULL)
5643  {
5644  m_pItem = m_pItem->pPrev;
5645  }
5646  else
5647  {
5648  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5649  m_pItem = m_pList->Back();
5650  }
5651  return *this;
5652  }
5653 
5654  iterator operator++(int)
5655  {
5656  iterator result = *this;
5657  ++*this;
5658  return result;
5659  }
5660  iterator operator--(int)
5661  {
5662  iterator result = *this;
5663  --*this;
5664  return result;
5665  }
5666 
5667  bool operator==(const iterator& rhs) const
5668  {
5669  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5670  return m_pItem == rhs.m_pItem;
5671  }
5672  bool operator!=(const iterator& rhs) const
5673  {
5674  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5675  return m_pItem != rhs.m_pItem;
5676  }
5677 
5678  private:
5679  VmaRawList<T>* m_pList;
5680  VmaListItem<T>* m_pItem;
5681 
5682  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5683  m_pList(pList),
5684  m_pItem(pItem)
5685  {
5686  }
5687 
5688  friend class VmaList<T, AllocatorT>;
5689  };
5690 
5691  class const_iterator
5692  {
5693  public:
5694  const_iterator() :
5695  m_pList(VMA_NULL),
5696  m_pItem(VMA_NULL)
5697  {
5698  }
5699 
5700  const_iterator(const iterator& src) :
5701  m_pList(src.m_pList),
5702  m_pItem(src.m_pItem)
5703  {
5704  }
5705 
5706  const T& operator*() const
5707  {
5708  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5709  return m_pItem->Value;
5710  }
5711  const T* operator->() const
5712  {
5713  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5714  return &m_pItem->Value;
5715  }
5716 
5717  const_iterator& operator++()
5718  {
5719  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5720  m_pItem = m_pItem->pNext;
5721  return *this;
5722  }
5723  const_iterator& operator--()
5724  {
5725  if(m_pItem != VMA_NULL)
5726  {
5727  m_pItem = m_pItem->pPrev;
5728  }
5729  else
5730  {
5731  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5732  m_pItem = m_pList->Back();
5733  }
5734  return *this;
5735  }
5736 
5737  const_iterator operator++(int)
5738  {
5739  const_iterator result = *this;
5740  ++*this;
5741  return result;
5742  }
5743  const_iterator operator--(int)
5744  {
5745  const_iterator result = *this;
5746  --*this;
5747  return result;
5748  }
5749 
5750  bool operator==(const const_iterator& rhs) const
5751  {
5752  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5753  return m_pItem == rhs.m_pItem;
5754  }
5755  bool operator!=(const const_iterator& rhs) const
5756  {
5757  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5758  return m_pItem != rhs.m_pItem;
5759  }
5760 
5761  private:
5762  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5763  m_pList(pList),
5764  m_pItem(pItem)
5765  {
5766  }
5767 
5768  const VmaRawList<T>* m_pList;
5769  const VmaListItem<T>* m_pItem;
5770 
5771  friend class VmaList<T, AllocatorT>;
5772  };
5773 
5774  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5775 
5776  bool empty() const { return m_RawList.IsEmpty(); }
5777  size_t size() const { return m_RawList.GetCount(); }
5778 
5779  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
5780  iterator end() { return iterator(&m_RawList, VMA_NULL); }
5781 
5782  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
5783  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5784 
5785  void clear() { m_RawList.Clear(); }
5786  void push_back(const T& value) { m_RawList.PushBack(value); }
5787  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
5788  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5789 
5790 private:
5791  VmaRawList<T> m_RawList;
5792 };
5793 
5794 #endif // #if VMA_USE_STL_LIST
5795 
5797 // class VmaMap
5798 
5799 // Unused in this version.
5800 #if 0
5801 
5802 #if VMA_USE_STL_UNORDERED_MAP
5803 
5804 #define VmaPair std::pair
5805 
5806 #define VMA_MAP_TYPE(KeyT, ValueT) \
5807  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5808 
5809 #else // #if VMA_USE_STL_UNORDERED_MAP
5810 
5811 template<typename T1, typename T2>
5812 struct VmaPair
5813 {
5814  T1 first;
5815  T2 second;
5816 
5817  VmaPair() : first(), second() { }
5818  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5819 };
5820 
5821 /* Class compatible with subset of interface of std::unordered_map.
5822 KeyT, ValueT must be POD because they will be stored in VmaVector.
5823 */
5824 template<typename KeyT, typename ValueT>
5825 class VmaMap
5826 {
5827 public:
5828  typedef VmaPair<KeyT, ValueT> PairType;
5829  typedef PairType* iterator;
5830 
5831  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5832 
5833  iterator begin() { return m_Vector.begin(); }
5834  iterator end() { return m_Vector.end(); }
5835 
5836  void insert(const PairType& pair);
5837  iterator find(const KeyT& key);
5838  void erase(iterator it);
5839 
5840 private:
5841  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5842 };
5843 
5844 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5845 
5846 template<typename FirstT, typename SecondT>
5847 struct VmaPairFirstLess
5848 {
5849  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5850  {
5851  return lhs.first < rhs.first;
5852  }
5853  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5854  {
5855  return lhs.first < rhsFirst;
5856  }
5857 };
5858 
5859 template<typename KeyT, typename ValueT>
5860 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5861 {
5862  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5863  m_Vector.data(),
5864  m_Vector.data() + m_Vector.size(),
5865  pair,
5866  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5867  VmaVectorInsert(m_Vector, indexToInsert, pair);
5868 }
5869 
5870 template<typename KeyT, typename ValueT>
5871 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5872 {
5873  PairType* it = VmaBinaryFindFirstNotLess(
5874  m_Vector.data(),
5875  m_Vector.data() + m_Vector.size(),
5876  key,
5877  VmaPairFirstLess<KeyT, ValueT>());
5878  if((it != m_Vector.end()) && (it->first == key))
5879  {
5880  return it;
5881  }
5882  else
5883  {
5884  return m_Vector.end();
5885  }
5886 }
5887 
5888 template<typename KeyT, typename ValueT>
5889 void VmaMap<KeyT, ValueT>::erase(iterator it)
5890 {
5891  VmaVectorRemove(m_Vector, it - m_Vector.begin());
5892 }
5893 
5894 #endif // #if VMA_USE_STL_UNORDERED_MAP
5895 
5896 #endif // #if 0
5897 
5899 
5900 class VmaDeviceMemoryBlock;
5901 
5902 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5903 
5904 struct VmaAllocation_T
5905 {
5906 private:
5907  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5908 
5909  enum FLAGS
5910  {
5911  FLAG_USER_DATA_STRING = 0x01,
5912  };
5913 
5914 public:
5915  enum ALLOCATION_TYPE
5916  {
5917  ALLOCATION_TYPE_NONE,
5918  ALLOCATION_TYPE_BLOCK,
5919  ALLOCATION_TYPE_DEDICATED,
5920  };
5921 
5922  /*
5923  This struct is allocated using VmaPoolAllocator.
5924  */
5925 
5926  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5927  m_Alignment{1},
5928  m_Size{0},
5929  m_pUserData{VMA_NULL},
5930  m_LastUseFrameIndex{currentFrameIndex},
5931  m_MemoryTypeIndex{0},
5932  m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
5933  m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
5934  m_MapCount{0},
5935  m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
5936  {
5937 #if VMA_STATS_STRING_ENABLED
5938  m_CreationFrameIndex = currentFrameIndex;
5939  m_BufferImageUsage = 0;
5940 #endif
5941  }
5942 
5943  ~VmaAllocation_T()
5944  {
5945  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5946 
5947  // Check if owned string was freed.
5948  VMA_ASSERT(m_pUserData == VMA_NULL);
5949  }
5950 
5951  void InitBlockAllocation(
5952  VmaDeviceMemoryBlock* block,
5953  VkDeviceSize offset,
5954  VkDeviceSize alignment,
5955  VkDeviceSize size,
5956  uint32_t memoryTypeIndex,
5957  VmaSuballocationType suballocationType,
5958  bool mapped,
5959  bool canBecomeLost)
5960  {
5961  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5962  VMA_ASSERT(block != VMA_NULL);
5963  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5964  m_Alignment = alignment;
5965  m_Size = size;
5966  m_MemoryTypeIndex = memoryTypeIndex;
5967  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5968  m_SuballocationType = (uint8_t)suballocationType;
5969  m_BlockAllocation.m_Block = block;
5970  m_BlockAllocation.m_Offset = offset;
5971  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
5972  }
5973 
5974  void InitLost()
5975  {
5976  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5977  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
5978  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5979  m_MemoryTypeIndex = 0;
5980  m_BlockAllocation.m_Block = VMA_NULL;
5981  m_BlockAllocation.m_Offset = 0;
5982  m_BlockAllocation.m_CanBecomeLost = true;
5983  }
5984 
5985  void ChangeBlockAllocation(
5986  VmaAllocator hAllocator,
5987  VmaDeviceMemoryBlock* block,
5988  VkDeviceSize offset);
5989 
5990  void ChangeOffset(VkDeviceSize newOffset);
5991 
5992  // pMappedData not null means allocation is created with MAPPED flag.
5993  void InitDedicatedAllocation(
5994  uint32_t memoryTypeIndex,
5995  VkDeviceMemory hMemory,
5996  VmaSuballocationType suballocationType,
5997  void* pMappedData,
5998  VkDeviceSize size)
5999  {
6000  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6001  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6002  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6003  m_Alignment = 0;
6004  m_Size = size;
6005  m_MemoryTypeIndex = memoryTypeIndex;
6006  m_SuballocationType = (uint8_t)suballocationType;
6007  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6008  m_DedicatedAllocation.m_hMemory = hMemory;
6009  m_DedicatedAllocation.m_pMappedData = pMappedData;
6010  }
6011 
6012  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
6013  VkDeviceSize GetAlignment() const { return m_Alignment; }
6014  VkDeviceSize GetSize() const { return m_Size; }
6015  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
6016  void* GetUserData() const { return m_pUserData; }
6017  void SetUserData(VmaAllocator hAllocator, void* pUserData);
6018  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6019 
6020  VmaDeviceMemoryBlock* GetBlock() const
6021  {
6022  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6023  return m_BlockAllocation.m_Block;
6024  }
6025  VkDeviceSize GetOffset() const;
6026  VkDeviceMemory GetMemory() const;
6027  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6028  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6029  void* GetMappedData() const;
6030  bool CanBecomeLost() const;
6031 
6032  uint32_t GetLastUseFrameIndex() const
6033  {
6034  return m_LastUseFrameIndex.load();
6035  }
6036  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6037  {
6038  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6039  }
6040  /*
6041  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6042  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6043  - Else, returns false.
6044 
6045  If hAllocation is already lost, assert - you should not call it then.
6046  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6047  */
6048  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6049 
6050  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6051  {
6052  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6053  outInfo.blockCount = 1;
6054  outInfo.allocationCount = 1;
6055  outInfo.unusedRangeCount = 0;
6056  outInfo.usedBytes = m_Size;
6057  outInfo.unusedBytes = 0;
6058  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6059  outInfo.unusedRangeSizeMin = UINT64_MAX;
6060  outInfo.unusedRangeSizeMax = 0;
6061  }
6062 
6063  void BlockAllocMap();
6064  void BlockAllocUnmap();
6065  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6066  void DedicatedAllocUnmap(VmaAllocator hAllocator);
6067 
6068 #if VMA_STATS_STRING_ENABLED
6069  uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
6070  uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6071 
6072  void InitBufferImageUsage(uint32_t bufferImageUsage)
6073  {
6074  VMA_ASSERT(m_BufferImageUsage == 0);
6075  m_BufferImageUsage = bufferImageUsage;
6076  }
6077 
6078  void PrintParameters(class VmaJsonWriter& json) const;
6079 #endif
6080 
6081 private:
6082  VkDeviceSize m_Alignment;
6083  VkDeviceSize m_Size;
6084  void* m_pUserData;
6085  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6086  uint32_t m_MemoryTypeIndex;
6087  uint8_t m_Type; // ALLOCATION_TYPE
6088  uint8_t m_SuballocationType; // VmaSuballocationType
6089  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6090  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6091  uint8_t m_MapCount;
6092  uint8_t m_Flags; // enum FLAGS
6093 
6094  // Allocation out of VmaDeviceMemoryBlock.
6095  struct BlockAllocation
6096  {
6097  VmaDeviceMemoryBlock* m_Block;
6098  VkDeviceSize m_Offset;
6099  bool m_CanBecomeLost;
6100  };
6101 
6102  // Allocation for an object that has its own private VkDeviceMemory.
6103  struct DedicatedAllocation
6104  {
6105  VkDeviceMemory m_hMemory;
6106  void* m_pMappedData; // Not null means memory is mapped.
6107  };
6108 
6109  union
6110  {
6111  // Allocation out of VmaDeviceMemoryBlock.
6112  BlockAllocation m_BlockAllocation;
6113  // Allocation for an object that has its own private VkDeviceMemory.
6114  DedicatedAllocation m_DedicatedAllocation;
6115  };
6116 
6117 #if VMA_STATS_STRING_ENABLED
6118  uint32_t m_CreationFrameIndex;
6119  uint32_t m_BufferImageUsage; // 0 if unknown.
6120 #endif
6121 
6122  void FreeUserDataString(VmaAllocator hAllocator);
6123 };
6124 
6125 /*
6126 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6127 allocated memory block or free.
6128 */
6129 struct VmaSuballocation
6130 {
6131  VkDeviceSize offset;
6132  VkDeviceSize size;
6133  VmaAllocation hAllocation;
6134  VmaSuballocationType type;
6135 };
6136 
6137 // Comparator for offsets.
6138 struct VmaSuballocationOffsetLess
6139 {
6140  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6141  {
6142  return lhs.offset < rhs.offset;
6143  }
6144 };
6145 struct VmaSuballocationOffsetGreater
6146 {
6147  bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6148  {
6149  return lhs.offset > rhs.offset;
6150  }
6151 };
6152 
6153 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6154 
6155 // Cost of one additional allocation lost, as equivalent in bytes.
6156 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6157 
6158 enum class VmaAllocationRequestType
6159 {
6160  Normal,
6161  // Used by "Linear" algorithm.
6162  UpperAddress,
6163  EndOf1st,
6164  EndOf2nd,
6165 };
6166 
6167 /*
6168 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6169 
6170 If canMakeOtherLost was false:
6171 - item points to a FREE suballocation.
6172 - itemsToMakeLostCount is 0.
6173 
6174 If canMakeOtherLost was true:
6175 - item points to first of sequence of suballocations, which are either FREE,
6176  or point to VmaAllocations that can become lost.
6177 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6178  the requested allocation to succeed.
6179 */
6180 struct VmaAllocationRequest
6181 {
6182  VkDeviceSize offset;
6183  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6184  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6185  VmaSuballocationList::iterator item;
6186  size_t itemsToMakeLostCount;
6187  void* customData;
6188  VmaAllocationRequestType type;
6189 
6190  VkDeviceSize CalcCost() const
6191  {
6192  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6193  }
6194 };
6195 
6196 /*
6197 Data structure used for bookkeeping of allocations and unused ranges of memory
6198 in a single VkDeviceMemory block.
6199 */
6200 class VmaBlockMetadata
6201 {
6202 public:
6203  VmaBlockMetadata(VmaAllocator hAllocator);
6204  virtual ~VmaBlockMetadata() { }
6205  virtual void Init(VkDeviceSize size) { m_Size = size; }
6206 
6207  // Validates all data structures inside this object. If not valid, returns false.
6208  virtual bool Validate() const = 0;
6209  VkDeviceSize GetSize() const { return m_Size; }
6210  virtual size_t GetAllocationCount() const = 0;
6211  virtual VkDeviceSize GetSumFreeSize() const = 0;
6212  virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6213  // Returns true if this block is empty - contains only single free suballocation.
6214  virtual bool IsEmpty() const = 0;
6215 
6216  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6217  // Shouldn't modify blockCount.
6218  virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6219 
6220 #if VMA_STATS_STRING_ENABLED
6221  virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6222 #endif
6223 
6224  // Tries to find a place for suballocation with given parameters inside this block.
6225  // If succeeded, fills pAllocationRequest and returns true.
6226  // If failed, returns false.
6227  virtual bool CreateAllocationRequest(
6228  uint32_t currentFrameIndex,
6229  uint32_t frameInUseCount,
6230  VkDeviceSize bufferImageGranularity,
6231  VkDeviceSize allocSize,
6232  VkDeviceSize allocAlignment,
6233  bool upperAddress,
6234  VmaSuballocationType allocType,
6235  bool canMakeOtherLost,
6236  // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6237  uint32_t strategy,
6238  VmaAllocationRequest* pAllocationRequest) = 0;
6239 
6240  virtual bool MakeRequestedAllocationsLost(
6241  uint32_t currentFrameIndex,
6242  uint32_t frameInUseCount,
6243  VmaAllocationRequest* pAllocationRequest) = 0;
6244 
6245  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6246 
6247  virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6248 
6249  // Makes actual allocation based on request. Request must already be checked and valid.
6250  virtual void Alloc(
6251  const VmaAllocationRequest& request,
6252  VmaSuballocationType type,
6253  VkDeviceSize allocSize,
6254  VmaAllocation hAllocation) = 0;
6255 
6256  // Frees suballocation assigned to given memory region.
6257  virtual void Free(const VmaAllocation allocation) = 0;
6258  virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6259 
6260 protected:
6261  const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6262 
6263 #if VMA_STATS_STRING_ENABLED
6264  void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6265  VkDeviceSize unusedBytes,
6266  size_t allocationCount,
6267  size_t unusedRangeCount) const;
6268  void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6269  VkDeviceSize offset,
6270  VmaAllocation hAllocation) const;
6271  void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6272  VkDeviceSize offset,
6273  VkDeviceSize size) const;
6274  void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6275 #endif
6276 
6277 private:
6278  VkDeviceSize m_Size;
6279  const VkAllocationCallbacks* m_pAllocationCallbacks;
6280 };
6281 
6282 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6283  VMA_ASSERT(0 && "Validation failed: " #cond); \
6284  return false; \
6285  } } while(false)
6286 
6287 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6288 {
6289  VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6290 public:
6291  VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6292  virtual ~VmaBlockMetadata_Generic();
6293  virtual void Init(VkDeviceSize size);
6294 
6295  virtual bool Validate() const;
6296  virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
6297  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6298  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6299  virtual bool IsEmpty() const;
6300 
6301  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6302  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6303 
6304 #if VMA_STATS_STRING_ENABLED
6305  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6306 #endif
6307 
6308  virtual bool CreateAllocationRequest(
6309  uint32_t currentFrameIndex,
6310  uint32_t frameInUseCount,
6311  VkDeviceSize bufferImageGranularity,
6312  VkDeviceSize allocSize,
6313  VkDeviceSize allocAlignment,
6314  bool upperAddress,
6315  VmaSuballocationType allocType,
6316  bool canMakeOtherLost,
6317  uint32_t strategy,
6318  VmaAllocationRequest* pAllocationRequest);
6319 
6320  virtual bool MakeRequestedAllocationsLost(
6321  uint32_t currentFrameIndex,
6322  uint32_t frameInUseCount,
6323  VmaAllocationRequest* pAllocationRequest);
6324 
6325  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6326 
6327  virtual VkResult CheckCorruption(const void* pBlockData);
6328 
6329  virtual void Alloc(
6330  const VmaAllocationRequest& request,
6331  VmaSuballocationType type,
6332  VkDeviceSize allocSize,
6333  VmaAllocation hAllocation);
6334 
6335  virtual void Free(const VmaAllocation allocation);
6336  virtual void FreeAtOffset(VkDeviceSize offset);
6337 
6339  // For defragmentation
6340 
6341  bool IsBufferImageGranularityConflictPossible(
6342  VkDeviceSize bufferImageGranularity,
6343  VmaSuballocationType& inOutPrevSuballocType) const;
6344 
6345 private:
6346  friend class VmaDefragmentationAlgorithm_Generic;
6347  friend class VmaDefragmentationAlgorithm_Fast;
6348 
6349  uint32_t m_FreeCount;
6350  VkDeviceSize m_SumFreeSize;
6351  VmaSuballocationList m_Suballocations;
6352  // Suballocations that are free and have size greater than certain threshold.
6353  // Sorted by size, ascending.
6354  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6355 
6356  bool ValidateFreeSuballocationList() const;
6357 
6358  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6359  // If yes, fills pOffset and returns true. If no, returns false.
6360  bool CheckAllocation(
6361  uint32_t currentFrameIndex,
6362  uint32_t frameInUseCount,
6363  VkDeviceSize bufferImageGranularity,
6364  VkDeviceSize allocSize,
6365  VkDeviceSize allocAlignment,
6366  VmaSuballocationType allocType,
6367  VmaSuballocationList::const_iterator suballocItem,
6368  bool canMakeOtherLost,
6369  VkDeviceSize* pOffset,
6370  size_t* itemsToMakeLostCount,
6371  VkDeviceSize* pSumFreeSize,
6372  VkDeviceSize* pSumItemSize) const;
6373  // Given free suballocation, it merges it with following one, which must also be free.
6374  void MergeFreeWithNext(VmaSuballocationList::iterator item);
6375  // Releases given suballocation, making it free.
6376  // Merges it with adjacent free suballocations if applicable.
6377  // Returns iterator to new free suballocation at this place.
6378  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6379  // Given free suballocation, it inserts it into sorted list of
6380  // m_FreeSuballocationsBySize if it's suitable.
6381  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6382  // Given free suballocation, it removes it from sorted list of
6383  // m_FreeSuballocationsBySize if it's suitable.
6384  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6385 };
6386 
6387 /*
6388 Allocations and their references in internal data structure look like this:
6389 
6390 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6391 
6392  0 +-------+
6393  | |
6394  | |
6395  | |
6396  +-------+
6397  | Alloc | 1st[m_1stNullItemsBeginCount]
6398  +-------+
6399  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6400  +-------+
6401  | ... |
6402  +-------+
6403  | Alloc | 1st[1st.size() - 1]
6404  +-------+
6405  | |
6406  | |
6407  | |
6408 GetSize() +-------+
6409 
6410 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6411 
6412  0 +-------+
6413  | Alloc | 2nd[0]
6414  +-------+
6415  | Alloc | 2nd[1]
6416  +-------+
6417  | ... |
6418  +-------+
6419  | Alloc | 2nd[2nd.size() - 1]
6420  +-------+
6421  | |
6422  | |
6423  | |
6424  +-------+
6425  | Alloc | 1st[m_1stNullItemsBeginCount]
6426  +-------+
6427  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6428  +-------+
6429  | ... |
6430  +-------+
6431  | Alloc | 1st[1st.size() - 1]
6432  +-------+
6433  | |
6434 GetSize() +-------+
6435 
6436 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6437 
6438  0 +-------+
6439  | |
6440  | |
6441  | |
6442  +-------+
6443  | Alloc | 1st[m_1stNullItemsBeginCount]
6444  +-------+
6445  | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6446  +-------+
6447  | ... |
6448  +-------+
6449  | Alloc | 1st[1st.size() - 1]
6450  +-------+
6451  | |
6452  | |
6453  | |
6454  +-------+
6455  | Alloc | 2nd[2nd.size() - 1]
6456  +-------+
6457  | ... |
6458  +-------+
6459  | Alloc | 2nd[1]
6460  +-------+
6461  | Alloc | 2nd[0]
6462 GetSize() +-------+
6463 
6464 */
6465 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6466 {
6467  VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6468 public:
6469  VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6470  virtual ~VmaBlockMetadata_Linear();
6471  virtual void Init(VkDeviceSize size);
6472 
6473  virtual bool Validate() const;
6474  virtual size_t GetAllocationCount() const;
6475  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6476  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6477  virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6478 
6479  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6480  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6481 
6482 #if VMA_STATS_STRING_ENABLED
6483  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6484 #endif
6485 
6486  virtual bool CreateAllocationRequest(
6487  uint32_t currentFrameIndex,
6488  uint32_t frameInUseCount,
6489  VkDeviceSize bufferImageGranularity,
6490  VkDeviceSize allocSize,
6491  VkDeviceSize allocAlignment,
6492  bool upperAddress,
6493  VmaSuballocationType allocType,
6494  bool canMakeOtherLost,
6495  uint32_t strategy,
6496  VmaAllocationRequest* pAllocationRequest);
6497 
6498  virtual bool MakeRequestedAllocationsLost(
6499  uint32_t currentFrameIndex,
6500  uint32_t frameInUseCount,
6501  VmaAllocationRequest* pAllocationRequest);
6502 
6503  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6504 
6505  virtual VkResult CheckCorruption(const void* pBlockData);
6506 
6507  virtual void Alloc(
6508  const VmaAllocationRequest& request,
6509  VmaSuballocationType type,
6510  VkDeviceSize allocSize,
6511  VmaAllocation hAllocation);
6512 
6513  virtual void Free(const VmaAllocation allocation);
6514  virtual void FreeAtOffset(VkDeviceSize offset);
6515 
6516 private:
6517  /*
6518  There are two suballocation vectors, used in ping-pong way.
6519  The one with index m_1stVectorIndex is called 1st.
6520  The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6521  2nd can be non-empty only when 1st is not empty.
6522  When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6523  */
6524  typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6525 
6526  enum SECOND_VECTOR_MODE
6527  {
6528  SECOND_VECTOR_EMPTY,
6529  /*
6530  Suballocations in 2nd vector are created later than the ones in 1st, but they
6531  all have smaller offset.
6532  */
6533  SECOND_VECTOR_RING_BUFFER,
6534  /*
6535  Suballocations in 2nd vector are upper side of double stack.
6536  They all have offsets higher than those in 1st vector.
6537  Top of this stack means smaller offsets, but higher indices in this vector.
6538  */
6539  SECOND_VECTOR_DOUBLE_STACK,
6540  };
6541 
6542  VkDeviceSize m_SumFreeSize;
6543  SuballocationVectorType m_Suballocations0, m_Suballocations1;
6544  uint32_t m_1stVectorIndex;
6545  SECOND_VECTOR_MODE m_2ndVectorMode;
6546 
6547  SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6548  SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6549  const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
6550  const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6551 
6552  // Number of items in 1st vector with hAllocation = null at the beginning.
6553  size_t m_1stNullItemsBeginCount;
6554  // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6555  size_t m_1stNullItemsMiddleCount;
6556  // Number of items in 2nd vector with hAllocation = null.
6557  size_t m_2ndNullItemsCount;
6558 
6559  bool ShouldCompact1st() const;
6560  void CleanupAfterFree();
6561 
6562  bool CreateAllocationRequest_LowerAddress(
6563  uint32_t currentFrameIndex,
6564  uint32_t frameInUseCount,
6565  VkDeviceSize bufferImageGranularity,
6566  VkDeviceSize allocSize,
6567  VkDeviceSize allocAlignment,
6568  VmaSuballocationType allocType,
6569  bool canMakeOtherLost,
6570  uint32_t strategy,
6571  VmaAllocationRequest* pAllocationRequest);
6572  bool CreateAllocationRequest_UpperAddress(
6573  uint32_t currentFrameIndex,
6574  uint32_t frameInUseCount,
6575  VkDeviceSize bufferImageGranularity,
6576  VkDeviceSize allocSize,
6577  VkDeviceSize allocAlignment,
6578  VmaSuballocationType allocType,
6579  bool canMakeOtherLost,
6580  uint32_t strategy,
6581  VmaAllocationRequest* pAllocationRequest);
6582 };
6583 
6584 /*
6585 - GetSize() is the original size of allocated memory block.
6586 - m_UsableSize is this size aligned down to a power of two.
6587  All allocations and calculations happen relative to m_UsableSize.
6588 - GetUnusableSize() is the difference between them.
6589  It is repoted as separate, unused range, not available for allocations.
6590 
6591 Node at level 0 has size = m_UsableSize.
6592 Each next level contains nodes with size 2 times smaller than current level.
6593 m_LevelCount is the maximum number of levels to use in the current object.
6594 */
6595 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6596 {
6597  VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6598 public:
6599  VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6600  virtual ~VmaBlockMetadata_Buddy();
6601  virtual void Init(VkDeviceSize size);
6602 
6603  virtual bool Validate() const;
6604  virtual size_t GetAllocationCount() const { return m_AllocationCount; }
6605  virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6606  virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6607  virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6608 
6609  virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6610  virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6611 
6612 #if VMA_STATS_STRING_ENABLED
6613  virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6614 #endif
6615 
6616  virtual bool CreateAllocationRequest(
6617  uint32_t currentFrameIndex,
6618  uint32_t frameInUseCount,
6619  VkDeviceSize bufferImageGranularity,
6620  VkDeviceSize allocSize,
6621  VkDeviceSize allocAlignment,
6622  bool upperAddress,
6623  VmaSuballocationType allocType,
6624  bool canMakeOtherLost,
6625  uint32_t strategy,
6626  VmaAllocationRequest* pAllocationRequest);
6627 
6628  virtual bool MakeRequestedAllocationsLost(
6629  uint32_t currentFrameIndex,
6630  uint32_t frameInUseCount,
6631  VmaAllocationRequest* pAllocationRequest);
6632 
6633  virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6634 
6635  virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6636 
6637  virtual void Alloc(
6638  const VmaAllocationRequest& request,
6639  VmaSuballocationType type,
6640  VkDeviceSize allocSize,
6641  VmaAllocation hAllocation);
6642 
6643  virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
6644  virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6645 
6646 private:
6647  static const VkDeviceSize MIN_NODE_SIZE = 32;
6648  static const size_t MAX_LEVELS = 30;
6649 
6650  struct ValidationContext
6651  {
6652  size_t calculatedAllocationCount;
6653  size_t calculatedFreeCount;
6654  VkDeviceSize calculatedSumFreeSize;
6655 
6656  ValidationContext() :
6657  calculatedAllocationCount(0),
6658  calculatedFreeCount(0),
6659  calculatedSumFreeSize(0) { }
6660  };
6661 
6662  struct Node
6663  {
6664  VkDeviceSize offset;
6665  enum TYPE
6666  {
6667  TYPE_FREE,
6668  TYPE_ALLOCATION,
6669  TYPE_SPLIT,
6670  TYPE_COUNT
6671  } type;
6672  Node* parent;
6673  Node* buddy;
6674 
6675  union
6676  {
6677  struct
6678  {
6679  Node* prev;
6680  Node* next;
6681  } free;
6682  struct
6683  {
6684  VmaAllocation alloc;
6685  } allocation;
6686  struct
6687  {
6688  Node* leftChild;
6689  } split;
6690  };
6691  };
6692 
6693  // Size of the memory block aligned down to a power of two.
6694  VkDeviceSize m_UsableSize;
6695  uint32_t m_LevelCount;
6696 
6697  Node* m_Root;
6698  struct {
6699  Node* front;
6700  Node* back;
6701  } m_FreeList[MAX_LEVELS];
6702  // Number of nodes in the tree with type == TYPE_ALLOCATION.
6703  size_t m_AllocationCount;
6704  // Number of nodes in the tree with type == TYPE_FREE.
6705  size_t m_FreeCount;
6706  // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6707  VkDeviceSize m_SumFreeSize;
6708 
6709  VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6710  void DeleteNode(Node* node);
6711  bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6712  uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
6713  inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6714  // Alloc passed just for validation. Can be null.
6715  void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6716  void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6717  // Adds node to the front of FreeList at given level.
6718  // node->type must be FREE.
6719  // node->free.prev, next can be undefined.
6720  void AddToFreeListFront(uint32_t level, Node* node);
6721  // Removes node from FreeList at given level.
6722  // node->type must be FREE.
6723  // node->free.prev, next stay untouched.
6724  void RemoveFromFreeList(uint32_t level, Node* node);
6725 
6726 #if VMA_STATS_STRING_ENABLED
6727  void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6728 #endif
6729 };
6730 
6731 /*
6732 Represents a single block of device memory (`VkDeviceMemory`) with all the
6733 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6734 
6735 Thread-safety: This class must be externally synchronized.
6736 */
6737 class VmaDeviceMemoryBlock
6738 {
6739  VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6740 public:
6741  VmaBlockMetadata* m_pMetadata;
6742 
6743  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6744 
6745  ~VmaDeviceMemoryBlock()
6746  {
6747  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6748  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6749  }
6750 
6751  // Always call after construction.
6752  void Init(
6753  VmaAllocator hAllocator,
6754  VmaPool hParentPool,
6755  uint32_t newMemoryTypeIndex,
6756  VkDeviceMemory newMemory,
6757  VkDeviceSize newSize,
6758  uint32_t id,
6759  uint32_t algorithm);
6760  // Always call before destruction.
6761  void Destroy(VmaAllocator allocator);
6762 
6763  VmaPool GetParentPool() const { return m_hParentPool; }
6764  VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
6765  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6766  uint32_t GetId() const { return m_Id; }
6767  void* GetMappedData() const { return m_pMappedData; }
6768 
6769  // Validates all data structures inside this object. If not valid, returns false.
6770  bool Validate() const;
6771 
6772  VkResult CheckCorruption(VmaAllocator hAllocator);
6773 
6774  // ppData can be null.
6775  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6776  void Unmap(VmaAllocator hAllocator, uint32_t count);
6777 
6778  VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6779  VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6780 
6781  VkResult BindBufferMemory(
6782  const VmaAllocator hAllocator,
6783  const VmaAllocation hAllocation,
6784  VkDeviceSize allocationLocalOffset,
6785  VkBuffer hBuffer,
6786  const void* pNext);
6787  VkResult BindImageMemory(
6788  const VmaAllocator hAllocator,
6789  const VmaAllocation hAllocation,
6790  VkDeviceSize allocationLocalOffset,
6791  VkImage hImage,
6792  const void* pNext);
6793 
6794 private:
6795  VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6796  uint32_t m_MemoryTypeIndex;
6797  uint32_t m_Id;
6798  VkDeviceMemory m_hMemory;
6799 
6800  /*
6801  Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6802  Also protects m_MapCount, m_pMappedData.
6803  Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6804  */
6805  VMA_MUTEX m_Mutex;
6806  uint32_t m_MapCount;
6807  void* m_pMappedData;
6808 };
6809 
6810 struct VmaPointerLess
6811 {
6812  bool operator()(const void* lhs, const void* rhs) const
6813  {
6814  return lhs < rhs;
6815  }
6816 };
6817 
6818 struct VmaDefragmentationMove
6819 {
6820  size_t srcBlockIndex;
6821  size_t dstBlockIndex;
6822  VkDeviceSize srcOffset;
6823  VkDeviceSize dstOffset;
6824  VkDeviceSize size;
6825  VmaAllocation hAllocation;
6826  VmaDeviceMemoryBlock* pSrcBlock;
6827  VmaDeviceMemoryBlock* pDstBlock;
6828 };
6829 
6830 class VmaDefragmentationAlgorithm;
6831 
6832 /*
6833 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6834 Vulkan memory type.
6835 
6836 Synchronized internally with a mutex.
6837 */
6838 struct VmaBlockVector
6839 {
6840  VMA_CLASS_NO_COPY(VmaBlockVector)
6841 public:
6842  VmaBlockVector(
6843  VmaAllocator hAllocator,
6844  VmaPool hParentPool,
6845  uint32_t memoryTypeIndex,
6846  VkDeviceSize preferredBlockSize,
6847  size_t minBlockCount,
6848  size_t maxBlockCount,
6849  VkDeviceSize bufferImageGranularity,
6850  uint32_t frameInUseCount,
6851  bool explicitBlockSize,
6852  uint32_t algorithm);
6853  ~VmaBlockVector();
6854 
6855  VkResult CreateMinBlocks();
6856 
6857  VmaAllocator GetAllocator() const { return m_hAllocator; }
6858  VmaPool GetParentPool() const { return m_hParentPool; }
6859  bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
6860  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
6861  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
6862  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
6863  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
6864  uint32_t GetAlgorithm() const { return m_Algorithm; }
6865 
6866  void GetPoolStats(VmaPoolStats* pStats);
6867 
6868  bool IsEmpty();
6869  bool IsCorruptionDetectionEnabled() const;
6870 
6871  VkResult Allocate(
6872  uint32_t currentFrameIndex,
6873  VkDeviceSize size,
6874  VkDeviceSize alignment,
6875  const VmaAllocationCreateInfo& createInfo,
6876  VmaSuballocationType suballocType,
6877  size_t allocationCount,
6878  VmaAllocation* pAllocations);
6879 
6880  void Free(const VmaAllocation hAllocation);
6881 
6882  // Adds statistics of this BlockVector to pStats.
6883  void AddStats(VmaStats* pStats);
6884 
6885 #if VMA_STATS_STRING_ENABLED
6886  void PrintDetailedMap(class VmaJsonWriter& json);
6887 #endif
6888 
6889  void MakePoolAllocationsLost(
6890  uint32_t currentFrameIndex,
6891  size_t* pLostAllocationCount);
6892  VkResult CheckCorruption();
6893 
6894  // Saves results in pCtx->res.
6895  void Defragment(
6896  class VmaBlockVectorDefragmentationContext* pCtx,
6898  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6899  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6900  VkCommandBuffer commandBuffer);
6901  void DefragmentationEnd(
6902  class VmaBlockVectorDefragmentationContext* pCtx,
6903  uint32_t flags,
6904  VmaDefragmentationStats* pStats);
6905 
6906  uint32_t ProcessDefragmentations(
6907  class VmaBlockVectorDefragmentationContext *pCtx,
6908  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6909 
6910  void CommitDefragmentations(
6911  class VmaBlockVectorDefragmentationContext *pCtx,
6912  VmaDefragmentationStats* pStats);
6913 
6915  // To be used only while the m_Mutex is locked. Used during defragmentation.
6916 
6917  size_t GetBlockCount() const { return m_Blocks.size(); }
6918  VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6919  size_t CalcAllocationCount() const;
6920  bool IsBufferImageGranularityConflictPossible() const;
6921 
6922 private:
6923  friend class VmaDefragmentationAlgorithm_Generic;
6924 
6925  const VmaAllocator m_hAllocator;
6926  const VmaPool m_hParentPool;
6927  const uint32_t m_MemoryTypeIndex;
6928  const VkDeviceSize m_PreferredBlockSize;
6929  const size_t m_MinBlockCount;
6930  const size_t m_MaxBlockCount;
6931  const VkDeviceSize m_BufferImageGranularity;
6932  const uint32_t m_FrameInUseCount;
6933  const bool m_ExplicitBlockSize;
6934  const uint32_t m_Algorithm;
6935  VMA_RW_MUTEX m_Mutex;
6936 
6937  /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
6938  a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
6939  bool m_HasEmptyBlock;
6940  // Incrementally sorted by sumFreeSize, ascending.
6941  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
6942  uint32_t m_NextBlockId;
6943 
6944  VkDeviceSize CalcMaxBlockSize() const;
6945 
6946  // Finds and removes given block from vector.
6947  void Remove(VmaDeviceMemoryBlock* pBlock);
6948 
6949  // Performs single step in sorting m_Blocks. They may not be fully sorted
6950  // after this call.
6951  void IncrementallySortBlocks();
6952 
6953  VkResult AllocatePage(
6954  uint32_t currentFrameIndex,
6955  VkDeviceSize size,
6956  VkDeviceSize alignment,
6957  const VmaAllocationCreateInfo& createInfo,
6958  VmaSuballocationType suballocType,
6959  VmaAllocation* pAllocation);
6960 
6961  // To be used only without CAN_MAKE_OTHER_LOST flag.
6962  VkResult AllocateFromBlock(
6963  VmaDeviceMemoryBlock* pBlock,
6964  uint32_t currentFrameIndex,
6965  VkDeviceSize size,
6966  VkDeviceSize alignment,
6967  VmaAllocationCreateFlags allocFlags,
6968  void* pUserData,
6969  VmaSuballocationType suballocType,
6970  uint32_t strategy,
6971  VmaAllocation* pAllocation);
6972 
6973  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
6974 
6975  // Saves result to pCtx->res.
6976  void ApplyDefragmentationMovesCpu(
6977  class VmaBlockVectorDefragmentationContext* pDefragCtx,
6978  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
6979  // Saves result to pCtx->res.
6980  void ApplyDefragmentationMovesGpu(
6981  class VmaBlockVectorDefragmentationContext* pDefragCtx,
6982  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6983  VkCommandBuffer commandBuffer);
6984 
6985  /*
6986  Used during defragmentation. pDefragmentationStats is optional. It's in/out
6987  - updated with new data.
6988  */
6989  void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
6990 
6991  void UpdateHasEmptyBlock();
6992 };
6993 
6994 struct VmaPool_T
6995 {
6996  VMA_CLASS_NO_COPY(VmaPool_T)
6997 public:
6998  VmaBlockVector m_BlockVector;
6999 
7000  VmaPool_T(
7001  VmaAllocator hAllocator,
7002  const VmaPoolCreateInfo& createInfo,
7003  VkDeviceSize preferredBlockSize);
7004  ~VmaPool_T();
7005 
7006  uint32_t GetId() const { return m_Id; }
7007  void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7008 
7009  const char* GetName() const { return m_Name; }
7010  void SetName(const char* pName);
7011 
7012 #if VMA_STATS_STRING_ENABLED
7013  //void PrintDetailedMap(class VmaStringBuilder& sb);
7014 #endif
7015 
7016 private:
7017  uint32_t m_Id;
7018  char* m_Name;
7019 };
7020 
7021 /*
7022 Performs defragmentation:
7023 
7024 - Updates `pBlockVector->m_pMetadata`.
7025 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7026 - Does not move actual data, only returns requested moves as `moves`.
7027 */
7028 class VmaDefragmentationAlgorithm
7029 {
7030  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7031 public:
7032  VmaDefragmentationAlgorithm(
7033  VmaAllocator hAllocator,
7034  VmaBlockVector* pBlockVector,
7035  uint32_t currentFrameIndex) :
7036  m_hAllocator(hAllocator),
7037  m_pBlockVector(pBlockVector),
7038  m_CurrentFrameIndex(currentFrameIndex)
7039  {
7040  }
7041  virtual ~VmaDefragmentationAlgorithm()
7042  {
7043  }
7044 
7045  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7046  virtual void AddAll() = 0;
7047 
7048  virtual VkResult Defragment(
7049  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7050  VkDeviceSize maxBytesToMove,
7051  uint32_t maxAllocationsToMove,
7052  VmaDefragmentationFlags flags) = 0;
7053 
7054  virtual VkDeviceSize GetBytesMoved() const = 0;
7055  virtual uint32_t GetAllocationsMoved() const = 0;
7056 
7057 protected:
7058  VmaAllocator const m_hAllocator;
7059  VmaBlockVector* const m_pBlockVector;
7060  const uint32_t m_CurrentFrameIndex;
7061 
7062  struct AllocationInfo
7063  {
7064  VmaAllocation m_hAllocation;
7065  VkBool32* m_pChanged;
7066 
7067  AllocationInfo() :
7068  m_hAllocation(VK_NULL_HANDLE),
7069  m_pChanged(VMA_NULL)
7070  {
7071  }
7072  AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7073  m_hAllocation(hAlloc),
7074  m_pChanged(pChanged)
7075  {
7076  }
7077  };
7078 };
7079 
7080 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7081 {
7082  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7083 public:
7084  VmaDefragmentationAlgorithm_Generic(
7085  VmaAllocator hAllocator,
7086  VmaBlockVector* pBlockVector,
7087  uint32_t currentFrameIndex,
7088  bool overlappingMoveSupported);
7089  virtual ~VmaDefragmentationAlgorithm_Generic();
7090 
7091  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7092  virtual void AddAll() { m_AllAllocations = true; }
7093 
7094  virtual VkResult Defragment(
7095  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7096  VkDeviceSize maxBytesToMove,
7097  uint32_t maxAllocationsToMove,
7098  VmaDefragmentationFlags flags);
7099 
7100  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7101  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7102 
7103 private:
7104  uint32_t m_AllocationCount;
7105  bool m_AllAllocations;
7106 
7107  VkDeviceSize m_BytesMoved;
7108  uint32_t m_AllocationsMoved;
7109 
7110  struct AllocationInfoSizeGreater
7111  {
7112  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7113  {
7114  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7115  }
7116  };
7117 
7118  struct AllocationInfoOffsetGreater
7119  {
7120  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7121  {
7122  return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7123  }
7124  };
7125 
7126  struct BlockInfo
7127  {
7128  size_t m_OriginalBlockIndex;
7129  VmaDeviceMemoryBlock* m_pBlock;
7130  bool m_HasNonMovableAllocations;
7131  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7132 
7133  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7134  m_OriginalBlockIndex(SIZE_MAX),
7135  m_pBlock(VMA_NULL),
7136  m_HasNonMovableAllocations(true),
7137  m_Allocations(pAllocationCallbacks)
7138  {
7139  }
7140 
7141  void CalcHasNonMovableAllocations()
7142  {
7143  const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7144  const size_t defragmentAllocCount = m_Allocations.size();
7145  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7146  }
7147 
7148  void SortAllocationsBySizeDescending()
7149  {
7150  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7151  }
7152 
7153  void SortAllocationsByOffsetDescending()
7154  {
7155  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7156  }
7157  };
7158 
7159  struct BlockPointerLess
7160  {
7161  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7162  {
7163  return pLhsBlockInfo->m_pBlock < pRhsBlock;
7164  }
7165  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7166  {
7167  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7168  }
7169  };
7170 
7171  // 1. Blocks with some non-movable allocations go first.
7172  // 2. Blocks with smaller sumFreeSize go first.
7173  struct BlockInfoCompareMoveDestination
7174  {
7175  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7176  {
7177  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7178  {
7179  return true;
7180  }
7181  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7182  {
7183  return false;
7184  }
7185  if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7186  {
7187  return true;
7188  }
7189  return false;
7190  }
7191  };
7192 
7193  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7194  BlockInfoVector m_Blocks;
7195 
7196  VkResult DefragmentRound(
7197  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7198  VkDeviceSize maxBytesToMove,
7199  uint32_t maxAllocationsToMove,
7200  bool freeOldAllocations);
7201 
7202  size_t CalcBlocksWithNonMovableCount() const;
7203 
7204  static bool MoveMakesSense(
7205  size_t dstBlockIndex, VkDeviceSize dstOffset,
7206  size_t srcBlockIndex, VkDeviceSize srcOffset);
7207 };
7208 
7209 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7210 {
7211  VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7212 public:
7213  VmaDefragmentationAlgorithm_Fast(
7214  VmaAllocator hAllocator,
7215  VmaBlockVector* pBlockVector,
7216  uint32_t currentFrameIndex,
7217  bool overlappingMoveSupported);
7218  virtual ~VmaDefragmentationAlgorithm_Fast();
7219 
7220  virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
7221  virtual void AddAll() { m_AllAllocations = true; }
7222 
7223  virtual VkResult Defragment(
7224  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7225  VkDeviceSize maxBytesToMove,
7226  uint32_t maxAllocationsToMove,
7227  VmaDefragmentationFlags flags);
7228 
7229  virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
7230  virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7231 
7232 private:
7233  struct BlockInfo
7234  {
7235  size_t origBlockIndex;
7236  };
7237 
7238  class FreeSpaceDatabase
7239  {
7240  public:
7241  FreeSpaceDatabase()
7242  {
7243  FreeSpace s = {};
7244  s.blockInfoIndex = SIZE_MAX;
7245  for(size_t i = 0; i < MAX_COUNT; ++i)
7246  {
7247  m_FreeSpaces[i] = s;
7248  }
7249  }
7250 
7251  void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7252  {
7253  if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7254  {
7255  return;
7256  }
7257 
7258  // Find first invalid or the smallest structure.
7259  size_t bestIndex = SIZE_MAX;
7260  for(size_t i = 0; i < MAX_COUNT; ++i)
7261  {
7262  // Empty structure.
7263  if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7264  {
7265  bestIndex = i;
7266  break;
7267  }
7268  if(m_FreeSpaces[i].size < size &&
7269  (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7270  {
7271  bestIndex = i;
7272  }
7273  }
7274 
7275  if(bestIndex != SIZE_MAX)
7276  {
7277  m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7278  m_FreeSpaces[bestIndex].offset = offset;
7279  m_FreeSpaces[bestIndex].size = size;
7280  }
7281  }
7282 
7283  bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7284  size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7285  {
7286  size_t bestIndex = SIZE_MAX;
7287  VkDeviceSize bestFreeSpaceAfter = 0;
7288  for(size_t i = 0; i < MAX_COUNT; ++i)
7289  {
7290  // Structure is valid.
7291  if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7292  {
7293  const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7294  // Allocation fits into this structure.
7295  if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7296  {
7297  const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7298  (dstOffset + size);
7299  if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7300  {
7301  bestIndex = i;
7302  bestFreeSpaceAfter = freeSpaceAfter;
7303  }
7304  }
7305  }
7306  }
7307 
7308  if(bestIndex != SIZE_MAX)
7309  {
7310  outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7311  outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7312 
7313  if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7314  {
7315  // Leave this structure for remaining empty space.
7316  const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7317  m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7318  m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7319  }
7320  else
7321  {
7322  // This structure becomes invalid.
7323  m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7324  }
7325 
7326  return true;
7327  }
7328 
7329  return false;
7330  }
7331 
7332  private:
7333  static const size_t MAX_COUNT = 4;
7334 
7335  struct FreeSpace
7336  {
7337  size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7338  VkDeviceSize offset;
7339  VkDeviceSize size;
7340  } m_FreeSpaces[MAX_COUNT];
7341  };
7342 
7343  const bool m_OverlappingMoveSupported;
7344 
7345  uint32_t m_AllocationCount;
7346  bool m_AllAllocations;
7347 
7348  VkDeviceSize m_BytesMoved;
7349  uint32_t m_AllocationsMoved;
7350 
7351  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7352 
7353  void PreprocessMetadata();
7354  void PostprocessMetadata();
7355  void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7356 };
7357 
7358 struct VmaBlockDefragmentationContext
7359 {
7360  enum BLOCK_FLAG
7361  {
7362  BLOCK_FLAG_USED = 0x00000001,
7363  };
7364  uint32_t flags;
7365  VkBuffer hBuffer;
7366 };
7367 
7368 class VmaBlockVectorDefragmentationContext
7369 {
7370  VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7371 public:
7372  VkResult res;
7373  bool mutexLocked;
7374  VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7375  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7376  uint32_t defragmentationMovesProcessed;
7377  uint32_t defragmentationMovesCommitted;
7378  bool hasDefragmentationPlan;
7379 
7380  VmaBlockVectorDefragmentationContext(
7381  VmaAllocator hAllocator,
7382  VmaPool hCustomPool, // Optional.
7383  VmaBlockVector* pBlockVector,
7384  uint32_t currFrameIndex);
7385  ~VmaBlockVectorDefragmentationContext();
7386 
7387  VmaPool GetCustomPool() const { return m_hCustomPool; }
7388  VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
7389  VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7390 
7391  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
7392  void AddAll() { m_AllAllocations = true; }
7393 
7394  void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7395 
7396 private:
7397  const VmaAllocator m_hAllocator;
7398  // Null if not from custom pool.
7399  const VmaPool m_hCustomPool;
7400  // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7401  VmaBlockVector* const m_pBlockVector;
7402  const uint32_t m_CurrFrameIndex;
7403  // Owner of this object.
7404  VmaDefragmentationAlgorithm* m_pAlgorithm;
7405 
7406  struct AllocInfo
7407  {
7408  VmaAllocation hAlloc;
7409  VkBool32* pChanged;
7410  };
7411  // Used between constructor and Begin.
7412  VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7413  bool m_AllAllocations;
7414 };
7415 
7416 struct VmaDefragmentationContext_T
7417 {
7418 private:
7419  VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7420 public:
7421  VmaDefragmentationContext_T(
7422  VmaAllocator hAllocator,
7423  uint32_t currFrameIndex,
7424  uint32_t flags,
7425  VmaDefragmentationStats* pStats);
7426  ~VmaDefragmentationContext_T();
7427 
7428  void AddPools(uint32_t poolCount, const VmaPool* pPools);
7429  void AddAllocations(
7430  uint32_t allocationCount,
7431  const VmaAllocation* pAllocations,
7432  VkBool32* pAllocationsChanged);
7433 
7434  /*
7435  Returns:
7436  - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7437  - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7438  - Negative value if error occured and object can be destroyed immediately.
7439  */
7440  VkResult Defragment(
7441  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7442  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7443  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7444 
7445  VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7446  VkResult DefragmentPassEnd();
7447 
7448 private:
7449  const VmaAllocator m_hAllocator;
7450  const uint32_t m_CurrFrameIndex;
7451  const uint32_t m_Flags;
7452  VmaDefragmentationStats* const m_pStats;
7453 
7454  VkDeviceSize m_MaxCpuBytesToMove;
7455  uint32_t m_MaxCpuAllocationsToMove;
7456  VkDeviceSize m_MaxGpuBytesToMove;
7457  uint32_t m_MaxGpuAllocationsToMove;
7458 
7459  // Owner of these objects.
7460  VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7461  // Owner of these objects.
7462  VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7463 };
7464 
7465 #if VMA_RECORDING_ENABLED
7466 
7467 class VmaRecorder
7468 {
7469 public:
7470  VmaRecorder();
7471  VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7472  void WriteConfiguration(
7473  const VkPhysicalDeviceProperties& devProps,
7474  const VkPhysicalDeviceMemoryProperties& memProps,
7475  uint32_t vulkanApiVersion,
7476  bool dedicatedAllocationExtensionEnabled,
7477  bool bindMemory2ExtensionEnabled,
7478  bool memoryBudgetExtensionEnabled,
7479  bool deviceCoherentMemoryExtensionEnabled);
7480  ~VmaRecorder();
7481 
7482  void RecordCreateAllocator(uint32_t frameIndex);
7483  void RecordDestroyAllocator(uint32_t frameIndex);
7484  void RecordCreatePool(uint32_t frameIndex,
7485  const VmaPoolCreateInfo& createInfo,
7486  VmaPool pool);
7487  void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7488  void RecordAllocateMemory(uint32_t frameIndex,
7489  const VkMemoryRequirements& vkMemReq,
7490  const VmaAllocationCreateInfo& createInfo,
7491  VmaAllocation allocation);
7492  void RecordAllocateMemoryPages(uint32_t frameIndex,
7493  const VkMemoryRequirements& vkMemReq,
7494  const VmaAllocationCreateInfo& createInfo,
7495  uint64_t allocationCount,
7496  const VmaAllocation* pAllocations);
7497  void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7498  const VkMemoryRequirements& vkMemReq,
7499  bool requiresDedicatedAllocation,
7500  bool prefersDedicatedAllocation,
7501  const VmaAllocationCreateInfo& createInfo,
7502  VmaAllocation allocation);
7503  void RecordAllocateMemoryForImage(uint32_t frameIndex,
7504  const VkMemoryRequirements& vkMemReq,
7505  bool requiresDedicatedAllocation,
7506  bool prefersDedicatedAllocation,
7507  const VmaAllocationCreateInfo& createInfo,
7508  VmaAllocation allocation);
7509  void RecordFreeMemory(uint32_t frameIndex,
7510  VmaAllocation allocation);
7511  void RecordFreeMemoryPages(uint32_t frameIndex,
7512  uint64_t allocationCount,
7513  const VmaAllocation* pAllocations);
7514  void RecordSetAllocationUserData(uint32_t frameIndex,
7515  VmaAllocation allocation,
7516  const void* pUserData);
7517  void RecordCreateLostAllocation(uint32_t frameIndex,
7518  VmaAllocation allocation);
7519  void RecordMapMemory(uint32_t frameIndex,
7520  VmaAllocation allocation);
7521  void RecordUnmapMemory(uint32_t frameIndex,
7522  VmaAllocation allocation);
7523  void RecordFlushAllocation(uint32_t frameIndex,
7524  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7525  void RecordInvalidateAllocation(uint32_t frameIndex,
7526  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7527  void RecordCreateBuffer(uint32_t frameIndex,
7528  const VkBufferCreateInfo& bufCreateInfo,
7529  const VmaAllocationCreateInfo& allocCreateInfo,
7530  VmaAllocation allocation);
7531  void RecordCreateImage(uint32_t frameIndex,
7532  const VkImageCreateInfo& imageCreateInfo,
7533  const VmaAllocationCreateInfo& allocCreateInfo,
7534  VmaAllocation allocation);
7535  void RecordDestroyBuffer(uint32_t frameIndex,
7536  VmaAllocation allocation);
7537  void RecordDestroyImage(uint32_t frameIndex,
7538  VmaAllocation allocation);
7539  void RecordTouchAllocation(uint32_t frameIndex,
7540  VmaAllocation allocation);
7541  void RecordGetAllocationInfo(uint32_t frameIndex,
7542  VmaAllocation allocation);
7543  void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7544  VmaPool pool);
7545  void RecordDefragmentationBegin(uint32_t frameIndex,
7546  const VmaDefragmentationInfo2& info,
7548  void RecordDefragmentationEnd(uint32_t frameIndex,
7550  void RecordSetPoolName(uint32_t frameIndex,
7551  VmaPool pool,
7552  const char* name);
7553 
7554 private:
7555  struct CallParams
7556  {
7557  uint32_t threadId;
7558  double time;
7559  };
7560 
7561  class UserDataString
7562  {
7563  public:
7564  UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
7565  const char* GetString() const { return m_Str; }
7566 
7567  private:
7568  char m_PtrStr[17];
7569  const char* m_Str;
7570  };
7571 
7572  bool m_UseMutex;
7573  VmaRecordFlags m_Flags;
7574  FILE* m_File;
7575  VMA_MUTEX m_FileMutex;
7576  std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7577 
7578  void GetBasicParams(CallParams& outParams);
7579 
7580  // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7581  template<typename T>
7582  void PrintPointerList(uint64_t count, const T* pItems)
7583  {
7584  if(count)
7585  {
7586  fprintf(m_File, "%p", pItems[0]);
7587  for(uint64_t i = 1; i < count; ++i)
7588  {
7589  fprintf(m_File, " %p", pItems[i]);
7590  }
7591  }
7592  }
7593 
7594  void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7595  void Flush();
7596 };
7597 
7598 #endif // #if VMA_RECORDING_ENABLED
7599 
7600 /*
7601 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7602 */
7603 class VmaAllocationObjectAllocator
7604 {
7605  VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7606 public:
7607  VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7608 
7609  template<typename... Types> VmaAllocation Allocate(Types... args);
7610  void Free(VmaAllocation hAlloc);
7611 
7612 private:
7613  VMA_MUTEX m_Mutex;
7614  VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7615 };
7616 
7617 struct VmaCurrentBudgetData
7618 {
7619  VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7620  VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7621 
7622 #if VMA_MEMORY_BUDGET
7623  VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7624  VMA_RW_MUTEX m_BudgetMutex;
7625  uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7626  uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7627  uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7628 #endif // #if VMA_MEMORY_BUDGET
7629 
7630  VmaCurrentBudgetData()
7631  {
7632  for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7633  {
7634  m_BlockBytes[heapIndex] = 0;
7635  m_AllocationBytes[heapIndex] = 0;
7636 #if VMA_MEMORY_BUDGET
7637  m_VulkanUsage[heapIndex] = 0;
7638  m_VulkanBudget[heapIndex] = 0;
7639  m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7640 #endif
7641  }
7642 
7643 #if VMA_MEMORY_BUDGET
7644  m_OperationsSinceBudgetFetch = 0;
7645 #endif
7646  }
7647 
7648  void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7649  {
7650  m_AllocationBytes[heapIndex] += allocationSize;
7651 #if VMA_MEMORY_BUDGET
7652  ++m_OperationsSinceBudgetFetch;
7653 #endif
7654  }
7655 
7656  void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7657  {
7658  VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7659  m_AllocationBytes[heapIndex] -= allocationSize;
7660 #if VMA_MEMORY_BUDGET
7661  ++m_OperationsSinceBudgetFetch;
7662 #endif
7663  }
7664 };
7665 
7666 // Main allocator object.
7667 struct VmaAllocator_T
7668 {
7669  VMA_CLASS_NO_COPY(VmaAllocator_T)
7670 public:
7671  bool m_UseMutex;
7672  uint32_t m_VulkanApiVersion;
7673  bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7674  bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7675  bool m_UseExtMemoryBudget;
7676  bool m_UseAmdDeviceCoherentMemory;
7677  bool m_UseKhrBufferDeviceAddress;
7678  VkDevice m_hDevice;
7679  VkInstance m_hInstance;
7680  bool m_AllocationCallbacksSpecified;
7681  VkAllocationCallbacks m_AllocationCallbacks;
7682  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7683  VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7684 
7685  // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7686  uint32_t m_HeapSizeLimitMask;
7687 
7688  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7689  VkPhysicalDeviceMemoryProperties m_MemProps;
7690 
7691  // Default pools.
7692  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7693 
7694  // Each vector is sorted by memory (handle value).
7695  typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7696  AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7697  VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7698 
7699  VmaCurrentBudgetData m_Budget;
7700 
7701  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7702  VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7703  ~VmaAllocator_T();
7704 
7705  const VkAllocationCallbacks* GetAllocationCallbacks() const
7706  {
7707  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7708  }
7709  const VmaVulkanFunctions& GetVulkanFunctions() const
7710  {
7711  return m_VulkanFunctions;
7712  }
7713 
7714  VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7715 
7716  VkDeviceSize GetBufferImageGranularity() const
7717  {
7718  return VMA_MAX(
7719  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7720  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7721  }
7722 
7723  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
7724  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7725 
7726  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7727  {
7728  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7729  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7730  }
7731  // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
7732  bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7733  {
7734  return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7735  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7736  }
7737  // Minimum alignment for all allocations in specific memory type.
7738  VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7739  {
7740  return IsMemoryTypeNonCoherent(memTypeIndex) ?
7741  VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7742  (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7743  }
7744 
7745  bool IsIntegratedGpu() const
7746  {
7747  return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7748  }
7749 
7750  uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7751 
7752 #if VMA_RECORDING_ENABLED
7753  VmaRecorder* GetRecorder() const { return m_pRecorder; }
7754 #endif
7755 
7756  void GetBufferMemoryRequirements(
7757  VkBuffer hBuffer,
7758  VkMemoryRequirements& memReq,
7759  bool& requiresDedicatedAllocation,
7760  bool& prefersDedicatedAllocation) const;
7761  void GetImageMemoryRequirements(
7762  VkImage hImage,
7763  VkMemoryRequirements& memReq,
7764  bool& requiresDedicatedAllocation,
7765  bool& prefersDedicatedAllocation) const;
7766 
7767  // Main allocation function.
7768  VkResult AllocateMemory(
7769  const VkMemoryRequirements& vkMemReq,
7770  bool requiresDedicatedAllocation,
7771  bool prefersDedicatedAllocation,
7772  VkBuffer dedicatedBuffer,
7773  VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7774  VkImage dedicatedImage,
7775  const VmaAllocationCreateInfo& createInfo,
7776  VmaSuballocationType suballocType,
7777  size_t allocationCount,
7778  VmaAllocation* pAllocations);
7779 
7780  // Main deallocation function.
7781  void FreeMemory(
7782  size_t allocationCount,
7783  const VmaAllocation* pAllocations);
7784 
7785  VkResult ResizeAllocation(
7786  const VmaAllocation alloc,
7787  VkDeviceSize newSize);
7788 
7789  void CalculateStats(VmaStats* pStats);
7790 
7791  void GetBudget(
7792  VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7793 
7794 #if VMA_STATS_STRING_ENABLED
7795  void PrintDetailedMap(class VmaJsonWriter& json);
7796 #endif
7797 
7798  VkResult DefragmentationBegin(
7799  const VmaDefragmentationInfo2& info,
7800  VmaDefragmentationStats* pStats,
7801  VmaDefragmentationContext* pContext);
7802  VkResult DefragmentationEnd(
7803  VmaDefragmentationContext context);
7804 
7805  VkResult DefragmentationPassBegin(
7807  VmaDefragmentationContext context);
7808  VkResult DefragmentationPassEnd(
7809  VmaDefragmentationContext context);
7810 
7811  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7812  bool TouchAllocation(VmaAllocation hAllocation);
7813 
7814  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7815  void DestroyPool(VmaPool pool);
7816  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7817 
7818  void SetCurrentFrameIndex(uint32_t frameIndex);
7819  uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7820 
7821  void MakePoolAllocationsLost(
7822  VmaPool hPool,
7823  size_t* pLostAllocationCount);
7824  VkResult CheckPoolCorruption(VmaPool hPool);
7825  VkResult CheckCorruption(uint32_t memoryTypeBits);
7826 
7827  void CreateLostAllocation(VmaAllocation* pAllocation);
7828 
7829  // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7830  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7831  // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7832  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7833  // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7834  VkResult BindVulkanBuffer(
7835  VkDeviceMemory memory,
7836  VkDeviceSize memoryOffset,
7837  VkBuffer buffer,
7838  const void* pNext);
7839  // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7840  VkResult BindVulkanImage(
7841  VkDeviceMemory memory,
7842  VkDeviceSize memoryOffset,
7843  VkImage image,
7844  const void* pNext);
7845 
7846  VkResult Map(VmaAllocation hAllocation, void** ppData);
7847  void Unmap(VmaAllocation hAllocation);
7848 
7849  VkResult BindBufferMemory(
7850  VmaAllocation hAllocation,
7851  VkDeviceSize allocationLocalOffset,
7852  VkBuffer hBuffer,
7853  const void* pNext);
7854  VkResult BindImageMemory(
7855  VmaAllocation hAllocation,
7856  VkDeviceSize allocationLocalOffset,
7857  VkImage hImage,
7858  const void* pNext);
7859 
7860  VkResult FlushOrInvalidateAllocation(
7861  VmaAllocation hAllocation,
7862  VkDeviceSize offset, VkDeviceSize size,
7863  VMA_CACHE_OPERATION op);
7864  VkResult FlushOrInvalidateAllocations(
7865  uint32_t allocationCount,
7866  const VmaAllocation* allocations,
7867  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7868  VMA_CACHE_OPERATION op);
7869 
7870  void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7871 
7872  /*
7873  Returns bit mask of memory types that can support defragmentation on GPU as
7874  they support creation of required buffer for copy operations.
7875  */
7876  uint32_t GetGpuDefragmentationMemoryTypeBits();
7877 
7878 private:
7879  VkDeviceSize m_PreferredLargeHeapBlockSize;
7880 
7881  VkPhysicalDevice m_PhysicalDevice;
7882  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7883  VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7884 
7885  VMA_RW_MUTEX m_PoolsMutex;
7886  // Protected by m_PoolsMutex. Sorted by pointer value.
7887  VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7888  uint32_t m_NextPoolId;
7889 
7890  VmaVulkanFunctions m_VulkanFunctions;
7891 
7892  // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7893  uint32_t m_GlobalMemoryTypeBits;
7894 
7895 #if VMA_RECORDING_ENABLED
7896  VmaRecorder* m_pRecorder;
7897 #endif
7898 
7899  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7900 
7901 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7902  void ImportVulkanFunctions_Static();
7903 #endif
7904 
7905  void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
7906 
7907 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
7908  void ImportVulkanFunctions_Dynamic();
7909 #endif
7910 
7911  void ValidateVulkanFunctions();
7912 
7913  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
7914 
7915  VkResult AllocateMemoryOfType(
7916  VkDeviceSize size,
7917  VkDeviceSize alignment,
7918  bool dedicatedAllocation,
7919  VkBuffer dedicatedBuffer,
7920  VkBufferUsageFlags dedicatedBufferUsage,
7921  VkImage dedicatedImage,
7922  const VmaAllocationCreateInfo& createInfo,
7923  uint32_t memTypeIndex,
7924  VmaSuballocationType suballocType,
7925  size_t allocationCount,
7926  VmaAllocation* pAllocations);
7927 
7928  // Helper function only to be used inside AllocateDedicatedMemory.
7929  VkResult AllocateDedicatedMemoryPage(
7930  VkDeviceSize size,
7931  VmaSuballocationType suballocType,
7932  uint32_t memTypeIndex,
7933  const VkMemoryAllocateInfo& allocInfo,
7934  bool map,
7935  bool isUserDataString,
7936  void* pUserData,
7937  VmaAllocation* pAllocation);
7938 
7939  // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
7940  VkResult AllocateDedicatedMemory(
7941  VkDeviceSize size,
7942  VmaSuballocationType suballocType,
7943  uint32_t memTypeIndex,
7944  bool withinBudget,
7945  bool map,
7946  bool isUserDataString,
7947  void* pUserData,
7948  VkBuffer dedicatedBuffer,
7949  VkBufferUsageFlags dedicatedBufferUsage,
7950  VkImage dedicatedImage,
7951  size_t allocationCount,
7952  VmaAllocation* pAllocations);
7953 
7954  void FreeDedicatedMemory(const VmaAllocation allocation);
7955 
7956  /*
7957  Calculates and returns bit mask of memory types that can support defragmentation
7958  on GPU as they support creation of required buffer for copy operations.
7959  */
7960  uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
7961 
7962  uint32_t CalculateGlobalMemoryTypeBits() const;
7963 
7964  bool GetFlushOrInvalidateRange(
7965  VmaAllocation allocation,
7966  VkDeviceSize offset, VkDeviceSize size,
7967  VkMappedMemoryRange& outRange) const;
7968 
7969 #if VMA_MEMORY_BUDGET
7970  void UpdateVulkanBudget();
7971 #endif // #if VMA_MEMORY_BUDGET
7972 };
7973 
7975 // Memory allocation #2 after VmaAllocator_T definition
7976 
7977 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7978 {
7979  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7980 }
7981 
7982 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7983 {
7984  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7985 }
7986 
7987 template<typename T>
7988 static T* VmaAllocate(VmaAllocator hAllocator)
7989 {
7990  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7991 }
7992 
7993 template<typename T>
7994 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7995 {
7996  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7997 }
7998 
7999 template<typename T>
8000 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8001 {
8002  if(ptr != VMA_NULL)
8003  {
8004  ptr->~T();
8005  VmaFree(hAllocator, ptr);
8006  }
8007 }
8008 
8009 template<typename T>
8010 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8011 {
8012  if(ptr != VMA_NULL)
8013  {
8014  for(size_t i = count; i--; )
8015  ptr[i].~T();
8016  VmaFree(hAllocator, ptr);
8017  }
8018 }
8019 
8021 // VmaStringBuilder
8022 
8023 #if VMA_STATS_STRING_ENABLED
8024 
8025 class VmaStringBuilder
8026 {
8027 public:
8028  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
8029  size_t GetLength() const { return m_Data.size(); }
8030  const char* GetData() const { return m_Data.data(); }
8031 
8032  void Add(char ch) { m_Data.push_back(ch); }
8033  void Add(const char* pStr);
8034  void AddNewLine() { Add('\n'); }
8035  void AddNumber(uint32_t num);
8036  void AddNumber(uint64_t num);
8037  void AddPointer(const void* ptr);
8038 
8039 private:
8040  VmaVector< char, VmaStlAllocator<char> > m_Data;
8041 };
8042 
8043 void VmaStringBuilder::Add(const char* pStr)
8044 {
8045  const size_t strLen = strlen(pStr);
8046  if(strLen > 0)
8047  {
8048  const size_t oldCount = m_Data.size();
8049  m_Data.resize(oldCount + strLen);
8050  memcpy(m_Data.data() + oldCount, pStr, strLen);
8051  }
8052 }
8053 
8054 void VmaStringBuilder::AddNumber(uint32_t num)
8055 {
8056  char buf[11];
8057  buf[10] = '\0';
8058  char *p = &buf[10];
8059  do
8060  {
8061  *--p = '0' + (num % 10);
8062  num /= 10;
8063  }
8064  while(num);
8065  Add(p);
8066 }
8067 
8068 void VmaStringBuilder::AddNumber(uint64_t num)
8069 {
8070  char buf[21];
8071  buf[20] = '\0';
8072  char *p = &buf[20];
8073  do
8074  {
8075  *--p = '0' + (num % 10);
8076  num /= 10;
8077  }
8078  while(num);
8079  Add(p);
8080 }
8081 
8082 void VmaStringBuilder::AddPointer(const void* ptr)
8083 {
8084  char buf[21];
8085  VmaPtrToStr(buf, sizeof(buf), ptr);
8086  Add(buf);
8087 }
8088 
8089 #endif // #if VMA_STATS_STRING_ENABLED
8090 
8092 // VmaJsonWriter
8093 
8094 #if VMA_STATS_STRING_ENABLED
8095 
8096 class VmaJsonWriter
8097 {
8098  VMA_CLASS_NO_COPY(VmaJsonWriter)
8099 public:
8100  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8101  ~VmaJsonWriter();
8102 
8103  void BeginObject(bool singleLine = false);
8104  void EndObject();
8105 
8106  void BeginArray(bool singleLine = false);
8107  void EndArray();
8108 
8109  void WriteString(const char* pStr);
8110  void BeginString(const char* pStr = VMA_NULL);
8111  void ContinueString(const char* pStr);
8112  void ContinueString(uint32_t n);
8113  void ContinueString(uint64_t n);
8114  void ContinueString_Pointer(const void* ptr);
8115  void EndString(const char* pStr = VMA_NULL);
8116 
8117  void WriteNumber(uint32_t n);
8118  void WriteNumber(uint64_t n);
8119  void WriteBool(bool b);
8120  void WriteNull();
8121 
8122 private:
8123  static const char* const INDENT;
8124 
8125  enum COLLECTION_TYPE
8126  {
8127  COLLECTION_TYPE_OBJECT,
8128  COLLECTION_TYPE_ARRAY,
8129  };
8130  struct StackItem
8131  {
8132  COLLECTION_TYPE type;
8133  uint32_t valueCount;
8134  bool singleLineMode;
8135  };
8136 
8137  VmaStringBuilder& m_SB;
8138  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8139  bool m_InsideString;
8140 
8141  void BeginValue(bool isString);
8142  void WriteIndent(bool oneLess = false);
8143 };
8144 
8145 const char* const VmaJsonWriter::INDENT = " ";
8146 
8147 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8148  m_SB(sb),
8149  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8150  m_InsideString(false)
8151 {
8152 }
8153 
8154 VmaJsonWriter::~VmaJsonWriter()
8155 {
8156  VMA_ASSERT(!m_InsideString);
8157  VMA_ASSERT(m_Stack.empty());
8158 }
8159 
8160 void VmaJsonWriter::BeginObject(bool singleLine)
8161 {
8162  VMA_ASSERT(!m_InsideString);
8163 
8164  BeginValue(false);
8165  m_SB.Add('{');
8166 
8167  StackItem item;
8168  item.type = COLLECTION_TYPE_OBJECT;
8169  item.valueCount = 0;
8170  item.singleLineMode = singleLine;
8171  m_Stack.push_back(item);
8172 }
8173 
8174 void VmaJsonWriter::EndObject()
8175 {
8176  VMA_ASSERT(!m_InsideString);
8177 
8178  WriteIndent(true);
8179  m_SB.Add('}');
8180 
8181  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8182  m_Stack.pop_back();
8183 }
8184 
8185 void VmaJsonWriter::BeginArray(bool singleLine)
8186 {
8187  VMA_ASSERT(!m_InsideString);
8188 
8189  BeginValue(false);
8190  m_SB.Add('[');
8191 
8192  StackItem item;
8193  item.type = COLLECTION_TYPE_ARRAY;
8194  item.valueCount = 0;
8195  item.singleLineMode = singleLine;
8196  m_Stack.push_back(item);
8197 }
8198 
8199 void VmaJsonWriter::EndArray()
8200 {
8201  VMA_ASSERT(!m_InsideString);
8202 
8203  WriteIndent(true);
8204  m_SB.Add(']');
8205 
8206  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8207  m_Stack.pop_back();
8208 }
8209 
8210 void VmaJsonWriter::WriteString(const char* pStr)
8211 {
8212  BeginString(pStr);
8213  EndString();
8214 }
8215 
8216 void VmaJsonWriter::BeginString(const char* pStr)
8217 {
8218  VMA_ASSERT(!m_InsideString);
8219 
8220  BeginValue(true);
8221  m_SB.Add('"');
8222  m_InsideString = true;
8223  if(pStr != VMA_NULL && pStr[0] != '\0')
8224  {
8225  ContinueString(pStr);
8226  }
8227 }
8228 
8229 void VmaJsonWriter::ContinueString(const char* pStr)
8230 {
8231  VMA_ASSERT(m_InsideString);
8232 
8233  const size_t strLen = strlen(pStr);
8234  for(size_t i = 0; i < strLen; ++i)
8235  {
8236  char ch = pStr[i];
8237  if(ch == '\\')
8238  {
8239  m_SB.Add("\\\\");
8240  }
8241  else if(ch == '"')
8242  {
8243  m_SB.Add("\\\"");
8244  }
8245  else if(ch >= 32)
8246  {
8247  m_SB.Add(ch);
8248  }
8249  else switch(ch)
8250  {
8251  case '\b':
8252  m_SB.Add("\\b");
8253  break;
8254  case '\f':
8255  m_SB.Add("\\f");
8256  break;
8257  case '\n':
8258  m_SB.Add("\\n");
8259  break;
8260  case '\r':
8261  m_SB.Add("\\r");
8262  break;
8263  case '\t':
8264  m_SB.Add("\\t");
8265  break;
8266  default:
8267  VMA_ASSERT(0 && "Character not currently supported.");
8268  break;
8269  }
8270  }
8271 }
8272 
8273 void VmaJsonWriter::ContinueString(uint32_t n)
8274 {
8275  VMA_ASSERT(m_InsideString);
8276  m_SB.AddNumber(n);
8277 }
8278 
8279 void VmaJsonWriter::ContinueString(uint64_t n)
8280 {
8281  VMA_ASSERT(m_InsideString);
8282  m_SB.AddNumber(n);
8283 }
8284 
8285 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8286 {
8287  VMA_ASSERT(m_InsideString);
8288  m_SB.AddPointer(ptr);
8289 }
8290 
8291 void VmaJsonWriter::EndString(const char* pStr)
8292 {
8293  VMA_ASSERT(m_InsideString);
8294  if(pStr != VMA_NULL && pStr[0] != '\0')
8295  {
8296  ContinueString(pStr);
8297  }
8298  m_SB.Add('"');
8299  m_InsideString = false;
8300 }
8301 
8302 void VmaJsonWriter::WriteNumber(uint32_t n)
8303 {
8304  VMA_ASSERT(!m_InsideString);
8305  BeginValue(false);
8306  m_SB.AddNumber(n);
8307 }
8308 
8309 void VmaJsonWriter::WriteNumber(uint64_t n)
8310 {
8311  VMA_ASSERT(!m_InsideString);
8312  BeginValue(false);
8313  m_SB.AddNumber(n);
8314 }
8315 
8316 void VmaJsonWriter::WriteBool(bool b)
8317 {
8318  VMA_ASSERT(!m_InsideString);
8319  BeginValue(false);
8320  m_SB.Add(b ? "true" : "false");
8321 }
8322 
8323 void VmaJsonWriter::WriteNull()
8324 {
8325  VMA_ASSERT(!m_InsideString);
8326  BeginValue(false);
8327  m_SB.Add("null");
8328 }
8329 
8330 void VmaJsonWriter::BeginValue(bool isString)
8331 {
8332  if(!m_Stack.empty())
8333  {
8334  StackItem& currItem = m_Stack.back();
8335  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8336  currItem.valueCount % 2 == 0)
8337  {
8338  VMA_ASSERT(isString);
8339  }
8340 
8341  if(currItem.type == COLLECTION_TYPE_OBJECT &&
8342  currItem.valueCount % 2 != 0)
8343  {
8344  m_SB.Add(": ");
8345  }
8346  else if(currItem.valueCount > 0)
8347  {
8348  m_SB.Add(", ");
8349  WriteIndent();
8350  }
8351  else
8352  {
8353  WriteIndent();
8354  }
8355  ++currItem.valueCount;
8356  }
8357 }
8358 
8359 void VmaJsonWriter::WriteIndent(bool oneLess)
8360 {
8361  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8362  {
8363  m_SB.AddNewLine();
8364 
8365  size_t count = m_Stack.size();
8366  if(count > 0 && oneLess)
8367  {
8368  --count;
8369  }
8370  for(size_t i = 0; i < count; ++i)
8371  {
8372  m_SB.Add(INDENT);
8373  }
8374  }
8375 }
8376 
8377 #endif // #if VMA_STATS_STRING_ENABLED
8378 
8380 
8381 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8382 {
8383  if(IsUserDataString())
8384  {
8385  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8386 
8387  FreeUserDataString(hAllocator);
8388 
8389  if(pUserData != VMA_NULL)
8390  {
8391  m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8392  }
8393  }
8394  else
8395  {
8396  m_pUserData = pUserData;
8397  }
8398 }
8399 
8400 void VmaAllocation_T::ChangeBlockAllocation(
8401  VmaAllocator hAllocator,
8402  VmaDeviceMemoryBlock* block,
8403  VkDeviceSize offset)
8404 {
8405  VMA_ASSERT(block != VMA_NULL);
8406  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8407 
8408  // Move mapping reference counter from old block to new block.
8409  if(block != m_BlockAllocation.m_Block)
8410  {
8411  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8412  if(IsPersistentMap())
8413  ++mapRefCount;
8414  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8415  block->Map(hAllocator, mapRefCount, VMA_NULL);
8416  }
8417 
8418  m_BlockAllocation.m_Block = block;
8419  m_BlockAllocation.m_Offset = offset;
8420 }
8421 
8422 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8423 {
8424  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8425  m_BlockAllocation.m_Offset = newOffset;
8426 }
8427 
8428 VkDeviceSize VmaAllocation_T::GetOffset() const
8429 {
8430  switch(m_Type)
8431  {
8432  case ALLOCATION_TYPE_BLOCK:
8433  return m_BlockAllocation.m_Offset;
8434  case ALLOCATION_TYPE_DEDICATED:
8435  return 0;
8436  default:
8437  VMA_ASSERT(0);
8438  return 0;
8439  }
8440 }
8441 
8442 VkDeviceMemory VmaAllocation_T::GetMemory() const
8443 {
8444  switch(m_Type)
8445  {
8446  case ALLOCATION_TYPE_BLOCK:
8447  return m_BlockAllocation.m_Block->GetDeviceMemory();
8448  case ALLOCATION_TYPE_DEDICATED:
8449  return m_DedicatedAllocation.m_hMemory;
8450  default:
8451  VMA_ASSERT(0);
8452  return VK_NULL_HANDLE;
8453  }
8454 }
8455 
8456 void* VmaAllocation_T::GetMappedData() const
8457 {
8458  switch(m_Type)
8459  {
8460  case ALLOCATION_TYPE_BLOCK:
8461  if(m_MapCount != 0)
8462  {
8463  void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8464  VMA_ASSERT(pBlockData != VMA_NULL);
8465  return (char*)pBlockData + m_BlockAllocation.m_Offset;
8466  }
8467  else
8468  {
8469  return VMA_NULL;
8470  }
8471  break;
8472  case ALLOCATION_TYPE_DEDICATED:
8473  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8474  return m_DedicatedAllocation.m_pMappedData;
8475  default:
8476  VMA_ASSERT(0);
8477  return VMA_NULL;
8478  }
8479 }
8480 
8481 bool VmaAllocation_T::CanBecomeLost() const
8482 {
8483  switch(m_Type)
8484  {
8485  case ALLOCATION_TYPE_BLOCK:
8486  return m_BlockAllocation.m_CanBecomeLost;
8487  case ALLOCATION_TYPE_DEDICATED:
8488  return false;
8489  default:
8490  VMA_ASSERT(0);
8491  return false;
8492  }
8493 }
8494 
8495 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8496 {
8497  VMA_ASSERT(CanBecomeLost());
8498 
8499  /*
8500  Warning: This is a carefully designed algorithm.
8501  Do not modify unless you really know what you're doing :)
8502  */
8503  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8504  for(;;)
8505  {
8506  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8507  {
8508  VMA_ASSERT(0);
8509  return false;
8510  }
8511  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8512  {
8513  return false;
8514  }
8515  else // Last use time earlier than current time.
8516  {
8517  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8518  {
8519  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8520  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8521  return true;
8522  }
8523  }
8524  }
8525 }
8526 
8527 #if VMA_STATS_STRING_ENABLED
8528 
8529 // Correspond to values of enum VmaSuballocationType.
8530 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8531  "FREE",
8532  "UNKNOWN",
8533  "BUFFER",
8534  "IMAGE_UNKNOWN",
8535  "IMAGE_LINEAR",
8536  "IMAGE_OPTIMAL",
8537 };
8538 
8539 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8540 {
8541  json.WriteString("Type");
8542  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8543 
8544  json.WriteString("Size");
8545  json.WriteNumber(m_Size);
8546 
8547  if(m_pUserData != VMA_NULL)
8548  {
8549  json.WriteString("UserData");
8550  if(IsUserDataString())
8551  {
8552  json.WriteString((const char*)m_pUserData);
8553  }
8554  else
8555  {
8556  json.BeginString();
8557  json.ContinueString_Pointer(m_pUserData);
8558  json.EndString();
8559  }
8560  }
8561 
8562  json.WriteString("CreationFrameIndex");
8563  json.WriteNumber(m_CreationFrameIndex);
8564 
8565  json.WriteString("LastUseFrameIndex");
8566  json.WriteNumber(GetLastUseFrameIndex());
8567 
8568  if(m_BufferImageUsage != 0)
8569  {
8570  json.WriteString("Usage");
8571  json.WriteNumber(m_BufferImageUsage);
8572  }
8573 }
8574 
8575 #endif
8576 
8577 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8578 {
8579  VMA_ASSERT(IsUserDataString());
8580  VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8581  m_pUserData = VMA_NULL;
8582 }
8583 
8584 void VmaAllocation_T::BlockAllocMap()
8585 {
8586  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8587 
8588  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8589  {
8590  ++m_MapCount;
8591  }
8592  else
8593  {
8594  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8595  }
8596 }
8597 
8598 void VmaAllocation_T::BlockAllocUnmap()
8599 {
8600  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8601 
8602  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8603  {
8604  --m_MapCount;
8605  }
8606  else
8607  {
8608  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8609  }
8610 }
8611 
8612 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8613 {
8614  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8615 
8616  if(m_MapCount != 0)
8617  {
8618  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8619  {
8620  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8621  *ppData = m_DedicatedAllocation.m_pMappedData;
8622  ++m_MapCount;
8623  return VK_SUCCESS;
8624  }
8625  else
8626  {
8627  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8628  return VK_ERROR_MEMORY_MAP_FAILED;
8629  }
8630  }
8631  else
8632  {
8633  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8634  hAllocator->m_hDevice,
8635  m_DedicatedAllocation.m_hMemory,
8636  0, // offset
8637  VK_WHOLE_SIZE,
8638  0, // flags
8639  ppData);
8640  if(result == VK_SUCCESS)
8641  {
8642  m_DedicatedAllocation.m_pMappedData = *ppData;
8643  m_MapCount = 1;
8644  }
8645  return result;
8646  }
8647 }
8648 
8649 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8650 {
8651  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8652 
8653  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8654  {
8655  --m_MapCount;
8656  if(m_MapCount == 0)
8657  {
8658  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8659  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8660  hAllocator->m_hDevice,
8661  m_DedicatedAllocation.m_hMemory);
8662  }
8663  }
8664  else
8665  {
8666  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8667  }
8668 }
8669 
8670 #if VMA_STATS_STRING_ENABLED
8671 
8672 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8673 {
8674  json.BeginObject();
8675 
8676  json.WriteString("Blocks");
8677  json.WriteNumber(stat.blockCount);
8678 
8679  json.WriteString("Allocations");
8680  json.WriteNumber(stat.allocationCount);
8681 
8682  json.WriteString("UnusedRanges");
8683  json.WriteNumber(stat.unusedRangeCount);
8684 
8685  json.WriteString("UsedBytes");
8686  json.WriteNumber(stat.usedBytes);
8687 
8688  json.WriteString("UnusedBytes");
8689  json.WriteNumber(stat.unusedBytes);
8690 
8691  if(stat.allocationCount > 1)
8692  {
8693  json.WriteString("AllocationSize");
8694  json.BeginObject(true);
8695  json.WriteString("Min");
8696  json.WriteNumber(stat.allocationSizeMin);
8697  json.WriteString("Avg");
8698  json.WriteNumber(stat.allocationSizeAvg);
8699  json.WriteString("Max");
8700  json.WriteNumber(stat.allocationSizeMax);
8701  json.EndObject();
8702  }
8703 
8704  if(stat.unusedRangeCount > 1)
8705  {
8706  json.WriteString("UnusedRangeSize");
8707  json.BeginObject(true);
8708  json.WriteString("Min");
8709  json.WriteNumber(stat.unusedRangeSizeMin);
8710  json.WriteString("Avg");
8711  json.WriteNumber(stat.unusedRangeSizeAvg);
8712  json.WriteString("Max");
8713  json.WriteNumber(stat.unusedRangeSizeMax);
8714  json.EndObject();
8715  }
8716 
8717  json.EndObject();
8718 }
8719 
8720 #endif // #if VMA_STATS_STRING_ENABLED
8721 
8722 struct VmaSuballocationItemSizeLess
8723 {
8724  bool operator()(
8725  const VmaSuballocationList::iterator lhs,
8726  const VmaSuballocationList::iterator rhs) const
8727  {
8728  return lhs->size < rhs->size;
8729  }
8730  bool operator()(
8731  const VmaSuballocationList::iterator lhs,
8732  VkDeviceSize rhsSize) const
8733  {
8734  return lhs->size < rhsSize;
8735  }
8736 };
8737 
8738 
8740 // class VmaBlockMetadata
8741 
8742 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8743  m_Size(0),
8744  m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8745 {
8746 }
8747 
8748 #if VMA_STATS_STRING_ENABLED
8749 
8750 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8751  VkDeviceSize unusedBytes,
8752  size_t allocationCount,
8753  size_t unusedRangeCount) const
8754 {
8755  json.BeginObject();
8756 
8757  json.WriteString("TotalBytes");
8758  json.WriteNumber(GetSize());
8759 
8760  json.WriteString("UnusedBytes");
8761  json.WriteNumber(unusedBytes);
8762 
8763  json.WriteString("Allocations");
8764  json.WriteNumber((uint64_t)allocationCount);
8765 
8766  json.WriteString("UnusedRanges");
8767  json.WriteNumber((uint64_t)unusedRangeCount);
8768 
8769  json.WriteString("Suballocations");
8770  json.BeginArray();
8771 }
8772 
8773 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8774  VkDeviceSize offset,
8775  VmaAllocation hAllocation) const
8776 {
8777  json.BeginObject(true);
8778 
8779  json.WriteString("Offset");
8780  json.WriteNumber(offset);
8781 
8782  hAllocation->PrintParameters(json);
8783 
8784  json.EndObject();
8785 }
8786 
8787 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8788  VkDeviceSize offset,
8789  VkDeviceSize size) const
8790 {
8791  json.BeginObject(true);
8792 
8793  json.WriteString("Offset");
8794  json.WriteNumber(offset);
8795 
8796  json.WriteString("Type");
8797  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8798 
8799  json.WriteString("Size");
8800  json.WriteNumber(size);
8801 
8802  json.EndObject();
8803 }
8804 
8805 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8806 {
8807  json.EndArray();
8808  json.EndObject();
8809 }
8810 
8811 #endif // #if VMA_STATS_STRING_ENABLED
8812 
8814 // class VmaBlockMetadata_Generic
8815 
8816 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8817  VmaBlockMetadata(hAllocator),
8818  m_FreeCount(0),
8819  m_SumFreeSize(0),
8820  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8821  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8822 {
8823 }
8824 
8825 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8826 {
8827 }
8828 
8829 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8830 {
8831  VmaBlockMetadata::Init(size);
8832 
8833  m_FreeCount = 1;
8834  m_SumFreeSize = size;
8835 
8836  VmaSuballocation suballoc = {};
8837  suballoc.offset = 0;
8838  suballoc.size = size;
8839  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8840  suballoc.hAllocation = VK_NULL_HANDLE;
8841 
8842  VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8843  m_Suballocations.push_back(suballoc);
8844  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8845  --suballocItem;
8846  m_FreeSuballocationsBySize.push_back(suballocItem);
8847 }
8848 
8849 bool VmaBlockMetadata_Generic::Validate() const
8850 {
8851  VMA_VALIDATE(!m_Suballocations.empty());
8852 
8853  // Expected offset of new suballocation as calculated from previous ones.
8854  VkDeviceSize calculatedOffset = 0;
8855  // Expected number of free suballocations as calculated from traversing their list.
8856  uint32_t calculatedFreeCount = 0;
8857  // Expected sum size of free suballocations as calculated from traversing their list.
8858  VkDeviceSize calculatedSumFreeSize = 0;
8859  // Expected number of free suballocations that should be registered in
8860  // m_FreeSuballocationsBySize calculated from traversing their list.
8861  size_t freeSuballocationsToRegister = 0;
8862  // True if previous visited suballocation was free.
8863  bool prevFree = false;
8864 
8865  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8866  suballocItem != m_Suballocations.cend();
8867  ++suballocItem)
8868  {
8869  const VmaSuballocation& subAlloc = *suballocItem;
8870 
8871  // Actual offset of this suballocation doesn't match expected one.
8872  VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8873 
8874  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8875  // Two adjacent free suballocations are invalid. They should be merged.
8876  VMA_VALIDATE(!prevFree || !currFree);
8877 
8878  VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8879 
8880  if(currFree)
8881  {
8882  calculatedSumFreeSize += subAlloc.size;
8883  ++calculatedFreeCount;
8884  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8885  {
8886  ++freeSuballocationsToRegister;
8887  }
8888 
8889  // Margin required between allocations - every free space must be at least that large.
8890  VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8891  }
8892  else
8893  {
8894  VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8895  VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8896 
8897  // Margin required between allocations - previous allocation must be free.
8898  VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8899  }
8900 
8901  calculatedOffset += subAlloc.size;
8902  prevFree = currFree;
8903  }
8904 
8905  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
8906  // match expected one.
8907  VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
8908 
8909  VkDeviceSize lastSize = 0;
8910  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
8911  {
8912  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
8913 
8914  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
8915  VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8916  // They must be sorted by size ascending.
8917  VMA_VALIDATE(suballocItem->size >= lastSize);
8918 
8919  lastSize = suballocItem->size;
8920  }
8921 
8922  // Check if totals match calculacted values.
8923  VMA_VALIDATE(ValidateFreeSuballocationList());
8924  VMA_VALIDATE(calculatedOffset == GetSize());
8925  VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
8926  VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
8927 
8928  return true;
8929 }
8930 
8931 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
8932 {
8933  if(!m_FreeSuballocationsBySize.empty())
8934  {
8935  return m_FreeSuballocationsBySize.back()->size;
8936  }
8937  else
8938  {
8939  return 0;
8940  }
8941 }
8942 
8943 bool VmaBlockMetadata_Generic::IsEmpty() const
8944 {
8945  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
8946 }
8947 
8948 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8949 {
8950  outInfo.blockCount = 1;
8951 
8952  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8953  outInfo.allocationCount = rangeCount - m_FreeCount;
8954  outInfo.unusedRangeCount = m_FreeCount;
8955 
8956  outInfo.unusedBytes = m_SumFreeSize;
8957  outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
8958 
8959  outInfo.allocationSizeMin = UINT64_MAX;
8960  outInfo.allocationSizeMax = 0;
8961  outInfo.unusedRangeSizeMin = UINT64_MAX;
8962  outInfo.unusedRangeSizeMax = 0;
8963 
8964  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8965  suballocItem != m_Suballocations.cend();
8966  ++suballocItem)
8967  {
8968  const VmaSuballocation& suballoc = *suballocItem;
8969  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8970  {
8971  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8972  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
8973  }
8974  else
8975  {
8976  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
8977  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
8978  }
8979  }
8980 }
8981 
8982 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8983 {
8984  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8985 
8986  inoutStats.size += GetSize();
8987  inoutStats.unusedSize += m_SumFreeSize;
8988  inoutStats.allocationCount += rangeCount - m_FreeCount;
8989  inoutStats.unusedRangeCount += m_FreeCount;
8990  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8991 }
8992 
8993 #if VMA_STATS_STRING_ENABLED
8994 
8995 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8996 {
8997  PrintDetailedMap_Begin(json,
8998  m_SumFreeSize, // unusedBytes
8999  m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9000  m_FreeCount); // unusedRangeCount
9001 
9002  size_t i = 0;
9003  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9004  suballocItem != m_Suballocations.cend();
9005  ++suballocItem, ++i)
9006  {
9007  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9008  {
9009  PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9010  }
9011  else
9012  {
9013  PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9014  }
9015  }
9016 
9017  PrintDetailedMap_End(json);
9018 }
9019 
9020 #endif // #if VMA_STATS_STRING_ENABLED
9021 
9022 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9023  uint32_t currentFrameIndex,
9024  uint32_t frameInUseCount,
9025  VkDeviceSize bufferImageGranularity,
9026  VkDeviceSize allocSize,
9027  VkDeviceSize allocAlignment,
9028  bool upperAddress,
9029  VmaSuballocationType allocType,
9030  bool canMakeOtherLost,
9031  uint32_t strategy,
9032  VmaAllocationRequest* pAllocationRequest)
9033 {
9034  VMA_ASSERT(allocSize > 0);
9035  VMA_ASSERT(!upperAddress);
9036  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9037  VMA_ASSERT(pAllocationRequest != VMA_NULL);
9038  VMA_HEAVY_ASSERT(Validate());
9039 
9040  pAllocationRequest->type = VmaAllocationRequestType::Normal;
9041 
9042  // There is not enough total free space in this block to fullfill the request: Early return.
9043  if(canMakeOtherLost == false &&
9044  m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9045  {
9046  return false;
9047  }
9048 
9049  // New algorithm, efficiently searching freeSuballocationsBySize.
9050  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9051  if(freeSuballocCount > 0)
9052  {
9054  {
9055  // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9056  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9057  m_FreeSuballocationsBySize.data(),
9058  m_FreeSuballocationsBySize.data() + freeSuballocCount,
9059  allocSize + 2 * VMA_DEBUG_MARGIN,
9060  VmaSuballocationItemSizeLess());
9061  size_t index = it - m_FreeSuballocationsBySize.data();
9062  for(; index < freeSuballocCount; ++index)
9063  {
9064  if(CheckAllocation(
9065  currentFrameIndex,
9066  frameInUseCount,
9067  bufferImageGranularity,
9068  allocSize,
9069  allocAlignment,
9070  allocType,
9071  m_FreeSuballocationsBySize[index],
9072  false, // canMakeOtherLost
9073  &pAllocationRequest->offset,
9074  &pAllocationRequest->itemsToMakeLostCount,
9075  &pAllocationRequest->sumFreeSize,
9076  &pAllocationRequest->sumItemSize))
9077  {
9078  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9079  return true;
9080  }
9081  }
9082  }
9083  else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9084  {
9085  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9086  it != m_Suballocations.end();
9087  ++it)
9088  {
9089  if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9090  currentFrameIndex,
9091  frameInUseCount,
9092  bufferImageGranularity,
9093  allocSize,
9094  allocAlignment,
9095  allocType,
9096  it,
9097  false, // canMakeOtherLost
9098  &pAllocationRequest->offset,
9099  &pAllocationRequest->itemsToMakeLostCount,
9100  &pAllocationRequest->sumFreeSize,
9101  &pAllocationRequest->sumItemSize))
9102  {
9103  pAllocationRequest->item = it;
9104  return true;
9105  }
9106  }
9107  }
9108  else // WORST_FIT, FIRST_FIT
9109  {
9110  // Search staring from biggest suballocations.
9111  for(size_t index = freeSuballocCount; index--; )
9112  {
9113  if(CheckAllocation(
9114  currentFrameIndex,
9115  frameInUseCount,
9116  bufferImageGranularity,
9117  allocSize,
9118  allocAlignment,
9119  allocType,
9120  m_FreeSuballocationsBySize[index],
9121  false, // canMakeOtherLost
9122  &pAllocationRequest->offset,
9123  &pAllocationRequest->itemsToMakeLostCount,
9124  &pAllocationRequest->sumFreeSize,
9125  &pAllocationRequest->sumItemSize))
9126  {
9127  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9128  return true;
9129  }
9130  }
9131  }
9132  }
9133 
9134  if(canMakeOtherLost)
9135  {
9136  // Brute-force algorithm. TODO: Come up with something better.
9137 
9138  bool found = false;
9139  VmaAllocationRequest tmpAllocRequest = {};
9140  tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9141  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9142  suballocIt != m_Suballocations.end();
9143  ++suballocIt)
9144  {
9145  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9146  suballocIt->hAllocation->CanBecomeLost())
9147  {
9148  if(CheckAllocation(
9149  currentFrameIndex,
9150  frameInUseCount,
9151  bufferImageGranularity,
9152  allocSize,
9153  allocAlignment,
9154  allocType,
9155  suballocIt,
9156  canMakeOtherLost,
9157  &tmpAllocRequest.offset,
9158  &tmpAllocRequest.itemsToMakeLostCount,
9159  &tmpAllocRequest.sumFreeSize,
9160  &tmpAllocRequest.sumItemSize))
9161  {
9163  {
9164  *pAllocationRequest = tmpAllocRequest;
9165  pAllocationRequest->item = suballocIt;
9166  break;
9167  }
9168  if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9169  {
9170  *pAllocationRequest = tmpAllocRequest;
9171  pAllocationRequest->item = suballocIt;
9172  found = true;
9173  }
9174  }
9175  }
9176  }
9177 
9178  return found;
9179  }
9180 
9181  return false;
9182 }
9183 
9184 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9185  uint32_t currentFrameIndex,
9186  uint32_t frameInUseCount,
9187  VmaAllocationRequest* pAllocationRequest)
9188 {
9189  VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9190 
9191  while(pAllocationRequest->itemsToMakeLostCount > 0)
9192  {
9193  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9194  {
9195  ++pAllocationRequest->item;
9196  }
9197  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9198  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9199  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9200  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9201  {
9202  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9203  --pAllocationRequest->itemsToMakeLostCount;
9204  }
9205  else
9206  {
9207  return false;
9208  }
9209  }
9210 
9211  VMA_HEAVY_ASSERT(Validate());
9212  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9213  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9214 
9215  return true;
9216 }
9217 
9218 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9219 {
9220  uint32_t lostAllocationCount = 0;
9221  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9222  it != m_Suballocations.end();
9223  ++it)
9224  {
9225  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9226  it->hAllocation->CanBecomeLost() &&
9227  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9228  {
9229  it = FreeSuballocation(it);
9230  ++lostAllocationCount;
9231  }
9232  }
9233  return lostAllocationCount;
9234 }
9235 
9236 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9237 {
9238  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9239  it != m_Suballocations.end();
9240  ++it)
9241  {
9242  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9243  {
9244  if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9245  {
9246  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9247  return VK_ERROR_VALIDATION_FAILED_EXT;
9248  }
9249  if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9250  {
9251  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9252  return VK_ERROR_VALIDATION_FAILED_EXT;
9253  }
9254  }
9255  }
9256 
9257  return VK_SUCCESS;
9258 }
9259 
9260 void VmaBlockMetadata_Generic::Alloc(
9261  const VmaAllocationRequest& request,
9262  VmaSuballocationType type,
9263  VkDeviceSize allocSize,
9264  VmaAllocation hAllocation)
9265 {
9266  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9267  VMA_ASSERT(request.item != m_Suballocations.end());
9268  VmaSuballocation& suballoc = *request.item;
9269  // Given suballocation is a free block.
9270  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9271  // Given offset is inside this suballocation.
9272  VMA_ASSERT(request.offset >= suballoc.offset);
9273  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9274  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9275  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9276 
9277  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9278  // it to become used.
9279  UnregisterFreeSuballocation(request.item);
9280 
9281  suballoc.offset = request.offset;
9282  suballoc.size = allocSize;
9283  suballoc.type = type;
9284  suballoc.hAllocation = hAllocation;
9285 
9286  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9287  if(paddingEnd)
9288  {
9289  VmaSuballocation paddingSuballoc = {};
9290  paddingSuballoc.offset = request.offset + allocSize;
9291  paddingSuballoc.size = paddingEnd;
9292  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9293  VmaSuballocationList::iterator next = request.item;
9294  ++next;
9295  const VmaSuballocationList::iterator paddingEndItem =
9296  m_Suballocations.insert(next, paddingSuballoc);
9297  RegisterFreeSuballocation(paddingEndItem);
9298  }
9299 
9300  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9301  if(paddingBegin)
9302  {
9303  VmaSuballocation paddingSuballoc = {};
9304  paddingSuballoc.offset = request.offset - paddingBegin;
9305  paddingSuballoc.size = paddingBegin;
9306  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9307  const VmaSuballocationList::iterator paddingBeginItem =
9308  m_Suballocations.insert(request.item, paddingSuballoc);
9309  RegisterFreeSuballocation(paddingBeginItem);
9310  }
9311 
9312  // Update totals.
9313  m_FreeCount = m_FreeCount - 1;
9314  if(paddingBegin > 0)
9315  {
9316  ++m_FreeCount;
9317  }
9318  if(paddingEnd > 0)
9319  {
9320  ++m_FreeCount;
9321  }
9322  m_SumFreeSize -= allocSize;
9323 }
9324 
9325 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9326 {
9327  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9328  suballocItem != m_Suballocations.end();
9329  ++suballocItem)
9330  {
9331  VmaSuballocation& suballoc = *suballocItem;
9332  if(suballoc.hAllocation == allocation)
9333  {
9334  FreeSuballocation(suballocItem);
9335  VMA_HEAVY_ASSERT(Validate());
9336  return;
9337  }
9338  }
9339  VMA_ASSERT(0 && "Not found!");
9340 }
9341 
9342 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9343 {
9344  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9345  suballocItem != m_Suballocations.end();
9346  ++suballocItem)
9347  {
9348  VmaSuballocation& suballoc = *suballocItem;
9349  if(suballoc.offset == offset)
9350  {
9351  FreeSuballocation(suballocItem);
9352  return;
9353  }
9354  }
9355  VMA_ASSERT(0 && "Not found!");
9356 }
9357 
9358 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9359 {
9360  VkDeviceSize lastSize = 0;
9361  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9362  {
9363  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9364 
9365  VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9366  VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9367  VMA_VALIDATE(it->size >= lastSize);
9368  lastSize = it->size;
9369  }
9370  return true;
9371 }
9372 
9373 bool VmaBlockMetadata_Generic::CheckAllocation(
9374  uint32_t currentFrameIndex,
9375  uint32_t frameInUseCount,
9376  VkDeviceSize bufferImageGranularity,
9377  VkDeviceSize allocSize,
9378  VkDeviceSize allocAlignment,
9379  VmaSuballocationType allocType,
9380  VmaSuballocationList::const_iterator suballocItem,
9381  bool canMakeOtherLost,
9382  VkDeviceSize* pOffset,
9383  size_t* itemsToMakeLostCount,
9384  VkDeviceSize* pSumFreeSize,
9385  VkDeviceSize* pSumItemSize) const
9386 {
9387  VMA_ASSERT(allocSize > 0);
9388  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9389  VMA_ASSERT(suballocItem != m_Suballocations.cend());
9390  VMA_ASSERT(pOffset != VMA_NULL);
9391 
9392  *itemsToMakeLostCount = 0;
9393  *pSumFreeSize = 0;
9394  *pSumItemSize = 0;
9395 
9396  if(canMakeOtherLost)
9397  {
9398  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9399  {
9400  *pSumFreeSize = suballocItem->size;
9401  }
9402  else
9403  {
9404  if(suballocItem->hAllocation->CanBecomeLost() &&
9405  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9406  {
9407  ++*itemsToMakeLostCount;
9408  *pSumItemSize = suballocItem->size;
9409  }
9410  else
9411  {
9412  return false;
9413  }
9414  }
9415 
9416  // Remaining size is too small for this request: Early return.
9417  if(GetSize() - suballocItem->offset < allocSize)
9418  {
9419  return false;
9420  }
9421 
9422  // Start from offset equal to beginning of this suballocation.
9423  *pOffset = suballocItem->offset;
9424 
9425  // Apply VMA_DEBUG_MARGIN at the beginning.
9426  if(VMA_DEBUG_MARGIN > 0)
9427  {
9428  *pOffset += VMA_DEBUG_MARGIN;
9429  }
9430 
9431  // Apply alignment.
9432  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9433 
9434  // Check previous suballocations for BufferImageGranularity conflicts.
9435  // Make bigger alignment if necessary.
9436  if(bufferImageGranularity > 1)
9437  {
9438  bool bufferImageGranularityConflict = false;
9439  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9440  while(prevSuballocItem != m_Suballocations.cbegin())
9441  {
9442  --prevSuballocItem;
9443  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9444  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9445  {
9446  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9447  {
9448  bufferImageGranularityConflict = true;
9449  break;
9450  }
9451  }
9452  else
9453  // Already on previous page.
9454  break;
9455  }
9456  if(bufferImageGranularityConflict)
9457  {
9458  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9459  }
9460  }
9461 
9462  // Now that we have final *pOffset, check if we are past suballocItem.
9463  // If yes, return false - this function should be called for another suballocItem as starting point.
9464  if(*pOffset >= suballocItem->offset + suballocItem->size)
9465  {
9466  return false;
9467  }
9468 
9469  // Calculate padding at the beginning based on current offset.
9470  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9471 
9472  // Calculate required margin at the end.
9473  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9474 
9475  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9476  // Another early return check.
9477  if(suballocItem->offset + totalSize > GetSize())
9478  {
9479  return false;
9480  }
9481 
9482  // Advance lastSuballocItem until desired size is reached.
9483  // Update itemsToMakeLostCount.
9484  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9485  if(totalSize > suballocItem->size)
9486  {
9487  VkDeviceSize remainingSize = totalSize - suballocItem->size;
9488  while(remainingSize > 0)
9489  {
9490  ++lastSuballocItem;
9491  if(lastSuballocItem == m_Suballocations.cend())
9492  {
9493  return false;
9494  }
9495  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9496  {
9497  *pSumFreeSize += lastSuballocItem->size;
9498  }
9499  else
9500  {
9501  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9502  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9503  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9504  {
9505  ++*itemsToMakeLostCount;
9506  *pSumItemSize += lastSuballocItem->size;
9507  }
9508  else
9509  {
9510  return false;
9511  }
9512  }
9513  remainingSize = (lastSuballocItem->size < remainingSize) ?
9514  remainingSize - lastSuballocItem->size : 0;
9515  }
9516  }
9517 
9518  // Check next suballocations for BufferImageGranularity conflicts.
9519  // If conflict exists, we must mark more allocations lost or fail.
9520  if(bufferImageGranularity > 1)
9521  {
9522  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9523  ++nextSuballocItem;
9524  while(nextSuballocItem != m_Suballocations.cend())
9525  {
9526  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9527  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9528  {
9529  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9530  {
9531  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9532  if(nextSuballoc.hAllocation->CanBecomeLost() &&
9533  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9534  {
9535  ++*itemsToMakeLostCount;
9536  }
9537  else
9538  {
9539  return false;
9540  }
9541  }
9542  }
9543  else
9544  {
9545  // Already on next page.
9546  break;
9547  }
9548  ++nextSuballocItem;
9549  }
9550  }
9551  }
9552  else
9553  {
9554  const VmaSuballocation& suballoc = *suballocItem;
9555  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9556 
9557  *pSumFreeSize = suballoc.size;
9558 
9559  // Size of this suballocation is too small for this request: Early return.
9560  if(suballoc.size < allocSize)
9561  {
9562  return false;
9563  }
9564 
9565  // Start from offset equal to beginning of this suballocation.
9566  *pOffset = suballoc.offset;
9567 
9568  // Apply VMA_DEBUG_MARGIN at the beginning.
9569  if(VMA_DEBUG_MARGIN > 0)
9570  {
9571  *pOffset += VMA_DEBUG_MARGIN;
9572  }
9573 
9574  // Apply alignment.
9575  *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9576 
9577  // Check previous suballocations for BufferImageGranularity conflicts.
9578  // Make bigger alignment if necessary.
9579  if(bufferImageGranularity > 1)
9580  {
9581  bool bufferImageGranularityConflict = false;
9582  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9583  while(prevSuballocItem != m_Suballocations.cbegin())
9584  {
9585  --prevSuballocItem;
9586  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9587  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9588  {
9589  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9590  {
9591  bufferImageGranularityConflict = true;
9592  break;
9593  }
9594  }
9595  else
9596  // Already on previous page.
9597  break;
9598  }
9599  if(bufferImageGranularityConflict)
9600  {
9601  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9602  }
9603  }
9604 
9605  // Calculate padding at the beginning based on current offset.
9606  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9607 
9608  // Calculate required margin at the end.
9609  const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9610 
9611  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9612  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9613  {
9614  return false;
9615  }
9616 
9617  // Check next suballocations for BufferImageGranularity conflicts.
9618  // If conflict exists, allocation cannot be made here.
9619  if(bufferImageGranularity > 1)
9620  {
9621  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9622  ++nextSuballocItem;
9623  while(nextSuballocItem != m_Suballocations.cend())
9624  {
9625  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9626  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9627  {
9628  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9629  {
9630  return false;
9631  }
9632  }
9633  else
9634  {
9635  // Already on next page.
9636  break;
9637  }
9638  ++nextSuballocItem;
9639  }
9640  }
9641  }
9642 
9643  // All tests passed: Success. pOffset is already filled.
9644  return true;
9645 }
9646 
9647 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9648 {
9649  VMA_ASSERT(item != m_Suballocations.end());
9650  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9651 
9652  VmaSuballocationList::iterator nextItem = item;
9653  ++nextItem;
9654  VMA_ASSERT(nextItem != m_Suballocations.end());
9655  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9656 
9657  item->size += nextItem->size;
9658  --m_FreeCount;
9659  m_Suballocations.erase(nextItem);
9660 }
9661 
9662 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9663 {
9664  // Change this suballocation to be marked as free.
9665  VmaSuballocation& suballoc = *suballocItem;
9666  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9667  suballoc.hAllocation = VK_NULL_HANDLE;
9668 
9669  // Update totals.
9670  ++m_FreeCount;
9671  m_SumFreeSize += suballoc.size;
9672 
9673  // Merge with previous and/or next suballocation if it's also free.
9674  bool mergeWithNext = false;
9675  bool mergeWithPrev = false;
9676 
9677  VmaSuballocationList::iterator nextItem = suballocItem;
9678  ++nextItem;
9679  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9680  {
9681  mergeWithNext = true;
9682  }
9683 
9684  VmaSuballocationList::iterator prevItem = suballocItem;
9685  if(suballocItem != m_Suballocations.begin())
9686  {
9687  --prevItem;
9688  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9689  {
9690  mergeWithPrev = true;
9691  }
9692  }
9693 
9694  if(mergeWithNext)
9695  {
9696  UnregisterFreeSuballocation(nextItem);
9697  MergeFreeWithNext(suballocItem);
9698  }
9699 
9700  if(mergeWithPrev)
9701  {
9702  UnregisterFreeSuballocation(prevItem);
9703  MergeFreeWithNext(prevItem);
9704  RegisterFreeSuballocation(prevItem);
9705  return prevItem;
9706  }
9707  else
9708  {
9709  RegisterFreeSuballocation(suballocItem);
9710  return suballocItem;
9711  }
9712 }
9713 
9714 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9715 {
9716  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9717  VMA_ASSERT(item->size > 0);
9718 
9719  // You may want to enable this validation at the beginning or at the end of
9720  // this function, depending on what do you want to check.
9721  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9722 
9723  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9724  {
9725  if(m_FreeSuballocationsBySize.empty())
9726  {
9727  m_FreeSuballocationsBySize.push_back(item);
9728  }
9729  else
9730  {
9731  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9732  }
9733  }
9734 
9735  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9736 }
9737 
9738 
9739 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9740 {
9741  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9742  VMA_ASSERT(item->size > 0);
9743 
9744  // You may want to enable this validation at the beginning or at the end of
9745  // this function, depending on what do you want to check.
9746  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9747 
9748  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9749  {
9750  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9751  m_FreeSuballocationsBySize.data(),
9752  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9753  item,
9754  VmaSuballocationItemSizeLess());
9755  for(size_t index = it - m_FreeSuballocationsBySize.data();
9756  index < m_FreeSuballocationsBySize.size();
9757  ++index)
9758  {
9759  if(m_FreeSuballocationsBySize[index] == item)
9760  {
9761  VmaVectorRemove(m_FreeSuballocationsBySize, index);
9762  return;
9763  }
9764  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9765  }
9766  VMA_ASSERT(0 && "Not found.");
9767  }
9768 
9769  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9770 }
9771 
9772 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9773  VkDeviceSize bufferImageGranularity,
9774  VmaSuballocationType& inOutPrevSuballocType) const
9775 {
9776  if(bufferImageGranularity == 1 || IsEmpty())
9777  {
9778  return false;
9779  }
9780 
9781  VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9782  bool typeConflictFound = false;
9783  for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9784  it != m_Suballocations.cend();
9785  ++it)
9786  {
9787  const VmaSuballocationType suballocType = it->type;
9788  if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9789  {
9790  minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9791  if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9792  {
9793  typeConflictFound = true;
9794  }
9795  inOutPrevSuballocType = suballocType;
9796  }
9797  }
9798 
9799  return typeConflictFound || minAlignment >= bufferImageGranularity;
9800 }
9801 
9803 // class VmaBlockMetadata_Linear
9804 
9805 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9806  VmaBlockMetadata(hAllocator),
9807  m_SumFreeSize(0),
9808  m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9809  m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9810  m_1stVectorIndex(0),
9811  m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9812  m_1stNullItemsBeginCount(0),
9813  m_1stNullItemsMiddleCount(0),
9814  m_2ndNullItemsCount(0)
9815 {
9816 }
9817 
9818 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9819 {
9820 }
9821 
9822 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9823 {
9824  VmaBlockMetadata::Init(size);
9825  m_SumFreeSize = size;
9826 }
9827 
9828 bool VmaBlockMetadata_Linear::Validate() const
9829 {
9830  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9831  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9832 
9833  VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9834  VMA_VALIDATE(!suballocations1st.empty() ||
9835  suballocations2nd.empty() ||
9836  m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9837 
9838  if(!suballocations1st.empty())
9839  {
9840  // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9841  VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9842  // Null item at the end should be just pop_back().
9843  VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9844  }
9845  if(!suballocations2nd.empty())
9846  {
9847  // Null item at the end should be just pop_back().
9848  VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9849  }
9850 
9851  VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9852  VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9853 
9854  VkDeviceSize sumUsedSize = 0;
9855  const size_t suballoc1stCount = suballocations1st.size();
9856  VkDeviceSize offset = VMA_DEBUG_MARGIN;
9857 
9858  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9859  {
9860  const size_t suballoc2ndCount = suballocations2nd.size();
9861  size_t nullItem2ndCount = 0;
9862  for(size_t i = 0; i < suballoc2ndCount; ++i)
9863  {
9864  const VmaSuballocation& suballoc = suballocations2nd[i];
9865  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9866 
9867  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9868  VMA_VALIDATE(suballoc.offset >= offset);
9869 
9870  if(!currFree)
9871  {
9872  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9873  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9874  sumUsedSize += suballoc.size;
9875  }
9876  else
9877  {
9878  ++nullItem2ndCount;
9879  }
9880 
9881  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9882  }
9883 
9884  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9885  }
9886 
9887  for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9888  {
9889  const VmaSuballocation& suballoc = suballocations1st[i];
9890  VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9891  suballoc.hAllocation == VK_NULL_HANDLE);
9892  }
9893 
9894  size_t nullItem1stCount = m_1stNullItemsBeginCount;
9895 
9896  for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9897  {
9898  const VmaSuballocation& suballoc = suballocations1st[i];
9899  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9900 
9901  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9902  VMA_VALIDATE(suballoc.offset >= offset);
9903  VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9904 
9905  if(!currFree)
9906  {
9907  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9908  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9909  sumUsedSize += suballoc.size;
9910  }
9911  else
9912  {
9913  ++nullItem1stCount;
9914  }
9915 
9916  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9917  }
9918  VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
9919 
9920  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9921  {
9922  const size_t suballoc2ndCount = suballocations2nd.size();
9923  size_t nullItem2ndCount = 0;
9924  for(size_t i = suballoc2ndCount; i--; )
9925  {
9926  const VmaSuballocation& suballoc = suballocations2nd[i];
9927  const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9928 
9929  VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9930  VMA_VALIDATE(suballoc.offset >= offset);
9931 
9932  if(!currFree)
9933  {
9934  VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9935  VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9936  sumUsedSize += suballoc.size;
9937  }
9938  else
9939  {
9940  ++nullItem2ndCount;
9941  }
9942 
9943  offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9944  }
9945 
9946  VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9947  }
9948 
9949  VMA_VALIDATE(offset <= GetSize());
9950  VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9951 
9952  return true;
9953 }
9954 
9955 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9956 {
9957  return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
9958  AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9959 }
9960 
9961 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9962 {
9963  const VkDeviceSize size = GetSize();
9964 
9965  /*
9966  We don't consider gaps inside allocation vectors with freed allocations because
9967  they are not suitable for reuse in linear allocator. We consider only space that
9968  is available for new allocations.
9969  */
9970  if(IsEmpty())
9971  {
9972  return size;
9973  }
9974 
9975  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9976 
9977  switch(m_2ndVectorMode)
9978  {
9979  case SECOND_VECTOR_EMPTY:
9980  /*
9981  Available space is after end of 1st, as well as before beginning of 1st (which
9982  whould make it a ring buffer).
9983  */
9984  {
9985  const size_t suballocations1stCount = suballocations1st.size();
9986  VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9987  const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9988  const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
9989  return VMA_MAX(
9990  firstSuballoc.offset,
9991  size - (lastSuballoc.offset + lastSuballoc.size));
9992  }
9993  break;
9994 
9995  case SECOND_VECTOR_RING_BUFFER:
9996  /*
9997  Available space is only between end of 2nd and beginning of 1st.
9998  */
9999  {
10000  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10001  const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10002  const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10003  return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10004  }
10005  break;
10006 
10007  case SECOND_VECTOR_DOUBLE_STACK:
10008  /*
10009  Available space is only between end of 1st and top of 2nd.
10010  */
10011  {
10012  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10013  const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10014  const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10015  return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10016  }
10017  break;
10018 
10019  default:
10020  VMA_ASSERT(0);
10021  return 0;
10022  }
10023 }
10024 
10025 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10026 {
10027  const VkDeviceSize size = GetSize();
10028  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10029  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10030  const size_t suballoc1stCount = suballocations1st.size();
10031  const size_t suballoc2ndCount = suballocations2nd.size();
10032 
10033  outInfo.blockCount = 1;
10034  outInfo.allocationCount = (uint32_t)GetAllocationCount();
10035  outInfo.unusedRangeCount = 0;
10036  outInfo.usedBytes = 0;
10037  outInfo.allocationSizeMin = UINT64_MAX;
10038  outInfo.allocationSizeMax = 0;
10039  outInfo.unusedRangeSizeMin = UINT64_MAX;
10040  outInfo.unusedRangeSizeMax = 0;
10041 
10042  VkDeviceSize lastOffset = 0;
10043 
10044  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10045  {
10046  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10047  size_t nextAlloc2ndIndex = 0;
10048  while(lastOffset < freeSpace2ndTo1stEnd)
10049  {
10050  // Find next non-null allocation or move nextAllocIndex to the end.
10051  while(nextAlloc2ndIndex < suballoc2ndCount &&
10052  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10053  {
10054  ++nextAlloc2ndIndex;
10055  }
10056 
10057  // Found non-null allocation.
10058  if(nextAlloc2ndIndex < suballoc2ndCount)
10059  {
10060  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10061 
10062  // 1. Process free space before this allocation.
10063  if(lastOffset < suballoc.offset)
10064  {
10065  // There is free space from lastOffset to suballoc.offset.
10066  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10067  ++outInfo.unusedRangeCount;
10068  outInfo.unusedBytes += unusedRangeSize;
10069  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10070  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10071  }
10072 
10073  // 2. Process this allocation.
10074  // There is allocation with suballoc.offset, suballoc.size.
10075  outInfo.usedBytes += suballoc.size;
10076  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10077  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10078 
10079  // 3. Prepare for next iteration.
10080  lastOffset = suballoc.offset + suballoc.size;
10081  ++nextAlloc2ndIndex;
10082  }
10083  // We are at the end.
10084  else
10085  {
10086  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10087  if(lastOffset < freeSpace2ndTo1stEnd)
10088  {
10089  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10090  ++outInfo.unusedRangeCount;
10091  outInfo.unusedBytes += unusedRangeSize;
10092  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10093  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10094  }
10095 
10096  // End of loop.
10097  lastOffset = freeSpace2ndTo1stEnd;
10098  }
10099  }
10100  }
10101 
10102  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10103  const VkDeviceSize freeSpace1stTo2ndEnd =
10104  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10105  while(lastOffset < freeSpace1stTo2ndEnd)
10106  {
10107  // Find next non-null allocation or move nextAllocIndex to the end.
10108  while(nextAlloc1stIndex < suballoc1stCount &&
10109  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10110  {
10111  ++nextAlloc1stIndex;
10112  }
10113 
10114  // Found non-null allocation.
10115  if(nextAlloc1stIndex < suballoc1stCount)
10116  {
10117  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10118 
10119  // 1. Process free space before this allocation.
10120  if(lastOffset < suballoc.offset)
10121  {
10122  // There is free space from lastOffset to suballoc.offset.
10123  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10124  ++outInfo.unusedRangeCount;
10125  outInfo.unusedBytes += unusedRangeSize;
10126  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10127  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10128  }
10129 
10130  // 2. Process this allocation.
10131  // There is allocation with suballoc.offset, suballoc.size.
10132  outInfo.usedBytes += suballoc.size;
10133  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10134  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10135 
10136  // 3. Prepare for next iteration.
10137  lastOffset = suballoc.offset + suballoc.size;
10138  ++nextAlloc1stIndex;
10139  }
10140  // We are at the end.
10141  else
10142  {
10143  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10144  if(lastOffset < freeSpace1stTo2ndEnd)
10145  {
10146  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10147  ++outInfo.unusedRangeCount;
10148  outInfo.unusedBytes += unusedRangeSize;
10149  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10150  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10151  }
10152 
10153  // End of loop.
10154  lastOffset = freeSpace1stTo2ndEnd;
10155  }
10156  }
10157 
10158  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10159  {
10160  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10161  while(lastOffset < size)
10162  {
10163  // Find next non-null allocation or move nextAllocIndex to the end.
10164  while(nextAlloc2ndIndex != SIZE_MAX &&
10165  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10166  {
10167  --nextAlloc2ndIndex;
10168  }
10169 
10170  // Found non-null allocation.
10171  if(nextAlloc2ndIndex != SIZE_MAX)
10172  {
10173  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10174 
10175  // 1. Process free space before this allocation.
10176  if(lastOffset < suballoc.offset)
10177  {
10178  // There is free space from lastOffset to suballoc.offset.
10179  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10180  ++outInfo.unusedRangeCount;
10181  outInfo.unusedBytes += unusedRangeSize;
10182  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10183  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10184  }
10185 
10186  // 2. Process this allocation.
10187  // There is allocation with suballoc.offset, suballoc.size.
10188  outInfo.usedBytes += suballoc.size;
10189  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10190  outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10191 
10192  // 3. Prepare for next iteration.
10193  lastOffset = suballoc.offset + suballoc.size;
10194  --nextAlloc2ndIndex;
10195  }
10196  // We are at the end.
10197  else
10198  {
10199  // There is free space from lastOffset to size.
10200  if(lastOffset < size)
10201  {
10202  const VkDeviceSize unusedRangeSize = size - lastOffset;
10203  ++outInfo.unusedRangeCount;
10204  outInfo.unusedBytes += unusedRangeSize;
10205  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10206  outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10207  }
10208 
10209  // End of loop.
10210  lastOffset = size;
10211  }
10212  }
10213  }
10214 
10215  outInfo.unusedBytes = size - outInfo.usedBytes;
10216 }
10217 
10218 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10219 {
10220  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10221  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10222  const VkDeviceSize size = GetSize();
10223  const size_t suballoc1stCount = suballocations1st.size();
10224  const size_t suballoc2ndCount = suballocations2nd.size();
10225 
10226  inoutStats.size += size;
10227 
10228  VkDeviceSize lastOffset = 0;
10229 
10230  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10231  {
10232  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10233  size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10234  while(lastOffset < freeSpace2ndTo1stEnd)
10235  {
10236  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10237  while(nextAlloc2ndIndex < suballoc2ndCount &&
10238  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10239  {
10240  ++nextAlloc2ndIndex;
10241  }
10242 
10243  // Found non-null allocation.
10244  if(nextAlloc2ndIndex < suballoc2ndCount)
10245  {
10246  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10247 
10248  // 1. Process free space before this allocation.
10249  if(lastOffset < suballoc.offset)
10250  {
10251  // There is free space from lastOffset to suballoc.offset.
10252  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10253  inoutStats.unusedSize += unusedRangeSize;
10254  ++inoutStats.unusedRangeCount;
10255  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10256  }
10257 
10258  // 2. Process this allocation.
10259  // There is allocation with suballoc.offset, suballoc.size.
10260  ++inoutStats.allocationCount;
10261 
10262  // 3. Prepare for next iteration.
10263  lastOffset = suballoc.offset + suballoc.size;
10264  ++nextAlloc2ndIndex;
10265  }
10266  // We are at the end.
10267  else
10268  {
10269  if(lastOffset < freeSpace2ndTo1stEnd)
10270  {
10271  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10272  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10273  inoutStats.unusedSize += unusedRangeSize;
10274  ++inoutStats.unusedRangeCount;
10275  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10276  }
10277 
10278  // End of loop.
10279  lastOffset = freeSpace2ndTo1stEnd;
10280  }
10281  }
10282  }
10283 
10284  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10285  const VkDeviceSize freeSpace1stTo2ndEnd =
10286  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10287  while(lastOffset < freeSpace1stTo2ndEnd)
10288  {
10289  // Find next non-null allocation or move nextAllocIndex to the end.
10290  while(nextAlloc1stIndex < suballoc1stCount &&
10291  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10292  {
10293  ++nextAlloc1stIndex;
10294  }
10295 
10296  // Found non-null allocation.
10297  if(nextAlloc1stIndex < suballoc1stCount)
10298  {
10299  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10300 
10301  // 1. Process free space before this allocation.
10302  if(lastOffset < suballoc.offset)
10303  {
10304  // There is free space from lastOffset to suballoc.offset.
10305  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10306  inoutStats.unusedSize += unusedRangeSize;
10307  ++inoutStats.unusedRangeCount;
10308  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10309  }
10310 
10311  // 2. Process this allocation.
10312  // There is allocation with suballoc.offset, suballoc.size.
10313  ++inoutStats.allocationCount;
10314 
10315  // 3. Prepare for next iteration.
10316  lastOffset = suballoc.offset + suballoc.size;
10317  ++nextAlloc1stIndex;
10318  }
10319  // We are at the end.
10320  else
10321  {
10322  if(lastOffset < freeSpace1stTo2ndEnd)
10323  {
10324  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10325  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10326  inoutStats.unusedSize += unusedRangeSize;
10327  ++inoutStats.unusedRangeCount;
10328  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10329  }
10330 
10331  // End of loop.
10332  lastOffset = freeSpace1stTo2ndEnd;
10333  }
10334  }
10335 
10336  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10337  {
10338  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10339  while(lastOffset < size)
10340  {
10341  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10342  while(nextAlloc2ndIndex != SIZE_MAX &&
10343  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10344  {
10345  --nextAlloc2ndIndex;
10346  }
10347 
10348  // Found non-null allocation.
10349  if(nextAlloc2ndIndex != SIZE_MAX)
10350  {
10351  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10352 
10353  // 1. Process free space before this allocation.
10354  if(lastOffset < suballoc.offset)
10355  {
10356  // There is free space from lastOffset to suballoc.offset.
10357  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10358  inoutStats.unusedSize += unusedRangeSize;
10359  ++inoutStats.unusedRangeCount;
10360  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10361  }
10362 
10363  // 2. Process this allocation.
10364  // There is allocation with suballoc.offset, suballoc.size.
10365  ++inoutStats.allocationCount;
10366 
10367  // 3. Prepare for next iteration.
10368  lastOffset = suballoc.offset + suballoc.size;
10369  --nextAlloc2ndIndex;
10370  }
10371  // We are at the end.
10372  else
10373  {
10374  if(lastOffset < size)
10375  {
10376  // There is free space from lastOffset to size.
10377  const VkDeviceSize unusedRangeSize = size - lastOffset;
10378  inoutStats.unusedSize += unusedRangeSize;
10379  ++inoutStats.unusedRangeCount;
10380  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10381  }
10382 
10383  // End of loop.
10384  lastOffset = size;
10385  }
10386  }
10387  }
10388 }
10389 
10390 #if VMA_STATS_STRING_ENABLED
10391 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10392 {
10393  const VkDeviceSize size = GetSize();
10394  const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10395  const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10396  const size_t suballoc1stCount = suballocations1st.size();
10397  const size_t suballoc2ndCount = suballocations2nd.size();
10398 
10399  // FIRST PASS
10400 
10401  size_t unusedRangeCount = 0;
10402  VkDeviceSize usedBytes = 0;
10403 
10404  VkDeviceSize lastOffset = 0;
10405 
10406  size_t alloc2ndCount = 0;
10407  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10408  {
10409  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10410  size_t nextAlloc2ndIndex = 0;
10411  while(lastOffset < freeSpace2ndTo1stEnd)
10412  {
10413  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10414  while(nextAlloc2ndIndex < suballoc2ndCount &&
10415  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10416  {
10417  ++nextAlloc2ndIndex;
10418  }
10419 
10420  // Found non-null allocation.
10421  if(nextAlloc2ndIndex < suballoc2ndCount)
10422  {
10423  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10424 
10425  // 1. Process free space before this allocation.
10426  if(lastOffset < suballoc.offset)
10427  {
10428  // There is free space from lastOffset to suballoc.offset.
10429  ++unusedRangeCount;
10430  }
10431 
10432  // 2. Process this allocation.
10433  // There is allocation with suballoc.offset, suballoc.size.
10434  ++alloc2ndCount;
10435  usedBytes += suballoc.size;
10436 
10437  // 3. Prepare for next iteration.
10438  lastOffset = suballoc.offset + suballoc.size;
10439  ++nextAlloc2ndIndex;
10440  }
10441  // We are at the end.
10442  else
10443  {
10444  if(lastOffset < freeSpace2ndTo1stEnd)
10445  {
10446  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10447  ++unusedRangeCount;
10448  }
10449 
10450  // End of loop.
10451  lastOffset = freeSpace2ndTo1stEnd;
10452  }
10453  }
10454  }
10455 
10456  size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10457  size_t alloc1stCount = 0;
10458  const VkDeviceSize freeSpace1stTo2ndEnd =
10459  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10460  while(lastOffset < freeSpace1stTo2ndEnd)
10461  {
10462  // Find next non-null allocation or move nextAllocIndex to the end.
10463  while(nextAlloc1stIndex < suballoc1stCount &&
10464  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10465  {
10466  ++nextAlloc1stIndex;
10467  }
10468 
10469  // Found non-null allocation.
10470  if(nextAlloc1stIndex < suballoc1stCount)
10471  {
10472  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10473 
10474  // 1. Process free space before this allocation.
10475  if(lastOffset < suballoc.offset)
10476  {
10477  // There is free space from lastOffset to suballoc.offset.
10478  ++unusedRangeCount;
10479  }
10480 
10481  // 2. Process this allocation.
10482  // There is allocation with suballoc.offset, suballoc.size.
10483  ++alloc1stCount;
10484  usedBytes += suballoc.size;
10485 
10486  // 3. Prepare for next iteration.
10487  lastOffset = suballoc.offset + suballoc.size;
10488  ++nextAlloc1stIndex;
10489  }
10490  // We are at the end.
10491  else
10492  {
10493  if(lastOffset < size)
10494  {
10495  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10496  ++unusedRangeCount;
10497  }
10498 
10499  // End of loop.
10500  lastOffset = freeSpace1stTo2ndEnd;
10501  }
10502  }
10503 
10504  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10505  {
10506  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10507  while(lastOffset < size)
10508  {
10509  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10510  while(nextAlloc2ndIndex != SIZE_MAX &&
10511  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10512  {
10513  --nextAlloc2ndIndex;
10514  }
10515 
10516  // Found non-null allocation.
10517  if(nextAlloc2ndIndex != SIZE_MAX)
10518  {
10519  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10520 
10521  // 1. Process free space before this allocation.
10522  if(lastOffset < suballoc.offset)
10523  {
10524  // There is free space from lastOffset to suballoc.offset.
10525  ++unusedRangeCount;
10526  }
10527 
10528  // 2. Process this allocation.
10529  // There is allocation with suballoc.offset, suballoc.size.
10530  ++alloc2ndCount;
10531  usedBytes += suballoc.size;
10532 
10533  // 3. Prepare for next iteration.
10534  lastOffset = suballoc.offset + suballoc.size;
10535  --nextAlloc2ndIndex;
10536  }
10537  // We are at the end.
10538  else
10539  {
10540  if(lastOffset < size)
10541  {
10542  // There is free space from lastOffset to size.
10543  ++unusedRangeCount;
10544  }
10545 
10546  // End of loop.
10547  lastOffset = size;
10548  }
10549  }
10550  }
10551 
10552  const VkDeviceSize unusedBytes = size - usedBytes;
10553  PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10554 
10555  // SECOND PASS
10556  lastOffset = 0;
10557 
10558  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10559  {
10560  const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10561  size_t nextAlloc2ndIndex = 0;
10562  while(lastOffset < freeSpace2ndTo1stEnd)
10563  {
10564  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10565  while(nextAlloc2ndIndex < suballoc2ndCount &&
10566  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10567  {
10568  ++nextAlloc2ndIndex;
10569  }
10570 
10571  // Found non-null allocation.
10572  if(nextAlloc2ndIndex < suballoc2ndCount)
10573  {
10574  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10575 
10576  // 1. Process free space before this allocation.
10577  if(lastOffset < suballoc.offset)
10578  {
10579  // There is free space from lastOffset to suballoc.offset.
10580  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10581  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10582  }
10583 
10584  // 2. Process this allocation.
10585  // There is allocation with suballoc.offset, suballoc.size.
10586  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10587 
10588  // 3. Prepare for next iteration.
10589  lastOffset = suballoc.offset + suballoc.size;
10590  ++nextAlloc2ndIndex;
10591  }
10592  // We are at the end.
10593  else
10594  {
10595  if(lastOffset < freeSpace2ndTo1stEnd)
10596  {
10597  // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10598  const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10599  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10600  }
10601 
10602  // End of loop.
10603  lastOffset = freeSpace2ndTo1stEnd;
10604  }
10605  }
10606  }
10607 
10608  nextAlloc1stIndex = m_1stNullItemsBeginCount;
10609  while(lastOffset < freeSpace1stTo2ndEnd)
10610  {
10611  // Find next non-null allocation or move nextAllocIndex to the end.
10612  while(nextAlloc1stIndex < suballoc1stCount &&
10613  suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10614  {
10615  ++nextAlloc1stIndex;
10616  }
10617 
10618  // Found non-null allocation.
10619  if(nextAlloc1stIndex < suballoc1stCount)
10620  {
10621  const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10622 
10623  // 1. Process free space before this allocation.
10624  if(lastOffset < suballoc.offset)
10625  {
10626  // There is free space from lastOffset to suballoc.offset.
10627  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10628  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10629  }
10630 
10631  // 2. Process this allocation.
10632  // There is allocation with suballoc.offset, suballoc.size.
10633  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10634 
10635  // 3. Prepare for next iteration.
10636  lastOffset = suballoc.offset + suballoc.size;
10637  ++nextAlloc1stIndex;
10638  }
10639  // We are at the end.
10640  else
10641  {
10642  if(lastOffset < freeSpace1stTo2ndEnd)
10643  {
10644  // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10645  const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10646  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10647  }
10648 
10649  // End of loop.
10650  lastOffset = freeSpace1stTo2ndEnd;
10651  }
10652  }
10653 
10654  if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10655  {
10656  size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10657  while(lastOffset < size)
10658  {
10659  // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10660  while(nextAlloc2ndIndex != SIZE_MAX &&
10661  suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10662  {
10663  --nextAlloc2ndIndex;
10664  }
10665 
10666  // Found non-null allocation.
10667  if(nextAlloc2ndIndex != SIZE_MAX)
10668  {
10669  const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10670 
10671  // 1. Process free space before this allocation.
10672  if(lastOffset < suballoc.offset)
10673  {
10674  // There is free space from lastOffset to suballoc.offset.
10675  const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10676  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10677  }
10678 
10679  // 2. Process this allocation.
10680  // There is allocation with suballoc.offset, suballoc.size.
10681  PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10682 
10683  // 3. Prepare for next iteration.
10684  lastOffset = suballoc.offset + suballoc.size;
10685  --nextAlloc2ndIndex;
10686  }
10687  // We are at the end.
10688  else
10689  {
10690  if(lastOffset < size)
10691  {
10692  // There is free space from lastOffset to size.
10693  const VkDeviceSize unusedRangeSize = size - lastOffset;
10694  PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10695  }
10696 
10697  // End of loop.
10698  lastOffset = size;
10699  }
10700  }
10701  }
10702 
10703  PrintDetailedMap_End(json);
10704 }
10705 #endif // #if VMA_STATS_STRING_ENABLED
10706 
10707 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10708  uint32_t currentFrameIndex,
10709  uint32_t frameInUseCount,
10710  VkDeviceSize bufferImageGranularity,
10711  VkDeviceSize allocSize,
10712  VkDeviceSize allocAlignment,
10713  bool upperAddress,
10714  VmaSuballocationType allocType,
10715  bool canMakeOtherLost,
10716  uint32_t strategy,
10717  VmaAllocationRequest* pAllocationRequest)
10718 {
10719  VMA_ASSERT(allocSize > 0);
10720  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10721  VMA_ASSERT(pAllocationRequest != VMA_NULL);
10722  VMA_HEAVY_ASSERT(Validate());
10723  return upperAddress ?
10724  CreateAllocationRequest_UpperAddress(
10725  currentFrameIndex, frameInUseCount, bufferImageGranularity,
10726  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10727  CreateAllocationRequest_LowerAddress(
10728  currentFrameIndex, frameInUseCount, bufferImageGranularity,
10729  allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10730 }
10731 
10732 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10733  uint32_t currentFrameIndex,
10734  uint32_t frameInUseCount,
10735  VkDeviceSize bufferImageGranularity,
10736  VkDeviceSize allocSize,
10737  VkDeviceSize allocAlignment,
10738  VmaSuballocationType allocType,
10739  bool canMakeOtherLost,
10740  uint32_t strategy,
10741  VmaAllocationRequest* pAllocationRequest)
10742 {
10743  const VkDeviceSize size = GetSize();
10744  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10745  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10746 
10747  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10748  {
10749  VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10750  return false;
10751  }
10752 
10753  // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10754  if(allocSize > size)
10755  {
10756  return false;
10757  }
10758  VkDeviceSize resultBaseOffset = size - allocSize;
10759  if(!suballocations2nd.empty())
10760  {
10761  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10762  resultBaseOffset = lastSuballoc.offset - allocSize;
10763  if(allocSize > lastSuballoc.offset)
10764  {
10765  return false;
10766  }
10767  }
10768 
10769  // Start from offset equal to end of free space.
10770  VkDeviceSize resultOffset = resultBaseOffset;
10771 
10772  // Apply VMA_DEBUG_MARGIN at the end.
10773  if(VMA_DEBUG_MARGIN > 0)
10774  {
10775  if(resultOffset < VMA_DEBUG_MARGIN)
10776  {
10777  return false;
10778  }
10779  resultOffset -= VMA_DEBUG_MARGIN;
10780  }
10781 
10782  // Apply alignment.
10783  resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10784 
10785  // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10786  // Make bigger alignment if necessary.
10787  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10788  {
10789  bool bufferImageGranularityConflict = false;
10790  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10791  {
10792  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10793  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10794  {
10795  if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10796  {
10797  bufferImageGranularityConflict = true;
10798  break;
10799  }
10800  }
10801  else
10802  // Already on previous page.
10803  break;
10804  }
10805  if(bufferImageGranularityConflict)
10806  {
10807  resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10808  }
10809  }
10810 
10811  // There is enough free space.
10812  const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10813  suballocations1st.back().offset + suballocations1st.back().size :
10814  0;
10815  if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10816  {
10817  // Check previous suballocations for BufferImageGranularity conflicts.
10818  // If conflict exists, allocation cannot be made here.
10819  if(bufferImageGranularity > 1)
10820  {
10821  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10822  {
10823  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10824  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10825  {
10826  if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10827  {
10828  return false;
10829  }
10830  }
10831  else
10832  {
10833  // Already on next page.
10834  break;
10835  }
10836  }
10837  }
10838 
10839  // All tests passed: Success.
10840  pAllocationRequest->offset = resultOffset;
10841  pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10842  pAllocationRequest->sumItemSize = 0;
10843  // pAllocationRequest->item unused.
10844  pAllocationRequest->itemsToMakeLostCount = 0;
10845  pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10846  return true;
10847  }
10848 
10849  return false;
10850 }
10851 
10852 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10853  uint32_t currentFrameIndex,
10854  uint32_t frameInUseCount,
10855  VkDeviceSize bufferImageGranularity,
10856  VkDeviceSize allocSize,
10857  VkDeviceSize allocAlignment,
10858  VmaSuballocationType allocType,
10859  bool canMakeOtherLost,
10860  uint32_t strategy,
10861  VmaAllocationRequest* pAllocationRequest)
10862 {
10863  const VkDeviceSize size = GetSize();
10864  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10865  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10866 
10867  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10868  {
10869  // Try to allocate at the end of 1st vector.
10870 
10871  VkDeviceSize resultBaseOffset = 0;
10872  if(!suballocations1st.empty())
10873  {
10874  const VmaSuballocation& lastSuballoc = suballocations1st.back();
10875  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10876  }
10877 
10878  // Start from offset equal to beginning of free space.
10879  VkDeviceSize resultOffset = resultBaseOffset;
10880 
10881  // Apply VMA_DEBUG_MARGIN at the beginning.
10882  if(VMA_DEBUG_MARGIN > 0)
10883  {
10884  resultOffset += VMA_DEBUG_MARGIN;
10885  }
10886 
10887  // Apply alignment.
10888  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10889 
10890  // Check previous suballocations for BufferImageGranularity conflicts.
10891  // Make bigger alignment if necessary.
10892  if(bufferImageGranularity > 1 && !suballocations1st.empty())
10893  {
10894  bool bufferImageGranularityConflict = false;
10895  for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10896  {
10897  const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10898  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10899  {
10900  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10901  {
10902  bufferImageGranularityConflict = true;
10903  break;
10904  }
10905  }
10906  else
10907  // Already on previous page.
10908  break;
10909  }
10910  if(bufferImageGranularityConflict)
10911  {
10912  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10913  }
10914  }
10915 
10916  const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
10917  suballocations2nd.back().offset : size;
10918 
10919  // There is enough free space at the end after alignment.
10920  if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
10921  {
10922  // Check next suballocations for BufferImageGranularity conflicts.
10923  // If conflict exists, allocation cannot be made here.
10924  if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10925  {
10926  for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10927  {
10928  const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10929  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10930  {
10931  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10932  {
10933  return false;
10934  }
10935  }
10936  else
10937  {
10938  // Already on previous page.
10939  break;
10940  }
10941  }
10942  }
10943 
10944  // All tests passed: Success.
10945  pAllocationRequest->offset = resultOffset;
10946  pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10947  pAllocationRequest->sumItemSize = 0;
10948  // pAllocationRequest->item, customData unused.
10949  pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10950  pAllocationRequest->itemsToMakeLostCount = 0;
10951  return true;
10952  }
10953  }
10954 
10955  // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10956  // beginning of 1st vector as the end of free space.
10957  if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10958  {
10959  VMA_ASSERT(!suballocations1st.empty());
10960 
10961  VkDeviceSize resultBaseOffset = 0;
10962  if(!suballocations2nd.empty())
10963  {
10964  const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10965  resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10966  }
10967 
10968  // Start from offset equal to beginning of free space.
10969  VkDeviceSize resultOffset = resultBaseOffset;
10970 
10971  // Apply VMA_DEBUG_MARGIN at the beginning.
10972  if(VMA_DEBUG_MARGIN > 0)
10973  {
10974  resultOffset += VMA_DEBUG_MARGIN;
10975  }
10976 
10977  // Apply alignment.
10978  resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10979 
10980  // Check previous suballocations for BufferImageGranularity conflicts.
10981  // Make bigger alignment if necessary.
10982  if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10983  {
10984  bool bufferImageGranularityConflict = false;
10985  for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10986  {
10987  const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10988  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10989  {
10990  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10991  {
10992  bufferImageGranularityConflict = true;
10993  break;
10994  }
10995  }
10996  else
10997  // Already on previous page.
10998  break;
10999  }
11000  if(bufferImageGranularityConflict)
11001  {
11002  resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11003  }
11004  }
11005 
11006  pAllocationRequest->itemsToMakeLostCount = 0;
11007  pAllocationRequest->sumItemSize = 0;
11008  size_t index1st = m_1stNullItemsBeginCount;
11009 
11010  if(canMakeOtherLost)
11011  {
11012  while(index1st < suballocations1st.size() &&
11013  resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11014  {
11015  // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11016  const VmaSuballocation& suballoc = suballocations1st[index1st];
11017  if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11018  {
11019  // No problem.
11020  }
11021  else
11022  {
11023  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11024  if(suballoc.hAllocation->CanBecomeLost() &&
11025  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11026  {
11027  ++pAllocationRequest->itemsToMakeLostCount;
11028  pAllocationRequest->sumItemSize += suballoc.size;
11029  }
11030  else
11031  {
11032  return false;
11033  }
11034  }
11035  ++index1st;
11036  }
11037 
11038  // Check next suballocations for BufferImageGranularity conflicts.
11039  // If conflict exists, we must mark more allocations lost or fail.
11040  if(bufferImageGranularity > 1)
11041  {
11042  while(index1st < suballocations1st.size())
11043  {
11044  const VmaSuballocation& suballoc = suballocations1st[index1st];
11045  if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11046  {
11047  if(suballoc.hAllocation != VK_NULL_HANDLE)
11048  {
11049  // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11050  if(suballoc.hAllocation->CanBecomeLost() &&
11051  suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11052  {
11053  ++pAllocationRequest->itemsToMakeLostCount;
11054  pAllocationRequest->sumItemSize += suballoc.size;
11055  }
11056  else
11057  {
11058  return false;
11059  }
11060  }
11061  }
11062  else
11063  {
11064  // Already on next page.
11065  break;
11066  }
11067  ++index1st;
11068  }
11069  }
11070 
11071  // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11072  if(index1st == suballocations1st.size() &&
11073  resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11074  {
11075  // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11076  VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11077  }
11078  }
11079 
11080  // There is enough free space at the end after alignment.
11081  if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11082  (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11083  {
11084  // Check next suballocations for BufferImageGranularity conflicts.
11085  // If conflict exists, allocation cannot be made here.
11086  if(bufferImageGranularity > 1)
11087  {
11088  for(size_t nextSuballocIndex = index1st;
11089  nextSuballocIndex < suballocations1st.size();
11090  nextSuballocIndex++)
11091  {
11092  const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11093  if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11094  {
11095  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11096  {
11097  return false;
11098  }
11099  }
11100  else
11101  {
11102  // Already on next page.
11103  break;
11104  }
11105  }
11106  }
11107 
11108  // All tests passed: Success.
11109  pAllocationRequest->offset = resultOffset;
11110  pAllocationRequest->sumFreeSize =
11111  (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11112  - resultBaseOffset
11113  - pAllocationRequest->sumItemSize;
11114  pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11115  // pAllocationRequest->item, customData unused.
11116  return true;
11117  }
11118  }
11119 
11120  return false;
11121 }
11122 
11123 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11124  uint32_t currentFrameIndex,
11125  uint32_t frameInUseCount,
11126  VmaAllocationRequest* pAllocationRequest)
11127 {
11128  if(pAllocationRequest->itemsToMakeLostCount == 0)
11129  {
11130  return true;
11131  }
11132 
11133  VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11134 
11135  // We always start from 1st.
11136  SuballocationVectorType* suballocations = &AccessSuballocations1st();
11137  size_t index = m_1stNullItemsBeginCount;
11138  size_t madeLostCount = 0;
11139  while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11140  {
11141  if(index == suballocations->size())
11142  {
11143  index = 0;
11144  // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11145  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11146  {
11147  suballocations = &AccessSuballocations2nd();
11148  }
11149  // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11150  // suballocations continues pointing at AccessSuballocations1st().
11151  VMA_ASSERT(!suballocations->empty());
11152  }
11153  VmaSuballocation& suballoc = (*suballocations)[index];
11154  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11155  {
11156  VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11157  VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11158  if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11159  {
11160  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11161  suballoc.hAllocation = VK_NULL_HANDLE;
11162  m_SumFreeSize += suballoc.size;
11163  if(suballocations == &AccessSuballocations1st())
11164  {
11165  ++m_1stNullItemsMiddleCount;
11166  }
11167  else
11168  {
11169  ++m_2ndNullItemsCount;
11170  }
11171  ++madeLostCount;
11172  }
11173  else
11174  {
11175  return false;
11176  }
11177  }
11178  ++index;
11179  }
11180 
11181  CleanupAfterFree();
11182  //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11183 
11184  return true;
11185 }
11186 
11187 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11188 {
11189  uint32_t lostAllocationCount = 0;
11190 
11191  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11192  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11193  {
11194  VmaSuballocation& suballoc = suballocations1st[i];
11195  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11196  suballoc.hAllocation->CanBecomeLost() &&
11197  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11198  {
11199  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11200  suballoc.hAllocation = VK_NULL_HANDLE;
11201  ++m_1stNullItemsMiddleCount;
11202  m_SumFreeSize += suballoc.size;
11203  ++lostAllocationCount;
11204  }
11205  }
11206 
11207  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11208  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11209  {
11210  VmaSuballocation& suballoc = suballocations2nd[i];
11211  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11212  suballoc.hAllocation->CanBecomeLost() &&
11213  suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11214  {
11215  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11216  suballoc.hAllocation = VK_NULL_HANDLE;
11217  ++m_2ndNullItemsCount;
11218  m_SumFreeSize += suballoc.size;
11219  ++lostAllocationCount;
11220  }
11221  }
11222 
11223  if(lostAllocationCount)
11224  {
11225  CleanupAfterFree();
11226  }
11227 
11228  return lostAllocationCount;
11229 }
11230 
11231 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11232 {
11233  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11234  for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11235  {
11236  const VmaSuballocation& suballoc = suballocations1st[i];
11237  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11238  {
11239  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11240  {
11241  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11242  return VK_ERROR_VALIDATION_FAILED_EXT;
11243  }
11244  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11245  {
11246  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11247  return VK_ERROR_VALIDATION_FAILED_EXT;
11248  }
11249  }
11250  }
11251 
11252  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11253  for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11254  {
11255  const VmaSuballocation& suballoc = suballocations2nd[i];
11256  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11257  {
11258  if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11259  {
11260  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11261  return VK_ERROR_VALIDATION_FAILED_EXT;
11262  }
11263  if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11264  {
11265  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11266  return VK_ERROR_VALIDATION_FAILED_EXT;
11267  }
11268  }
11269  }
11270 
11271  return VK_SUCCESS;
11272 }
11273 
11274 void VmaBlockMetadata_Linear::Alloc(
11275  const VmaAllocationRequest& request,
11276  VmaSuballocationType type,
11277  VkDeviceSize allocSize,
11278  VmaAllocation hAllocation)
11279 {
11280  const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11281 
11282  switch(request.type)
11283  {
11284  case VmaAllocationRequestType::UpperAddress:
11285  {
11286  VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11287  "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11288  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11289  suballocations2nd.push_back(newSuballoc);
11290  m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11291  }
11292  break;
11293  case VmaAllocationRequestType::EndOf1st:
11294  {
11295  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11296 
11297  VMA_ASSERT(suballocations1st.empty() ||
11298  request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11299  // Check if it fits before the end of the block.
11300  VMA_ASSERT(request.offset + allocSize <= GetSize());
11301 
11302  suballocations1st.push_back(newSuballoc);
11303  }
11304  break;
11305  case VmaAllocationRequestType::EndOf2nd:
11306  {
11307  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11308  // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11309  VMA_ASSERT(!suballocations1st.empty() &&
11310  request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11311  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11312 
11313  switch(m_2ndVectorMode)
11314  {
11315  case SECOND_VECTOR_EMPTY:
11316  // First allocation from second part ring buffer.
11317  VMA_ASSERT(suballocations2nd.empty());
11318  m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11319  break;
11320  case SECOND_VECTOR_RING_BUFFER:
11321  // 2-part ring buffer is already started.
11322  VMA_ASSERT(!suballocations2nd.empty());
11323  break;
11324  case SECOND_VECTOR_DOUBLE_STACK:
11325  VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11326  break;
11327  default:
11328  VMA_ASSERT(0);
11329  }
11330 
11331  suballocations2nd.push_back(newSuballoc);
11332  }
11333  break;
11334  default:
11335  VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11336  }
11337 
11338  m_SumFreeSize -= newSuballoc.size;
11339 }
11340 
11341 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11342 {
11343  FreeAtOffset(allocation->GetOffset());
11344 }
11345 
11346 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11347 {
11348  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11349  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11350 
11351  if(!suballocations1st.empty())
11352  {
11353  // First allocation: Mark it as next empty at the beginning.
11354  VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11355  if(firstSuballoc.offset == offset)
11356  {
11357  firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11358  firstSuballoc.hAllocation = VK_NULL_HANDLE;
11359  m_SumFreeSize += firstSuballoc.size;
11360  ++m_1stNullItemsBeginCount;
11361  CleanupAfterFree();
11362  return;
11363  }
11364  }
11365 
11366  // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11367  if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11368  m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11369  {
11370  VmaSuballocation& lastSuballoc = suballocations2nd.back();
11371  if(lastSuballoc.offset == offset)
11372  {
11373  m_SumFreeSize += lastSuballoc.size;
11374  suballocations2nd.pop_back();
11375  CleanupAfterFree();
11376  return;
11377  }
11378  }
11379  // Last allocation in 1st vector.
11380  else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11381  {
11382  VmaSuballocation& lastSuballoc = suballocations1st.back();
11383  if(lastSuballoc.offset == offset)
11384  {
11385  m_SumFreeSize += lastSuballoc.size;
11386  suballocations1st.pop_back();
11387  CleanupAfterFree();
11388  return;
11389  }
11390  }
11391 
11392  // Item from the middle of 1st vector.
11393  {
11394  VmaSuballocation refSuballoc;
11395  refSuballoc.offset = offset;
11396  // Rest of members stays uninitialized intentionally for better performance.
11397  SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11398  suballocations1st.begin() + m_1stNullItemsBeginCount,
11399  suballocations1st.end(),
11400  refSuballoc,
11401  VmaSuballocationOffsetLess());
11402  if(it != suballocations1st.end())
11403  {
11404  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11405  it->hAllocation = VK_NULL_HANDLE;
11406  ++m_1stNullItemsMiddleCount;
11407  m_SumFreeSize += it->size;
11408  CleanupAfterFree();
11409  return;
11410  }
11411  }
11412 
11413  if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11414  {
11415  // Item from the middle of 2nd vector.
11416  VmaSuballocation refSuballoc;
11417  refSuballoc.offset = offset;
11418  // Rest of members stays uninitialized intentionally for better performance.
11419  SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11420  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11421  VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11422  if(it != suballocations2nd.end())
11423  {
11424  it->type = VMA_SUBALLOCATION_TYPE_FREE;
11425  it->hAllocation = VK_NULL_HANDLE;
11426  ++m_2ndNullItemsCount;
11427  m_SumFreeSize += it->size;
11428  CleanupAfterFree();
11429  return;
11430  }
11431  }
11432 
11433  VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11434 }
11435 
11436 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11437 {
11438  const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11439  const size_t suballocCount = AccessSuballocations1st().size();
11440  return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11441 }
11442 
11443 void VmaBlockMetadata_Linear::CleanupAfterFree()
11444 {
11445  SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11446  SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11447 
11448  if(IsEmpty())
11449  {
11450  suballocations1st.clear();
11451  suballocations2nd.clear();
11452  m_1stNullItemsBeginCount = 0;
11453  m_1stNullItemsMiddleCount = 0;
11454  m_2ndNullItemsCount = 0;
11455  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11456  }
11457  else
11458  {
11459  const size_t suballoc1stCount = suballocations1st.size();
11460  const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11461  VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11462 
11463  // Find more null items at the beginning of 1st vector.
11464  while(m_1stNullItemsBeginCount < suballoc1stCount &&
11465  suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11466  {
11467  ++m_1stNullItemsBeginCount;
11468  --m_1stNullItemsMiddleCount;
11469  }
11470 
11471  // Find more null items at the end of 1st vector.
11472  while(m_1stNullItemsMiddleCount > 0 &&
11473  suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11474  {
11475  --m_1stNullItemsMiddleCount;
11476  suballocations1st.pop_back();
11477  }
11478 
11479  // Find more null items at the end of 2nd vector.
11480  while(m_2ndNullItemsCount > 0 &&
11481  suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11482  {
11483  --m_2ndNullItemsCount;
11484  suballocations2nd.pop_back();
11485  }
11486 
11487  // Find more null items at the beginning of 2nd vector.
11488  while(m_2ndNullItemsCount > 0 &&
11489  suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11490  {
11491  --m_2ndNullItemsCount;
11492  VmaVectorRemove(suballocations2nd, 0);
11493  }
11494 
11495  if(ShouldCompact1st())
11496  {
11497  const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11498  size_t srcIndex = m_1stNullItemsBeginCount;
11499  for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11500  {
11501  while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11502  {
11503  ++srcIndex;
11504  }
11505  if(dstIndex != srcIndex)
11506  {
11507  suballocations1st[dstIndex] = suballocations1st[srcIndex];
11508  }
11509  ++srcIndex;
11510  }
11511  suballocations1st.resize(nonNullItemCount);
11512  m_1stNullItemsBeginCount = 0;
11513  m_1stNullItemsMiddleCount = 0;
11514  }
11515 
11516  // 2nd vector became empty.
11517  if(suballocations2nd.empty())
11518  {
11519  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11520  }
11521 
11522  // 1st vector became empty.
11523  if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11524  {
11525  suballocations1st.clear();
11526  m_1stNullItemsBeginCount = 0;
11527 
11528  if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11529  {
11530  // Swap 1st with 2nd. Now 2nd is empty.
11531  m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11532  m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11533  while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11534  suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11535  {
11536  ++m_1stNullItemsBeginCount;
11537  --m_1stNullItemsMiddleCount;
11538  }
11539  m_2ndNullItemsCount = 0;
11540  m_1stVectorIndex ^= 1;
11541  }
11542  }
11543  }
11544 
11545  VMA_HEAVY_ASSERT(Validate());
11546 }
11547 
11548 
11550 // class VmaBlockMetadata_Buddy
11551 
11552 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11553  VmaBlockMetadata(hAllocator),
11554  m_Root(VMA_NULL),
11555  m_AllocationCount(0),
11556  m_FreeCount(1),
11557  m_SumFreeSize(0)
11558 {
11559  memset(m_FreeList, 0, sizeof(m_FreeList));
11560 }
11561 
11562 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11563 {
11564  DeleteNode(m_Root);
11565 }
11566 
11567 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11568 {
11569  VmaBlockMetadata::Init(size);
11570 
11571  m_UsableSize = VmaPrevPow2(size);
11572  m_SumFreeSize = m_UsableSize;
11573 
11574  // Calculate m_LevelCount.
11575  m_LevelCount = 1;
11576  while(m_LevelCount < MAX_LEVELS &&
11577  LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11578  {
11579  ++m_LevelCount;
11580  }
11581 
11582  Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11583  rootNode->offset = 0;
11584  rootNode->type = Node::TYPE_FREE;
11585  rootNode->parent = VMA_NULL;
11586  rootNode->buddy = VMA_NULL;
11587 
11588  m_Root = rootNode;
11589  AddToFreeListFront(0, rootNode);
11590 }
11591 
11592 bool VmaBlockMetadata_Buddy::Validate() const
11593 {
11594  // Validate tree.
11595  ValidationContext ctx;
11596  if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11597  {
11598  VMA_VALIDATE(false && "ValidateNode failed.");
11599  }
11600  VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11601  VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11602 
11603  // Validate free node lists.
11604  for(uint32_t level = 0; level < m_LevelCount; ++level)
11605  {
11606  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11607  m_FreeList[level].front->free.prev == VMA_NULL);
11608 
11609  for(Node* node = m_FreeList[level].front;
11610  node != VMA_NULL;
11611  node = node->free.next)
11612  {
11613  VMA_VALIDATE(node->type == Node::TYPE_FREE);
11614 
11615  if(node->free.next == VMA_NULL)
11616  {
11617  VMA_VALIDATE(m_FreeList[level].back == node);
11618  }
11619  else
11620  {
11621  VMA_VALIDATE(node->free.next->free.prev == node);
11622  }
11623  }
11624  }
11625 
11626  // Validate that free lists ar higher levels are empty.
11627  for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11628  {
11629  VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11630  }
11631 
11632  return true;
11633 }
11634 
11635 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11636 {
11637  for(uint32_t level = 0; level < m_LevelCount; ++level)
11638  {
11639  if(m_FreeList[level].front != VMA_NULL)
11640  {
11641  return LevelToNodeSize(level);
11642  }
11643  }
11644  return 0;
11645 }
11646 
11647 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11648 {
11649  const VkDeviceSize unusableSize = GetUnusableSize();
11650 
11651  outInfo.blockCount = 1;
11652 
11653  outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11654  outInfo.usedBytes = outInfo.unusedBytes = 0;
11655 
11656  outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11657  outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11658  outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11659 
11660  CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11661 
11662  if(unusableSize > 0)
11663  {
11664  ++outInfo.unusedRangeCount;
11665  outInfo.unusedBytes += unusableSize;
11666  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11667  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11668  }
11669 }
11670 
11671 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11672 {
11673  const VkDeviceSize unusableSize = GetUnusableSize();
11674 
11675  inoutStats.size += GetSize();
11676  inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11677  inoutStats.allocationCount += m_AllocationCount;
11678  inoutStats.unusedRangeCount += m_FreeCount;
11679  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11680 
11681  if(unusableSize > 0)
11682  {
11683  ++inoutStats.unusedRangeCount;
11684  // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11685  }
11686 }
11687 
11688 #if VMA_STATS_STRING_ENABLED
11689 
11690 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11691 {
11692  // TODO optimize
11693  VmaStatInfo stat;
11694  CalcAllocationStatInfo(stat);
11695 
11696  PrintDetailedMap_Begin(
11697  json,
11698  stat.unusedBytes,
11699  stat.allocationCount,
11700  stat.unusedRangeCount);
11701 
11702  PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11703 
11704  const VkDeviceSize unusableSize = GetUnusableSize();
11705  if(unusableSize > 0)
11706  {
11707  PrintDetailedMap_UnusedRange(json,
11708  m_UsableSize, // offset
11709  unusableSize); // size
11710  }
11711 
11712  PrintDetailedMap_End(json);
11713 }
11714 
11715 #endif // #if VMA_STATS_STRING_ENABLED
11716 
11717 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11718  uint32_t currentFrameIndex,
11719  uint32_t frameInUseCount,
11720  VkDeviceSize bufferImageGranularity,
11721  VkDeviceSize allocSize,
11722  VkDeviceSize allocAlignment,
11723  bool upperAddress,
11724  VmaSuballocationType allocType,
11725  bool canMakeOtherLost,
11726  uint32_t strategy,
11727  VmaAllocationRequest* pAllocationRequest)
11728 {
11729  VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11730 
11731  // Simple way to respect bufferImageGranularity. May be optimized some day.
11732  // Whenever it might be an OPTIMAL image...
11733  if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11734  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11735  allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11736  {
11737  allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11738  allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11739  }
11740 
11741  if(allocSize > m_UsableSize)
11742  {
11743  return false;
11744  }
11745 
11746  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11747  for(uint32_t level = targetLevel + 1; level--; )
11748  {
11749  for(Node* freeNode = m_FreeList[level].front;
11750  freeNode != VMA_NULL;
11751  freeNode = freeNode->free.next)
11752  {
11753  if(freeNode->offset % allocAlignment == 0)
11754  {
11755  pAllocationRequest->type = VmaAllocationRequestType::Normal;
11756  pAllocationRequest->offset = freeNode->offset;
11757  pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11758  pAllocationRequest->sumItemSize = 0;
11759  pAllocationRequest->itemsToMakeLostCount = 0;
11760  pAllocationRequest->customData = (void*)(uintptr_t)level;
11761  return true;
11762  }
11763  }
11764  }
11765 
11766  return false;
11767 }
11768 
11769 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11770  uint32_t currentFrameIndex,
11771  uint32_t frameInUseCount,
11772  VmaAllocationRequest* pAllocationRequest)
11773 {
11774  /*
11775  Lost allocations are not supported in buddy allocator at the moment.
11776  Support might be added in the future.
11777  */
11778  return pAllocationRequest->itemsToMakeLostCount == 0;
11779 }
11780 
11781 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11782 {
11783  /*
11784  Lost allocations are not supported in buddy allocator at the moment.
11785  Support might be added in the future.
11786  */
11787  return 0;
11788 }
11789 
11790 void VmaBlockMetadata_Buddy::Alloc(
11791  const VmaAllocationRequest& request,
11792  VmaSuballocationType type,
11793  VkDeviceSize allocSize,
11794  VmaAllocation hAllocation)
11795 {
11796  VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11797 
11798  const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11799  uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11800 
11801  Node* currNode = m_FreeList[currLevel].front;
11802  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11803  while(currNode->offset != request.offset)
11804  {
11805  currNode = currNode->free.next;
11806  VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11807  }
11808 
11809  // Go down, splitting free nodes.
11810  while(currLevel < targetLevel)
11811  {
11812  // currNode is already first free node at currLevel.
11813  // Remove it from list of free nodes at this currLevel.
11814  RemoveFromFreeList(currLevel, currNode);
11815 
11816  const uint32_t childrenLevel = currLevel + 1;
11817 
11818  // Create two free sub-nodes.
11819  Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11820  Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11821 
11822  leftChild->offset = currNode->offset;
11823  leftChild->type = Node::TYPE_FREE;
11824  leftChild->parent = currNode;
11825  leftChild->buddy = rightChild;
11826 
11827  rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11828  rightChild->type = Node::TYPE_FREE;
11829  rightChild->parent = currNode;
11830  rightChild->buddy = leftChild;
11831 
11832  // Convert current currNode to split type.
11833  currNode->type = Node::TYPE_SPLIT;
11834  currNode->split.leftChild = leftChild;
11835 
11836  // Add child nodes to free list. Order is important!
11837  AddToFreeListFront(childrenLevel, rightChild);
11838  AddToFreeListFront(childrenLevel, leftChild);
11839 
11840  ++m_FreeCount;
11841  //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11842  ++currLevel;
11843  currNode = m_FreeList[currLevel].front;
11844 
11845  /*
11846  We can be sure that currNode, as left child of node previously split,
11847  also fullfills the alignment requirement.
11848  */
11849  }
11850 
11851  // Remove from free list.
11852  VMA_ASSERT(currLevel == targetLevel &&
11853  currNode != VMA_NULL &&
11854  currNode->type == Node::TYPE_FREE);
11855  RemoveFromFreeList(currLevel, currNode);
11856 
11857  // Convert to allocation node.
11858  currNode->type = Node::TYPE_ALLOCATION;
11859  currNode->allocation.alloc = hAllocation;
11860 
11861  ++m_AllocationCount;
11862  --m_FreeCount;
11863  m_SumFreeSize -= allocSize;
11864 }
11865 
11866 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11867 {
11868  if(node->type == Node::TYPE_SPLIT)
11869  {
11870  DeleteNode(node->split.leftChild->buddy);
11871  DeleteNode(node->split.leftChild);
11872  }
11873 
11874  vma_delete(GetAllocationCallbacks(), node);
11875 }
11876 
11877 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11878 {
11879  VMA_VALIDATE(level < m_LevelCount);
11880  VMA_VALIDATE(curr->parent == parent);
11881  VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11882  VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11883  switch(curr->type)
11884  {
11885  case Node::TYPE_FREE:
11886  // curr->free.prev, next are validated separately.
11887  ctx.calculatedSumFreeSize += levelNodeSize;
11888  ++ctx.calculatedFreeCount;
11889  break;
11890  case Node::TYPE_ALLOCATION:
11891  ++ctx.calculatedAllocationCount;
11892  ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11893  VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11894  break;
11895  case Node::TYPE_SPLIT:
11896  {
11897  const uint32_t childrenLevel = level + 1;
11898  const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11899  const Node* const leftChild = curr->split.leftChild;
11900  VMA_VALIDATE(leftChild != VMA_NULL);
11901  VMA_VALIDATE(leftChild->offset == curr->offset);
11902  if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11903  {
11904  VMA_VALIDATE(false && "ValidateNode for left child failed.");
11905  }
11906  const Node* const rightChild = leftChild->buddy;
11907  VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11908  if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11909  {
11910  VMA_VALIDATE(false && "ValidateNode for right child failed.");
11911  }
11912  }
11913  break;
11914  default:
11915  return false;
11916  }
11917 
11918  return true;
11919 }
11920 
11921 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11922 {
11923  // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11924  uint32_t level = 0;
11925  VkDeviceSize currLevelNodeSize = m_UsableSize;
11926  VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11927  while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11928  {
11929  ++level;
11930  currLevelNodeSize = nextLevelNodeSize;
11931  nextLevelNodeSize = currLevelNodeSize >> 1;
11932  }
11933  return level;
11934 }
11935 
11936 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
11937 {
11938  // Find node and level.
11939  Node* node = m_Root;
11940  VkDeviceSize nodeOffset = 0;
11941  uint32_t level = 0;
11942  VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11943  while(node->type == Node::TYPE_SPLIT)
11944  {
11945  const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
11946  if(offset < nodeOffset + nextLevelSize)
11947  {
11948  node = node->split.leftChild;
11949  }
11950  else
11951  {
11952  node = node->split.leftChild->buddy;
11953  nodeOffset += nextLevelSize;
11954  }
11955  ++level;
11956  levelNodeSize = nextLevelSize;
11957  }
11958 
11959  VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11960  VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
11961 
11962  ++m_FreeCount;
11963  --m_AllocationCount;
11964  m_SumFreeSize += alloc->GetSize();
11965 
11966  node->type = Node::TYPE_FREE;
11967 
11968  // Join free nodes if possible.
11969  while(level > 0 && node->buddy->type == Node::TYPE_FREE)
11970  {
11971  RemoveFromFreeList(level, node->buddy);
11972  Node* const parent = node->parent;
11973 
11974  vma_delete(GetAllocationCallbacks(), node->buddy);
11975  vma_delete(GetAllocationCallbacks(), node);
11976  parent->type = Node::TYPE_FREE;
11977 
11978  node = parent;
11979  --level;
11980  //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
11981  --m_FreeCount;
11982  }
11983 
11984  AddToFreeListFront(level, node);
11985 }
11986 
11987 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
11988 {
11989  switch(node->type)
11990  {
11991  case Node::TYPE_FREE:
11992  ++outInfo.unusedRangeCount;
11993  outInfo.unusedBytes += levelNodeSize;
11994  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
11995  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
11996  break;
11997  case Node::TYPE_ALLOCATION:
11998  {
11999  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12000  ++outInfo.allocationCount;
12001  outInfo.usedBytes += allocSize;
12002  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12003  outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12004 
12005  const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12006  if(unusedRangeSize > 0)
12007  {
12008  ++outInfo.unusedRangeCount;
12009  outInfo.unusedBytes += unusedRangeSize;
12010  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12011  outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12012  }
12013  }
12014  break;
12015  case Node::TYPE_SPLIT:
12016  {
12017  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12018  const Node* const leftChild = node->split.leftChild;
12019  CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12020  const Node* const rightChild = leftChild->buddy;
12021  CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12022  }
12023  break;
12024  default:
12025  VMA_ASSERT(0);
12026  }
12027 }
12028 
12029 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12030 {
12031  VMA_ASSERT(node->type == Node::TYPE_FREE);
12032 
12033  // List is empty.
12034  Node* const frontNode = m_FreeList[level].front;
12035  if(frontNode == VMA_NULL)
12036  {
12037  VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12038  node->free.prev = node->free.next = VMA_NULL;
12039  m_FreeList[level].front = m_FreeList[level].back = node;
12040  }
12041  else
12042  {
12043  VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12044  node->free.prev = VMA_NULL;
12045  node->free.next = frontNode;
12046  frontNode->free.prev = node;
12047  m_FreeList[level].front = node;
12048  }
12049 }
12050 
12051 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12052 {
12053  VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12054 
12055  // It is at the front.
12056  if(node->free.prev == VMA_NULL)
12057  {
12058  VMA_ASSERT(m_FreeList[level].front == node);
12059  m_FreeList[level].front = node->free.next;
12060  }
12061  else
12062  {
12063  Node* const prevFreeNode = node->free.prev;
12064  VMA_ASSERT(prevFreeNode->free.next == node);
12065  prevFreeNode->free.next = node->free.next;
12066  }
12067 
12068  // It is at the back.
12069  if(node->free.next == VMA_NULL)
12070  {
12071  VMA_ASSERT(m_FreeList[level].back == node);
12072  m_FreeList[level].back = node->free.prev;
12073  }
12074  else
12075  {
12076  Node* const nextFreeNode = node->free.next;
12077  VMA_ASSERT(nextFreeNode->free.prev == node);
12078  nextFreeNode->free.prev = node->free.prev;
12079  }
12080 }
12081 
12082 #if VMA_STATS_STRING_ENABLED
12083 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12084 {
12085  switch(node->type)
12086  {
12087  case Node::TYPE_FREE:
12088  PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12089  break;
12090  case Node::TYPE_ALLOCATION:
12091  {
12092  PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12093  const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12094  if(allocSize < levelNodeSize)
12095  {
12096  PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12097  }
12098  }
12099  break;
12100  case Node::TYPE_SPLIT:
12101  {
12102  const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12103  const Node* const leftChild = node->split.leftChild;
12104  PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12105  const Node* const rightChild = leftChild->buddy;
12106  PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12107  }
12108  break;
12109  default:
12110  VMA_ASSERT(0);
12111  }
12112 }
12113 #endif // #if VMA_STATS_STRING_ENABLED
12114 
12115 
12117 // class VmaDeviceMemoryBlock
12118 
12119 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12120  m_pMetadata(VMA_NULL),
12121  m_MemoryTypeIndex(UINT32_MAX),
12122  m_Id(0),
12123  m_hMemory(VK_NULL_HANDLE),
12124  m_MapCount(0),
12125  m_pMappedData(VMA_NULL)
12126 {
12127 }
12128 
12129 void VmaDeviceMemoryBlock::Init(
12130  VmaAllocator hAllocator,
12131  VmaPool hParentPool,
12132  uint32_t newMemoryTypeIndex,
12133  VkDeviceMemory newMemory,
12134  VkDeviceSize newSize,
12135  uint32_t id,
12136  uint32_t algorithm)
12137 {
12138  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12139 
12140  m_hParentPool = hParentPool;
12141  m_MemoryTypeIndex = newMemoryTypeIndex;
12142  m_Id = id;
12143  m_hMemory = newMemory;
12144 
12145  switch(algorithm)
12146  {
12148  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12149  break;
12151  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12152  break;
12153  default:
12154  VMA_ASSERT(0);
12155  // Fall-through.
12156  case 0:
12157  m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12158  }
12159  m_pMetadata->Init(newSize);
12160 }
12161 
12162 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12163 {
12164  // This is the most important assert in the entire library.
12165  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12166  VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12167 
12168  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12169  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12170  m_hMemory = VK_NULL_HANDLE;
12171 
12172  vma_delete(allocator, m_pMetadata);
12173  m_pMetadata = VMA_NULL;
12174 }
12175 
12176 bool VmaDeviceMemoryBlock::Validate() const
12177 {
12178  VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12179  (m_pMetadata->GetSize() != 0));
12180 
12181  return m_pMetadata->Validate();
12182 }
12183 
12184 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12185 {
12186  void* pData = nullptr;
12187  VkResult res = Map(hAllocator, 1, &pData);
12188  if(res != VK_SUCCESS)
12189  {
12190  return res;
12191  }
12192 
12193  res = m_pMetadata->CheckCorruption(pData);
12194 
12195  Unmap(hAllocator, 1);
12196 
12197  return res;
12198 }
12199 
12200 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12201 {
12202  if(count == 0)
12203  {
12204  return VK_SUCCESS;
12205  }
12206 
12207  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12208  if(m_MapCount != 0)
12209  {
12210  m_MapCount += count;
12211  VMA_ASSERT(m_pMappedData != VMA_NULL);
12212  if(ppData != VMA_NULL)
12213  {
12214  *ppData = m_pMappedData;
12215  }
12216  return VK_SUCCESS;
12217  }
12218  else
12219  {
12220  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12221  hAllocator->m_hDevice,
12222  m_hMemory,
12223  0, // offset
12224  VK_WHOLE_SIZE,
12225  0, // flags
12226  &m_pMappedData);
12227  if(result == VK_SUCCESS)
12228  {
12229  if(ppData != VMA_NULL)
12230  {
12231  *ppData = m_pMappedData;
12232  }
12233  m_MapCount = count;
12234  }
12235  return result;
12236  }
12237 }
12238 
12239 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12240 {
12241  if(count == 0)
12242  {
12243  return;
12244  }
12245 
12246  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12247  if(m_MapCount >= count)
12248  {
12249  m_MapCount -= count;
12250  if(m_MapCount == 0)
12251  {
12252  m_pMappedData = VMA_NULL;
12253  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12254  }
12255  }
12256  else
12257  {
12258  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12259  }
12260 }
12261 
12262 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12263 {
12264  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12265  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12266 
12267  void* pData;
12268  VkResult res = Map(hAllocator, 1, &pData);
12269  if(res != VK_SUCCESS)
12270  {
12271  return res;
12272  }
12273 
12274  VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12275  VmaWriteMagicValue(pData, allocOffset + allocSize);
12276 
12277  Unmap(hAllocator, 1);
12278 
12279  return VK_SUCCESS;
12280 }
12281 
12282 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12283 {
12284  VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12285  VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12286 
12287  void* pData;
12288  VkResult res = Map(hAllocator, 1, &pData);
12289  if(res != VK_SUCCESS)
12290  {
12291  return res;
12292  }
12293 
12294  if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12295  {
12296  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12297  }
12298  else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12299  {
12300  VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12301  }
12302 
12303  Unmap(hAllocator, 1);
12304 
12305  return VK_SUCCESS;
12306 }
12307 
12308 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12309  const VmaAllocator hAllocator,
12310  const VmaAllocation hAllocation,
12311  VkDeviceSize allocationLocalOffset,
12312  VkBuffer hBuffer,
12313  const void* pNext)
12314 {
12315  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12316  hAllocation->GetBlock() == this);
12317  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12318  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12319  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12320  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12321  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12322  return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12323 }
12324 
12325 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12326  const VmaAllocator hAllocator,
12327  const VmaAllocation hAllocation,
12328  VkDeviceSize allocationLocalOffset,
12329  VkImage hImage,
12330  const void* pNext)
12331 {
12332  VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12333  hAllocation->GetBlock() == this);
12334  VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12335  "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12336  const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12337  // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12338  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12339  return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12340 }
12341 
12342 static void InitStatInfo(VmaStatInfo& outInfo)
12343 {
12344  memset(&outInfo, 0, sizeof(outInfo));
12345  outInfo.allocationSizeMin = UINT64_MAX;
12346  outInfo.unusedRangeSizeMin = UINT64_MAX;
12347 }
12348 
12349 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
12350 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12351 {
12352  inoutInfo.blockCount += srcInfo.blockCount;
12353  inoutInfo.allocationCount += srcInfo.allocationCount;
12354  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12355  inoutInfo.usedBytes += srcInfo.usedBytes;
12356  inoutInfo.unusedBytes += srcInfo.unusedBytes;
12357  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12358  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12359  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12360  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12361 }
12362 
12363 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12364 {
12365  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12366  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12367  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12368  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12369 }
12370 
12371 VmaPool_T::VmaPool_T(
12372  VmaAllocator hAllocator,
12373  const VmaPoolCreateInfo& createInfo,
12374  VkDeviceSize preferredBlockSize) :
12375  m_BlockVector(
12376  hAllocator,
12377  this, // hParentPool
12378  createInfo.memoryTypeIndex,
12379  createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12380  createInfo.minBlockCount,
12381  createInfo.maxBlockCount,
12382  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12383  createInfo.frameInUseCount,
12384  createInfo.blockSize != 0, // explicitBlockSize
12385  createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12386  m_Id(0),
12387  m_Name(VMA_NULL)
12388 {
12389 }
12390 
12391 VmaPool_T::~VmaPool_T()
12392 {
12393 }
12394 
12395 void VmaPool_T::SetName(const char* pName)
12396 {
12397  const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12398  VmaFreeString(allocs, m_Name);
12399 
12400  if(pName != VMA_NULL)
12401  {
12402  m_Name = VmaCreateStringCopy(allocs, pName);
12403  }
12404  else
12405  {
12406  m_Name = VMA_NULL;
12407  }
12408 }
12409 
12410 #if VMA_STATS_STRING_ENABLED
12411 
12412 #endif // #if VMA_STATS_STRING_ENABLED
12413 
12414 VmaBlockVector::VmaBlockVector(
12415  VmaAllocator hAllocator,
12416  VmaPool hParentPool,
12417  uint32_t memoryTypeIndex,
12418  VkDeviceSize preferredBlockSize,
12419  size_t minBlockCount,
12420  size_t maxBlockCount,
12421  VkDeviceSize bufferImageGranularity,
12422  uint32_t frameInUseCount,
12423  bool explicitBlockSize,
12424  uint32_t algorithm) :
12425  m_hAllocator(hAllocator),
12426  m_hParentPool(hParentPool),
12427  m_MemoryTypeIndex(memoryTypeIndex),
12428  m_PreferredBlockSize(preferredBlockSize),
12429  m_MinBlockCount(minBlockCount),
12430  m_MaxBlockCount(maxBlockCount),
12431  m_BufferImageGranularity(bufferImageGranularity),
12432  m_FrameInUseCount(frameInUseCount),
12433  m_ExplicitBlockSize(explicitBlockSize),
12434  m_Algorithm(algorithm),
12435  m_HasEmptyBlock(false),
12436  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12437  m_NextBlockId(0)
12438 {
12439 }
12440 
12441 VmaBlockVector::~VmaBlockVector()
12442 {
12443  for(size_t i = m_Blocks.size(); i--; )
12444  {
12445  m_Blocks[i]->Destroy(m_hAllocator);
12446  vma_delete(m_hAllocator, m_Blocks[i]);
12447  }
12448 }
12449 
12450 VkResult VmaBlockVector::CreateMinBlocks()
12451 {
12452  for(size_t i = 0; i < m_MinBlockCount; ++i)
12453  {
12454  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12455  if(res != VK_SUCCESS)
12456  {
12457  return res;
12458  }
12459  }
12460  return VK_SUCCESS;
12461 }
12462 
12463 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12464 {
12465  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12466 
12467  const size_t blockCount = m_Blocks.size();
12468 
12469  pStats->size = 0;
12470  pStats->unusedSize = 0;
12471  pStats->allocationCount = 0;
12472  pStats->unusedRangeCount = 0;
12473  pStats->unusedRangeSizeMax = 0;
12474  pStats->blockCount = blockCount;
12475 
12476  for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12477  {
12478  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12479  VMA_ASSERT(pBlock);
12480  VMA_HEAVY_ASSERT(pBlock->Validate());
12481  pBlock->m_pMetadata->AddPoolStats(*pStats);
12482  }
12483 }
12484 
12485 bool VmaBlockVector::IsEmpty()
12486 {
12487  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12488  return m_Blocks.empty();
12489 }
12490 
12491 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12492 {
12493  const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12494  return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12495  (VMA_DEBUG_MARGIN > 0) &&
12496  (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12497  (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12498 }
12499 
12500 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12501 
12502 VkResult VmaBlockVector::Allocate(
12503  uint32_t currentFrameIndex,
12504  VkDeviceSize size,
12505  VkDeviceSize alignment,
12506  const VmaAllocationCreateInfo& createInfo,
12507  VmaSuballocationType suballocType,
12508  size_t allocationCount,
12509  VmaAllocation* pAllocations)
12510 {
12511  size_t allocIndex;
12512  VkResult res = VK_SUCCESS;
12513 
12514  if(IsCorruptionDetectionEnabled())
12515  {
12516  size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12517  alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12518  }
12519 
12520  {
12521  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12522  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12523  {
12524  res = AllocatePage(
12525  currentFrameIndex,
12526  size,
12527  alignment,
12528  createInfo,
12529  suballocType,
12530  pAllocations + allocIndex);
12531  if(res != VK_SUCCESS)
12532  {
12533  break;
12534  }
12535  }
12536  }
12537 
12538  if(res != VK_SUCCESS)
12539  {
12540  // Free all already created allocations.
12541  while(allocIndex--)
12542  {
12543  Free(pAllocations[allocIndex]);
12544  }
12545  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12546  }
12547 
12548  return res;
12549 }
12550 
12551 VkResult VmaBlockVector::AllocatePage(
12552  uint32_t currentFrameIndex,
12553  VkDeviceSize size,
12554  VkDeviceSize alignment,
12555  const VmaAllocationCreateInfo& createInfo,
12556  VmaSuballocationType suballocType,
12557  VmaAllocation* pAllocation)
12558 {
12559  const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12560  bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12561  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12562  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12563 
12564  VkDeviceSize freeMemory;
12565  {
12566  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12567  VmaBudget heapBudget = {};
12568  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12569  freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12570  }
12571 
12572  const bool canFallbackToDedicated = !IsCustomPool();
12573  const bool canCreateNewBlock =
12574  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12575  (m_Blocks.size() < m_MaxBlockCount) &&
12576  (freeMemory >= size || !canFallbackToDedicated);
12577  uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12578 
12579  // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12580  // Which in turn is available only when maxBlockCount = 1.
12581  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12582  {
12583  canMakeOtherLost = false;
12584  }
12585 
12586  // Upper address can only be used with linear allocator and within single memory block.
12587  if(isUpperAddress &&
12588  (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12589  {
12590  return VK_ERROR_FEATURE_NOT_PRESENT;
12591  }
12592 
12593  // Validate strategy.
12594  switch(strategy)
12595  {
12596  case 0:
12598  break;
12602  break;
12603  default:
12604  return VK_ERROR_FEATURE_NOT_PRESENT;
12605  }
12606 
12607  // Early reject: requested allocation size is larger that maximum block size for this block vector.
12608  if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12609  {
12610  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12611  }
12612 
12613  /*
12614  Under certain condition, this whole section can be skipped for optimization, so
12615  we move on directly to trying to allocate with canMakeOtherLost. That's the case
12616  e.g. for custom pools with linear algorithm.
12617  */
12618  if(!canMakeOtherLost || canCreateNewBlock)
12619  {
12620  // 1. Search existing allocations. Try to allocate without making other allocations lost.
12621  VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12623 
12624  if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12625  {
12626  // Use only last block.
12627  if(!m_Blocks.empty())
12628  {
12629  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12630  VMA_ASSERT(pCurrBlock);
12631  VkResult res = AllocateFromBlock(
12632  pCurrBlock,
12633  currentFrameIndex,
12634  size,
12635  alignment,
12636  allocFlagsCopy,
12637  createInfo.pUserData,
12638  suballocType,
12639  strategy,
12640  pAllocation);
12641  if(res == VK_SUCCESS)
12642  {
12643  VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12644  return VK_SUCCESS;
12645  }
12646  }
12647  }
12648  else
12649  {
12651  {
12652  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12653  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12654  {
12655  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12656  VMA_ASSERT(pCurrBlock);
12657  VkResult res = AllocateFromBlock(
12658  pCurrBlock,
12659  currentFrameIndex,
12660  size,
12661  alignment,
12662  allocFlagsCopy,
12663  createInfo.pUserData,
12664  suballocType,
12665  strategy,
12666  pAllocation);
12667  if(res == VK_SUCCESS)
12668  {
12669  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12670  return VK_SUCCESS;
12671  }
12672  }
12673  }
12674  else // WORST_FIT, FIRST_FIT
12675  {
12676  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12677  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12678  {
12679  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12680  VMA_ASSERT(pCurrBlock);
12681  VkResult res = AllocateFromBlock(
12682  pCurrBlock,
12683  currentFrameIndex,
12684  size,
12685  alignment,
12686  allocFlagsCopy,
12687  createInfo.pUserData,
12688  suballocType,
12689  strategy,
12690  pAllocation);
12691  if(res == VK_SUCCESS)
12692  {
12693  VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12694  return VK_SUCCESS;
12695  }
12696  }
12697  }
12698  }
12699 
12700  // 2. Try to create new block.
12701  if(canCreateNewBlock)
12702  {
12703  // Calculate optimal size for new block.
12704  VkDeviceSize newBlockSize = m_PreferredBlockSize;
12705  uint32_t newBlockSizeShift = 0;
12706  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12707 
12708  if(!m_ExplicitBlockSize)
12709  {
12710  // Allocate 1/8, 1/4, 1/2 as first blocks.
12711  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12712  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12713  {
12714  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12715  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12716  {
12717  newBlockSize = smallerNewBlockSize;
12718  ++newBlockSizeShift;
12719  }
12720  else
12721  {
12722  break;
12723  }
12724  }
12725  }
12726 
12727  size_t newBlockIndex = 0;
12728  VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12729  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12730  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12731  if(!m_ExplicitBlockSize)
12732  {
12733  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12734  {
12735  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12736  if(smallerNewBlockSize >= size)
12737  {
12738  newBlockSize = smallerNewBlockSize;
12739  ++newBlockSizeShift;
12740  res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12741  CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12742  }
12743  else
12744  {
12745  break;
12746  }
12747  }
12748  }
12749 
12750  if(res == VK_SUCCESS)
12751  {
12752  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12753  VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12754 
12755  res = AllocateFromBlock(
12756  pBlock,
12757  currentFrameIndex,
12758  size,
12759  alignment,
12760  allocFlagsCopy,
12761  createInfo.pUserData,
12762  suballocType,
12763  strategy,
12764  pAllocation);
12765  if(res == VK_SUCCESS)
12766  {
12767  VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12768  return VK_SUCCESS;
12769  }
12770  else
12771  {
12772  // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12773  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12774  }
12775  }
12776  }
12777  }
12778 
12779  // 3. Try to allocate from existing blocks with making other allocations lost.
12780  if(canMakeOtherLost)
12781  {
12782  uint32_t tryIndex = 0;
12783  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12784  {
12785  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12786  VmaAllocationRequest bestRequest = {};
12787  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12788 
12789  // 1. Search existing allocations.
12791  {
12792  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12793  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12794  {
12795  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12796  VMA_ASSERT(pCurrBlock);
12797  VmaAllocationRequest currRequest = {};
12798  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12799  currentFrameIndex,
12800  m_FrameInUseCount,
12801  m_BufferImageGranularity,
12802  size,
12803  alignment,
12804  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12805  suballocType,
12806  canMakeOtherLost,
12807  strategy,
12808  &currRequest))
12809  {
12810  const VkDeviceSize currRequestCost = currRequest.CalcCost();
12811  if(pBestRequestBlock == VMA_NULL ||
12812  currRequestCost < bestRequestCost)
12813  {
12814  pBestRequestBlock = pCurrBlock;
12815  bestRequest = currRequest;
12816  bestRequestCost = currRequestCost;
12817 
12818  if(bestRequestCost == 0)
12819  {
12820  break;
12821  }
12822  }
12823  }
12824  }
12825  }
12826  else // WORST_FIT, FIRST_FIT
12827  {
12828  // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12829  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12830  {
12831  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12832  VMA_ASSERT(pCurrBlock);
12833  VmaAllocationRequest currRequest = {};
12834  if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12835  currentFrameIndex,
12836  m_FrameInUseCount,
12837  m_BufferImageGranularity,
12838  size,
12839  alignment,
12840  (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12841  suballocType,
12842  canMakeOtherLost,
12843  strategy,
12844  &currRequest))
12845  {
12846  const VkDeviceSize currRequestCost = currRequest.CalcCost();
12847  if(pBestRequestBlock == VMA_NULL ||
12848  currRequestCost < bestRequestCost ||
12850  {
12851  pBestRequestBlock = pCurrBlock;
12852  bestRequest = currRequest;
12853  bestRequestCost = currRequestCost;
12854 
12855  if(bestRequestCost == 0 ||
12857  {
12858  break;
12859  }
12860  }
12861  }
12862  }
12863  }
12864 
12865  if(pBestRequestBlock != VMA_NULL)
12866  {
12867  if(mapped)
12868  {
12869  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
12870  if(res != VK_SUCCESS)
12871  {
12872  return res;
12873  }
12874  }
12875 
12876  if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
12877  currentFrameIndex,
12878  m_FrameInUseCount,
12879  &bestRequest))
12880  {
12881  // Allocate from this pBlock.
12882  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12883  pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
12884  UpdateHasEmptyBlock();
12885  (*pAllocation)->InitBlockAllocation(
12886  pBestRequestBlock,
12887  bestRequest.offset,
12888  alignment,
12889  size,
12890  m_MemoryTypeIndex,
12891  suballocType,
12892  mapped,
12893  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12894  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12895  VMA_DEBUG_LOG(" Returned from existing block");
12896  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12897  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
12898  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12899  {
12900  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12901  }
12902  if(IsCorruptionDetectionEnabled())
12903  {
12904  VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
12905  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12906  }
12907  return VK_SUCCESS;
12908  }
12909  // else: Some allocations must have been touched while we are here. Next try.
12910  }
12911  else
12912  {
12913  // Could not find place in any of the blocks - break outer loop.
12914  break;
12915  }
12916  }
12917  /* Maximum number of tries exceeded - a very unlike event when many other
12918  threads are simultaneously touching allocations making it impossible to make
12919  lost at the same time as we try to allocate. */
12920  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
12921  {
12922  return VK_ERROR_TOO_MANY_OBJECTS;
12923  }
12924  }
12925 
12926  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12927 }
12928 
12929 void VmaBlockVector::Free(
12930  const VmaAllocation hAllocation)
12931 {
12932  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12933 
12934  bool budgetExceeded = false;
12935  {
12936  const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12937  VmaBudget heapBudget = {};
12938  m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12939  budgetExceeded = heapBudget.usage >= heapBudget.budget;
12940  }
12941 
12942  // Scope for lock.
12943  {
12944  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12945 
12946  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12947 
12948  if(IsCorruptionDetectionEnabled())
12949  {
12950  VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12951  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12952  }
12953 
12954  if(hAllocation->IsPersistentMap())
12955  {
12956  pBlock->Unmap(m_hAllocator, 1);
12957  }
12958 
12959  pBlock->m_pMetadata->Free(hAllocation);
12960  VMA_HEAVY_ASSERT(pBlock->Validate());
12961 
12962  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12963 
12964  const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12965  // pBlock became empty after this deallocation.
12966  if(pBlock->m_pMetadata->IsEmpty())
12967  {
12968  // Already has empty block. We don't want to have two, so delete this one.
12969  if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12970  {
12971  pBlockToDelete = pBlock;
12972  Remove(pBlock);
12973  }
12974  // else: We now have an empty block - leave it.
12975  }
12976  // pBlock didn't become empty, but we have another empty block - find and free that one.
12977  // (This is optional, heuristics.)
12978  else if(m_HasEmptyBlock && canDeleteBlock)
12979  {
12980  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12981  if(pLastBlock->m_pMetadata->IsEmpty())
12982  {
12983  pBlockToDelete = pLastBlock;
12984  m_Blocks.pop_back();
12985  }
12986  }
12987 
12988  UpdateHasEmptyBlock();
12989  IncrementallySortBlocks();
12990  }
12991 
12992  // Destruction of a free block. Deferred until this point, outside of mutex
12993  // lock, for performance reason.
12994  if(pBlockToDelete != VMA_NULL)
12995  {
12996  VMA_DEBUG_LOG(" Deleted empty block");
12997  pBlockToDelete->Destroy(m_hAllocator);
12998  vma_delete(m_hAllocator, pBlockToDelete);
12999  }
13000 }
13001 
13002 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13003 {
13004  VkDeviceSize result = 0;
13005  for(size_t i = m_Blocks.size(); i--; )
13006  {
13007  result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13008  if(result >= m_PreferredBlockSize)
13009  {
13010  break;
13011  }
13012  }
13013  return result;
13014 }
13015 
13016 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13017 {
13018  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13019  {
13020  if(m_Blocks[blockIndex] == pBlock)
13021  {
13022  VmaVectorRemove(m_Blocks, blockIndex);
13023  return;
13024  }
13025  }
13026  VMA_ASSERT(0);
13027 }
13028 
13029 void VmaBlockVector::IncrementallySortBlocks()
13030 {
13031  if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13032  {
13033  // Bubble sort only until first swap.
13034  for(size_t i = 1; i < m_Blocks.size(); ++i)
13035  {
13036  if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13037  {
13038  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13039  return;
13040  }
13041  }
13042  }
13043 }
13044 
13045 VkResult VmaBlockVector::AllocateFromBlock(
13046  VmaDeviceMemoryBlock* pBlock,
13047  uint32_t currentFrameIndex,
13048  VkDeviceSize size,
13049  VkDeviceSize alignment,
13050  VmaAllocationCreateFlags allocFlags,
13051  void* pUserData,
13052  VmaSuballocationType suballocType,
13053  uint32_t strategy,
13054  VmaAllocation* pAllocation)
13055 {
13056  VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13057  const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13058  const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13059  const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13060 
13061  VmaAllocationRequest currRequest = {};
13062  if(pBlock->m_pMetadata->CreateAllocationRequest(
13063  currentFrameIndex,
13064  m_FrameInUseCount,
13065  m_BufferImageGranularity,
13066  size,
13067  alignment,
13068  isUpperAddress,
13069  suballocType,
13070  false, // canMakeOtherLost
13071  strategy,
13072  &currRequest))
13073  {
13074  // Allocate from pCurrBlock.
13075  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13076 
13077  if(mapped)
13078  {
13079  VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13080  if(res != VK_SUCCESS)
13081  {
13082  return res;
13083  }
13084  }
13085 
13086  *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13087  pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13088  UpdateHasEmptyBlock();
13089  (*pAllocation)->InitBlockAllocation(
13090  pBlock,
13091  currRequest.offset,
13092  alignment,
13093  size,
13094  m_MemoryTypeIndex,
13095  suballocType,
13096  mapped,
13097  (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13098  VMA_HEAVY_ASSERT(pBlock->Validate());
13099  (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13100  m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13101  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13102  {
13103  m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13104  }
13105  if(IsCorruptionDetectionEnabled())
13106  {
13107  VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13108  VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13109  }
13110  return VK_SUCCESS;
13111  }
13112  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13113 }
13114 
13115 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13116 {
13117  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13118  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13119  allocInfo.allocationSize = blockSize;
13120 
13121 #if VMA_BUFFER_DEVICE_ADDRESS
13122  // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13123  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13124  if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13125  {
13126  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13127  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13128  }
13129 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13130 
13131  VkDeviceMemory mem = VK_NULL_HANDLE;
13132  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13133  if(res < 0)
13134  {
13135  return res;
13136  }
13137 
13138  // New VkDeviceMemory successfully created.
13139 
13140  // Create new Allocation for it.
13141  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13142  pBlock->Init(
13143  m_hAllocator,
13144  m_hParentPool,
13145  m_MemoryTypeIndex,
13146  mem,
13147  allocInfo.allocationSize,
13148  m_NextBlockId++,
13149  m_Algorithm);
13150 
13151  m_Blocks.push_back(pBlock);
13152  if(pNewBlockIndex != VMA_NULL)
13153  {
13154  *pNewBlockIndex = m_Blocks.size() - 1;
13155  }
13156 
13157  return VK_SUCCESS;
13158 }
13159 
13160 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13161  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13162  const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13163 {
13164  const size_t blockCount = m_Blocks.size();
13165  const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13166 
13167  enum BLOCK_FLAG
13168  {
13169  BLOCK_FLAG_USED = 0x00000001,
13170  BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13171  };
13172 
13173  struct BlockInfo
13174  {
13175  uint32_t flags;
13176  void* pMappedData;
13177  };
13178  VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13179  blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13180  memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13181 
13182  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13183  const size_t moveCount = moves.size();
13184  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13185  {
13186  const VmaDefragmentationMove& move = moves[moveIndex];
13187  blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13188  blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13189  }
13190 
13191  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13192 
13193  // Go over all blocks. Get mapped pointer or map if necessary.
13194  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13195  {
13196  BlockInfo& currBlockInfo = blockInfo[blockIndex];
13197  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13198  if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13199  {
13200  currBlockInfo.pMappedData = pBlock->GetMappedData();
13201  // It is not originally mapped - map it.
13202  if(currBlockInfo.pMappedData == VMA_NULL)
13203  {
13204  pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13205  if(pDefragCtx->res == VK_SUCCESS)
13206  {
13207  currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13208  }
13209  }
13210  }
13211  }
13212 
13213  // Go over all moves. Do actual data transfer.
13214  if(pDefragCtx->res == VK_SUCCESS)
13215  {
13216  const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13217  VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13218 
13219  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13220  {
13221  const VmaDefragmentationMove& move = moves[moveIndex];
13222 
13223  const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13224  const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13225 
13226  VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13227 
13228  // Invalidate source.
13229  if(isNonCoherent)
13230  {
13231  VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13232  memRange.memory = pSrcBlock->GetDeviceMemory();
13233  memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13234  memRange.size = VMA_MIN(
13235  VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13236  pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13237  (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13238  }
13239 
13240  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13241  memmove(
13242  reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13243  reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13244  static_cast<size_t>(move.size));
13245 
13246  if(IsCorruptionDetectionEnabled())
13247  {
13248  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13249  VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13250  }
13251 
13252  // Flush destination.
13253  if(isNonCoherent)
13254  {
13255  VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13256  memRange.memory = pDstBlock->GetDeviceMemory();
13257  memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13258  memRange.size = VMA_MIN(
13259  VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13260  pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13261  (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13262  }
13263  }
13264  }
13265 
13266  // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13267  // Regardless of pCtx->res == VK_SUCCESS.
13268  for(size_t blockIndex = blockCount; blockIndex--; )
13269  {
13270  const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13271  if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13272  {
13273  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13274  pBlock->Unmap(m_hAllocator, 1);
13275  }
13276  }
13277 }
13278 
13279 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13280  class VmaBlockVectorDefragmentationContext* pDefragCtx,
13281  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13282  VkCommandBuffer commandBuffer)
13283 {
13284  const size_t blockCount = m_Blocks.size();
13285 
13286  pDefragCtx->blockContexts.resize(blockCount);
13287  memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13288 
13289  // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13290  const size_t moveCount = moves.size();
13291  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13292  {
13293  const VmaDefragmentationMove& move = moves[moveIndex];
13294 
13295  //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13296  {
13297  // Old school move still require us to map the whole block
13298  pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13299  pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13300  }
13301  }
13302 
13303  VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13304 
13305  // Go over all blocks. Create and bind buffer for whole block if necessary.
13306  {
13307  VkBufferCreateInfo bufCreateInfo;
13308  VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13309 
13310  for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13311  {
13312  VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13313  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13314  if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13315  {
13316  bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13317  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13318  m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13319  if(pDefragCtx->res == VK_SUCCESS)
13320  {
13321  pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13322  m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13323  }
13324  }
13325  }
13326  }
13327 
13328  // Go over all moves. Post data transfer commands to command buffer.
13329  if(pDefragCtx->res == VK_SUCCESS)
13330  {
13331  for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13332  {
13333  const VmaDefragmentationMove& move = moves[moveIndex];
13334 
13335  const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13336  const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13337 
13338  VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13339 
13340  VkBufferCopy region = {
13341  move.srcOffset,
13342  move.dstOffset,
13343  move.size };
13344  (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13345  commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
13346  }
13347  }
13348 
13349  // Save buffers to defrag context for later destruction.
13350  if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13351  {
13352  pDefragCtx->res = VK_NOT_READY;
13353  }
13354 }
13355 
13356 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13357 {
13358  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13359  {
13360  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13361  if(pBlock->m_pMetadata->IsEmpty())
13362  {
13363  if(m_Blocks.size() > m_MinBlockCount)
13364  {
13365  if(pDefragmentationStats != VMA_NULL)
13366  {
13367  ++pDefragmentationStats->deviceMemoryBlocksFreed;
13368  pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13369  }
13370 
13371  VmaVectorRemove(m_Blocks, blockIndex);
13372  pBlock->Destroy(m_hAllocator);
13373  vma_delete(m_hAllocator, pBlock);
13374  }
13375  else
13376  {
13377  break;
13378  }
13379  }
13380  }
13381  UpdateHasEmptyBlock();
13382 }
13383 
13384 void VmaBlockVector::UpdateHasEmptyBlock()
13385 {
13386  m_HasEmptyBlock = false;
13387  for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13388  {
13389  VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13390  if(pBlock->m_pMetadata->IsEmpty())
13391  {
13392  m_HasEmptyBlock = true;
13393  break;
13394  }
13395  }
13396 }
13397 
13398 #if VMA_STATS_STRING_ENABLED
13399 
13400 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13401 {
13402  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13403 
13404  json.BeginObject();
13405 
13406  if(IsCustomPool())
13407  {
13408  const char* poolName = m_hParentPool->GetName();
13409  if(poolName != VMA_NULL && poolName[0] != '\0')
13410  {
13411  json.WriteString("Name");
13412  json.WriteString(poolName);
13413  }
13414 
13415  json.WriteString("MemoryTypeIndex");
13416  json.WriteNumber(m_MemoryTypeIndex);
13417 
13418  json.WriteString("BlockSize");
13419  json.WriteNumber(m_PreferredBlockSize);
13420 
13421  json.WriteString("BlockCount");
13422  json.BeginObject(true);
13423  if(m_MinBlockCount > 0)
13424  {
13425  json.WriteString("Min");
13426  json.WriteNumber((uint64_t)m_MinBlockCount);
13427  }
13428  if(m_MaxBlockCount < SIZE_MAX)
13429  {
13430  json.WriteString("Max");
13431  json.WriteNumber((uint64_t)m_MaxBlockCount);
13432  }
13433  json.WriteString("Cur");
13434  json.WriteNumber((uint64_t)m_Blocks.size());
13435  json.EndObject();
13436 
13437  if(m_FrameInUseCount > 0)
13438  {
13439  json.WriteString("FrameInUseCount");
13440  json.WriteNumber(m_FrameInUseCount);
13441  }
13442 
13443  if(m_Algorithm != 0)
13444  {
13445  json.WriteString("Algorithm");
13446  json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13447  }
13448  }
13449  else
13450  {
13451  json.WriteString("PreferredBlockSize");
13452  json.WriteNumber(m_PreferredBlockSize);
13453  }
13454 
13455  json.WriteString("Blocks");
13456  json.BeginObject();
13457  for(size_t i = 0; i < m_Blocks.size(); ++i)
13458  {
13459  json.BeginString();
13460  json.ContinueString(m_Blocks[i]->GetId());
13461  json.EndString();
13462 
13463  m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13464  }
13465  json.EndObject();
13466 
13467  json.EndObject();
13468 }
13469 
13470 #endif // #if VMA_STATS_STRING_ENABLED
13471 
13472 void VmaBlockVector::Defragment(
13473  class VmaBlockVectorDefragmentationContext* pCtx,
13475  VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13476  VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13477  VkCommandBuffer commandBuffer)
13478 {
13479  pCtx->res = VK_SUCCESS;
13480 
13481  const VkMemoryPropertyFlags memPropFlags =
13482  m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13483  const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13484 
13485  const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13486  isHostVisible;
13487  const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13488  !IsCorruptionDetectionEnabled() &&
13489  ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13490 
13491  // There are options to defragment this memory type.
13492  if(canDefragmentOnCpu || canDefragmentOnGpu)
13493  {
13494  bool defragmentOnGpu;
13495  // There is only one option to defragment this memory type.
13496  if(canDefragmentOnGpu != canDefragmentOnCpu)
13497  {
13498  defragmentOnGpu = canDefragmentOnGpu;
13499  }
13500  // Both options are available: Heuristics to choose the best one.
13501  else
13502  {
13503  defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13504  m_hAllocator->IsIntegratedGpu();
13505  }
13506 
13507  bool overlappingMoveSupported = !defragmentOnGpu;
13508 
13509  if(m_hAllocator->m_UseMutex)
13510  {
13512  {
13513  if(!m_Mutex.TryLockWrite())
13514  {
13515  pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13516  return;
13517  }
13518  }
13519  else
13520  {
13521  m_Mutex.LockWrite();
13522  pCtx->mutexLocked = true;
13523  }
13524  }
13525 
13526  pCtx->Begin(overlappingMoveSupported, flags);
13527 
13528  // Defragment.
13529 
13530  const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13531  const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13532  pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13533 
13534  // Accumulate statistics.
13535  if(pStats != VMA_NULL)
13536  {
13537  const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13538  const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13539  pStats->bytesMoved += bytesMoved;
13540  pStats->allocationsMoved += allocationsMoved;
13541  VMA_ASSERT(bytesMoved <= maxBytesToMove);
13542  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13543  if(defragmentOnGpu)
13544  {
13545  maxGpuBytesToMove -= bytesMoved;
13546  maxGpuAllocationsToMove -= allocationsMoved;
13547  }
13548  else
13549  {
13550  maxCpuBytesToMove -= bytesMoved;
13551  maxCpuAllocationsToMove -= allocationsMoved;
13552  }
13553  }
13554 
13556  {
13557  if(m_hAllocator->m_UseMutex)
13558  m_Mutex.UnlockWrite();
13559 
13560  if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13561  pCtx->res = VK_NOT_READY;
13562 
13563  return;
13564  }
13565 
13566  if(pCtx->res >= VK_SUCCESS)
13567  {
13568  if(defragmentOnGpu)
13569  {
13570  ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13571  }
13572  else
13573  {
13574  ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13575  }
13576  }
13577  }
13578 }
13579 
13580 void VmaBlockVector::DefragmentationEnd(
13581  class VmaBlockVectorDefragmentationContext* pCtx,
13582  uint32_t flags,
13583  VmaDefragmentationStats* pStats)
13584 {
13585  if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13586  {
13587  VMA_ASSERT(pCtx->mutexLocked == false);
13588 
13589  // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13590  // lock protecting us. Since we mutate state here, we have to take the lock out now
13591  m_Mutex.LockWrite();
13592  pCtx->mutexLocked = true;
13593  }
13594 
13595  // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13596  if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13597  {
13598  // Destroy buffers.
13599  for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13600  {
13601  VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13602  if(blockCtx.hBuffer)
13603  {
13604  (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13605  }
13606  }
13607 
13608  if(pCtx->res >= VK_SUCCESS)
13609  {
13610  FreeEmptyBlocks(pStats);
13611  }
13612  }
13613 
13614  if(pCtx->mutexLocked)
13615  {
13616  VMA_ASSERT(m_hAllocator->m_UseMutex);
13617  m_Mutex.UnlockWrite();
13618  }
13619 }
13620 
13621 uint32_t VmaBlockVector::ProcessDefragmentations(
13622  class VmaBlockVectorDefragmentationContext *pCtx,
13623  VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13624 {
13625  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13626 
13627  const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13628 
13629  for(uint32_t i = 0; i < moveCount; ++ i)
13630  {
13631  VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13632 
13633  pMove->allocation = move.hAllocation;
13634  pMove->memory = move.pDstBlock->GetDeviceMemory();
13635  pMove->offset = move.dstOffset;
13636 
13637  ++ pMove;
13638  }
13639 
13640  pCtx->defragmentationMovesProcessed += moveCount;
13641 
13642  return moveCount;
13643 }
13644 
13645 void VmaBlockVector::CommitDefragmentations(
13646  class VmaBlockVectorDefragmentationContext *pCtx,
13647  VmaDefragmentationStats* pStats)
13648 {
13649  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13650 
13651  for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13652  {
13653  const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13654 
13655  move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13656  move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13657  }
13658 
13659  pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13660  FreeEmptyBlocks(pStats);
13661 }
13662 
13663 size_t VmaBlockVector::CalcAllocationCount() const
13664 {
13665  size_t result = 0;
13666  for(size_t i = 0; i < m_Blocks.size(); ++i)
13667  {
13668  result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13669  }
13670  return result;
13671 }
13672 
13673 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13674 {
13675  if(m_BufferImageGranularity == 1)
13676  {
13677  return false;
13678  }
13679  VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13680  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13681  {
13682  VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13683  VMA_ASSERT(m_Algorithm == 0);
13684  VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13685  if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13686  {
13687  return true;
13688  }
13689  }
13690  return false;
13691 }
13692 
13693 void VmaBlockVector::MakePoolAllocationsLost(
13694  uint32_t currentFrameIndex,
13695  size_t* pLostAllocationCount)
13696 {
13697  VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13698  size_t lostAllocationCount = 0;
13699  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13700  {
13701  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13702  VMA_ASSERT(pBlock);
13703  lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13704  }
13705  if(pLostAllocationCount != VMA_NULL)
13706  {
13707  *pLostAllocationCount = lostAllocationCount;
13708  }
13709 }
13710 
13711 VkResult VmaBlockVector::CheckCorruption()
13712 {
13713  if(!IsCorruptionDetectionEnabled())
13714  {
13715  return VK_ERROR_FEATURE_NOT_PRESENT;
13716  }
13717 
13718  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13719  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13720  {
13721  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13722  VMA_ASSERT(pBlock);
13723  VkResult res = pBlock->CheckCorruption(m_hAllocator);
13724  if(res != VK_SUCCESS)
13725  {
13726  return res;
13727  }
13728  }
13729  return VK_SUCCESS;
13730 }
13731 
13732 void VmaBlockVector::AddStats(VmaStats* pStats)
13733 {
13734  const uint32_t memTypeIndex = m_MemoryTypeIndex;
13735  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13736 
13737  VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13738 
13739  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13740  {
13741  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13742  VMA_ASSERT(pBlock);
13743  VMA_HEAVY_ASSERT(pBlock->Validate());
13744  VmaStatInfo allocationStatInfo;
13745  pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13746  VmaAddStatInfo(pStats->total, allocationStatInfo);
13747  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13748  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13749  }
13750 }
13751 
13753 // VmaDefragmentationAlgorithm_Generic members definition
13754 
13755 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13756  VmaAllocator hAllocator,
13757  VmaBlockVector* pBlockVector,
13758  uint32_t currentFrameIndex,
13759  bool overlappingMoveSupported) :
13760  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13761  m_AllocationCount(0),
13762  m_AllAllocations(false),
13763  m_BytesMoved(0),
13764  m_AllocationsMoved(0),
13765  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13766 {
13767  // Create block info for each block.
13768  const size_t blockCount = m_pBlockVector->m_Blocks.size();
13769  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13770  {
13771  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13772  pBlockInfo->m_OriginalBlockIndex = blockIndex;
13773  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13774  m_Blocks.push_back(pBlockInfo);
13775  }
13776 
13777  // Sort them by m_pBlock pointer value.
13778  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13779 }
13780 
13781 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13782 {
13783  for(size_t i = m_Blocks.size(); i--; )
13784  {
13785  vma_delete(m_hAllocator, m_Blocks[i]);
13786  }
13787 }
13788 
13789 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13790 {
13791  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13792  if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13793  {
13794  VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13795  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13796  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13797  {
13798  AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13799  (*it)->m_Allocations.push_back(allocInfo);
13800  }
13801  else
13802  {
13803  VMA_ASSERT(0);
13804  }
13805 
13806  ++m_AllocationCount;
13807  }
13808 }
13809 
13810 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
13811  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13812  VkDeviceSize maxBytesToMove,
13813  uint32_t maxAllocationsToMove,
13814  bool freeOldAllocations)
13815 {
13816  if(m_Blocks.empty())
13817  {
13818  return VK_SUCCESS;
13819  }
13820 
13821  // This is a choice based on research.
13822  // Option 1:
13823  uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
13824  // Option 2:
13825  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
13826  // Option 3:
13827  //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
13828 
13829  size_t srcBlockMinIndex = 0;
13830  // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
13831  /*
13832  if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
13833  {
13834  const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
13835  if(blocksWithNonMovableCount > 0)
13836  {
13837  srcBlockMinIndex = blocksWithNonMovableCount - 1;
13838  }
13839  }
13840  */
13841 
13842  size_t srcBlockIndex = m_Blocks.size() - 1;
13843  size_t srcAllocIndex = SIZE_MAX;
13844  for(;;)
13845  {
13846  // 1. Find next allocation to move.
13847  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
13848  // 1.2. Then start from last to first m_Allocations.
13849  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
13850  {
13851  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
13852  {
13853  // Finished: no more allocations to process.
13854  if(srcBlockIndex == srcBlockMinIndex)
13855  {
13856  return VK_SUCCESS;
13857  }
13858  else
13859  {
13860  --srcBlockIndex;
13861  srcAllocIndex = SIZE_MAX;
13862  }
13863  }
13864  else
13865  {
13866  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13867  }
13868  }
13869 
13870  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13871  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13872 
13873  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13874  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13875  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13876  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13877 
13878  // 2. Try to find new place for this allocation in preceding or current block.
13879  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13880  {
13881  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13882  VmaAllocationRequest dstAllocRequest;
13883  if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13884  m_CurrentFrameIndex,
13885  m_pBlockVector->GetFrameInUseCount(),
13886  m_pBlockVector->GetBufferImageGranularity(),
13887  size,
13888  alignment,
13889  false, // upperAddress
13890  suballocType,
13891  false, // canMakeOtherLost
13892  strategy,
13893  &dstAllocRequest) &&
13894  MoveMakesSense(
13895  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13896  {
13897  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13898 
13899  // Reached limit on number of allocations or bytes to move.
13900  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13901  (m_BytesMoved + size > maxBytesToMove))
13902  {
13903  return VK_SUCCESS;
13904  }
13905 
13906  VmaDefragmentationMove move = {};
13907  move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13908  move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13909  move.srcOffset = srcOffset;
13910  move.dstOffset = dstAllocRequest.offset;
13911  move.size = size;
13912  move.hAllocation = allocInfo.m_hAllocation;
13913  move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13914  move.pDstBlock = pDstBlockInfo->m_pBlock;
13915 
13916  moves.push_back(move);
13917 
13918  pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
13919  dstAllocRequest,
13920  suballocType,
13921  size,
13922  allocInfo.m_hAllocation);
13923 
13924  if(freeOldAllocations)
13925  {
13926  pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13927  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13928  }
13929 
13930  if(allocInfo.m_pChanged != VMA_NULL)
13931  {
13932  *allocInfo.m_pChanged = VK_TRUE;
13933  }
13934 
13935  ++m_AllocationsMoved;
13936  m_BytesMoved += size;
13937 
13938  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13939 
13940  break;
13941  }
13942  }
13943 
13944  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13945 
13946  if(srcAllocIndex > 0)
13947  {
13948  --srcAllocIndex;
13949  }
13950  else
13951  {
13952  if(srcBlockIndex > 0)
13953  {
13954  --srcBlockIndex;
13955  srcAllocIndex = SIZE_MAX;
13956  }
13957  else
13958  {
13959  return VK_SUCCESS;
13960  }
13961  }
13962  }
13963 }
13964 
13965 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13966 {
13967  size_t result = 0;
13968  for(size_t i = 0; i < m_Blocks.size(); ++i)
13969  {
13970  if(m_Blocks[i]->m_HasNonMovableAllocations)
13971  {
13972  ++result;
13973  }
13974  }
13975  return result;
13976 }
13977 
13978 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13979  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13980  VkDeviceSize maxBytesToMove,
13981  uint32_t maxAllocationsToMove,
13983 {
13984  if(!m_AllAllocations && m_AllocationCount == 0)
13985  {
13986  return VK_SUCCESS;
13987  }
13988 
13989  const size_t blockCount = m_Blocks.size();
13990  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13991  {
13992  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13993 
13994  if(m_AllAllocations)
13995  {
13996  VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13997  for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13998  it != pMetadata->m_Suballocations.end();
13999  ++it)
14000  {
14001  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14002  {
14003  AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14004  pBlockInfo->m_Allocations.push_back(allocInfo);
14005  }
14006  }
14007  }
14008 
14009  pBlockInfo->CalcHasNonMovableAllocations();
14010 
14011  // This is a choice based on research.
14012  // Option 1:
14013  pBlockInfo->SortAllocationsByOffsetDescending();
14014  // Option 2:
14015  //pBlockInfo->SortAllocationsBySizeDescending();
14016  }
14017 
14018  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14019  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14020 
14021  // This is a choice based on research.
14022  const uint32_t roundCount = 2;
14023 
14024  // Execute defragmentation rounds (the main part).
14025  VkResult result = VK_SUCCESS;
14026  for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14027  {
14028  result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14029  }
14030 
14031  return result;
14032 }
14033 
14034 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14035  size_t dstBlockIndex, VkDeviceSize dstOffset,
14036  size_t srcBlockIndex, VkDeviceSize srcOffset)
14037 {
14038  if(dstBlockIndex < srcBlockIndex)
14039  {
14040  return true;
14041  }
14042  if(dstBlockIndex > srcBlockIndex)
14043  {
14044  return false;
14045  }
14046  if(dstOffset < srcOffset)
14047  {
14048  return true;
14049  }
14050  return false;
14051 }
14052 
14054 // VmaDefragmentationAlgorithm_Fast
14055 
14056 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14057  VmaAllocator hAllocator,
14058  VmaBlockVector* pBlockVector,
14059  uint32_t currentFrameIndex,
14060  bool overlappingMoveSupported) :
14061  VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14062  m_OverlappingMoveSupported(overlappingMoveSupported),
14063  m_AllocationCount(0),
14064  m_AllAllocations(false),
14065  m_BytesMoved(0),
14066  m_AllocationsMoved(0),
14067  m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14068 {
14069  VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14070 
14071 }
14072 
14073 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14074 {
14075 }
14076 
14077 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14078  VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14079  VkDeviceSize maxBytesToMove,
14080  uint32_t maxAllocationsToMove,
14082 {
14083  VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14084 
14085  const size_t blockCount = m_pBlockVector->GetBlockCount();
14086  if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14087  {
14088  return VK_SUCCESS;
14089  }
14090 
14091  PreprocessMetadata();
14092 
14093  // Sort blocks in order from most destination.
14094 
14095  m_BlockInfos.resize(blockCount);
14096  for(size_t i = 0; i < blockCount; ++i)
14097  {
14098  m_BlockInfos[i].origBlockIndex = i;
14099  }
14100 
14101  VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14102  return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14103  m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14104  });
14105 
14106  // THE MAIN ALGORITHM
14107 
14108  FreeSpaceDatabase freeSpaceDb;
14109 
14110  size_t dstBlockInfoIndex = 0;
14111  size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14112  VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14113  VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14114  VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14115  VkDeviceSize dstOffset = 0;
14116 
14117  bool end = false;
14118  for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14119  {
14120  const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14121  VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14122  VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14123  for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14124  !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14125  {
14126  VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14127  const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14128  const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14129  if(m_AllocationsMoved == maxAllocationsToMove ||
14130  m_BytesMoved + srcAllocSize > maxBytesToMove)
14131  {
14132  end = true;
14133  break;
14134  }
14135  const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14136 
14137  VmaDefragmentationMove move = {};
14138  // Try to place it in one of free spaces from the database.
14139  size_t freeSpaceInfoIndex;
14140  VkDeviceSize dstAllocOffset;
14141  if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14142  freeSpaceInfoIndex, dstAllocOffset))
14143  {
14144  size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14145  VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14146  VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14147 
14148  // Same block
14149  if(freeSpaceInfoIndex == srcBlockInfoIndex)
14150  {
14151  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14152 
14153  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14154 
14155  VmaSuballocation suballoc = *srcSuballocIt;
14156  suballoc.offset = dstAllocOffset;
14157  suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14158  m_BytesMoved += srcAllocSize;
14159  ++m_AllocationsMoved;
14160 
14161  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14162  ++nextSuballocIt;
14163  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14164  srcSuballocIt = nextSuballocIt;
14165 
14166  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14167 
14168  move.srcBlockIndex = srcOrigBlockIndex;
14169  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14170  move.srcOffset = srcAllocOffset;
14171  move.dstOffset = dstAllocOffset;
14172  move.size = srcAllocSize;
14173 
14174  moves.push_back(move);
14175  }
14176  // Different block
14177  else
14178  {
14179  // MOVE OPTION 2: Move the allocation to a different block.
14180 
14181  VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14182 
14183  VmaSuballocation suballoc = *srcSuballocIt;
14184  suballoc.offset = dstAllocOffset;
14185  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14186  m_BytesMoved += srcAllocSize;
14187  ++m_AllocationsMoved;
14188 
14189  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14190  ++nextSuballocIt;
14191  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14192  srcSuballocIt = nextSuballocIt;
14193 
14194  InsertSuballoc(pFreeSpaceMetadata, suballoc);
14195 
14196  move.srcBlockIndex = srcOrigBlockIndex;
14197  move.dstBlockIndex = freeSpaceOrigBlockIndex;
14198  move.srcOffset = srcAllocOffset;
14199  move.dstOffset = dstAllocOffset;
14200  move.size = srcAllocSize;
14201 
14202  moves.push_back(move);
14203  }
14204  }
14205  else
14206  {
14207  dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14208 
14209  // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14210  while(dstBlockInfoIndex < srcBlockInfoIndex &&
14211  dstAllocOffset + srcAllocSize > dstBlockSize)
14212  {
14213  // But before that, register remaining free space at the end of dst block.
14214  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14215 
14216  ++dstBlockInfoIndex;
14217  dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14218  pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14219  pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14220  dstBlockSize = pDstMetadata->GetSize();
14221  dstOffset = 0;
14222  dstAllocOffset = 0;
14223  }
14224 
14225  // Same block
14226  if(dstBlockInfoIndex == srcBlockInfoIndex)
14227  {
14228  VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14229 
14230  const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14231 
14232  bool skipOver = overlap;
14233  if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14234  {
14235  // If destination and source place overlap, skip if it would move it
14236  // by only < 1/64 of its size.
14237  skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14238  }
14239 
14240  if(skipOver)
14241  {
14242  freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14243 
14244  dstOffset = srcAllocOffset + srcAllocSize;
14245  ++srcSuballocIt;
14246  }
14247  // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14248  else
14249  {
14250  srcSuballocIt->offset = dstAllocOffset;
14251  srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14252  dstOffset = dstAllocOffset + srcAllocSize;
14253  m_BytesMoved += srcAllocSize;
14254  ++m_AllocationsMoved;
14255  ++srcSuballocIt;
14256 
14257  move.srcBlockIndex = srcOrigBlockIndex;
14258  move.dstBlockIndex = dstOrigBlockIndex;
14259  move.srcOffset = srcAllocOffset;
14260  move.dstOffset = dstAllocOffset;
14261  move.size = srcAllocSize;
14262 
14263  moves.push_back(move);
14264  }
14265  }
14266  // Different block
14267  else
14268  {
14269  // MOVE OPTION 2: Move the allocation to a different block.
14270 
14271  VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14272  VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14273 
14274  VmaSuballocation suballoc = *srcSuballocIt;
14275  suballoc.offset = dstAllocOffset;
14276  suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14277  dstOffset = dstAllocOffset + srcAllocSize;
14278  m_BytesMoved += srcAllocSize;
14279  ++m_AllocationsMoved;
14280 
14281  VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14282  ++nextSuballocIt;
14283  pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14284  srcSuballocIt = nextSuballocIt;
14285 
14286  pDstMetadata->m_Suballocations.push_back(suballoc);
14287 
14288  move.srcBlockIndex = srcOrigBlockIndex;
14289  move.dstBlockIndex = dstOrigBlockIndex;
14290  move.srcOffset = srcAllocOffset;
14291  move.dstOffset = dstAllocOffset;
14292  move.size = srcAllocSize;
14293 
14294  moves.push_back(move);
14295  }
14296  }
14297  }
14298  }
14299 
14300  m_BlockInfos.clear();
14301 
14302  PostprocessMetadata();
14303 
14304  return VK_SUCCESS;
14305 }
14306 
14307 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14308 {
14309  const size_t blockCount = m_pBlockVector->GetBlockCount();
14310  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14311  {
14312  VmaBlockMetadata_Generic* const pMetadata =
14313  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14314  pMetadata->m_FreeCount = 0;
14315  pMetadata->m_SumFreeSize = pMetadata->GetSize();
14316  pMetadata->m_FreeSuballocationsBySize.clear();
14317  for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14318  it != pMetadata->m_Suballocations.end(); )
14319  {
14320  if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14321  {
14322  VmaSuballocationList::iterator nextIt = it;
14323  ++nextIt;
14324  pMetadata->m_Suballocations.erase(it);
14325  it = nextIt;
14326  }
14327  else
14328  {
14329  ++it;
14330  }
14331  }
14332  }
14333 }
14334 
14335 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14336 {
14337  const size_t blockCount = m_pBlockVector->GetBlockCount();
14338  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14339  {
14340  VmaBlockMetadata_Generic* const pMetadata =
14341  (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14342  const VkDeviceSize blockSize = pMetadata->GetSize();
14343 
14344  // No allocations in this block - entire area is free.
14345  if(pMetadata->m_Suballocations.empty())
14346  {
14347  pMetadata->m_FreeCount = 1;
14348  //pMetadata->m_SumFreeSize is already set to blockSize.
14349  VmaSuballocation suballoc = {
14350  0, // offset
14351  blockSize, // size
14352  VMA_NULL, // hAllocation
14353  VMA_SUBALLOCATION_TYPE_FREE };
14354  pMetadata->m_Suballocations.push_back(suballoc);
14355  pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14356  }
14357  // There are some allocations in this block.
14358  else
14359  {
14360  VkDeviceSize offset = 0;
14361  VmaSuballocationList::iterator it;
14362  for(it = pMetadata->m_Suballocations.begin();
14363  it != pMetadata->m_Suballocations.end();
14364  ++it)
14365  {
14366  VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14367  VMA_ASSERT(it->offset >= offset);
14368 
14369  // Need to insert preceding free space.
14370  if(it->offset > offset)
14371  {
14372  ++pMetadata->m_FreeCount;
14373  const VkDeviceSize freeSize = it->offset - offset;
14374  VmaSuballocation suballoc = {
14375  offset, // offset
14376  freeSize, // size
14377  VMA_NULL, // hAllocation
14378  VMA_SUBALLOCATION_TYPE_FREE };
14379  VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14380  if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14381  {
14382  pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14383  }
14384  }
14385 
14386  pMetadata->m_SumFreeSize -= it->size;
14387  offset = it->offset + it->size;
14388  }
14389 
14390  // Need to insert trailing free space.
14391  if(offset < blockSize)
14392  {
14393  ++pMetadata->m_FreeCount;
14394  const VkDeviceSize freeSize = blockSize - offset;
14395  VmaSuballocation suballoc = {
14396  offset, // offset
14397  freeSize, // size
14398  VMA_NULL, // hAllocation
14399  VMA_SUBALLOCATION_TYPE_FREE };
14400  VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14401  VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14402  if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14403  {
14404  pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14405  }
14406  }
14407 
14408  VMA_SORT(
14409  pMetadata->m_FreeSuballocationsBySize.begin(),
14410  pMetadata->m_FreeSuballocationsBySize.end(),
14411  VmaSuballocationItemSizeLess());
14412  }
14413 
14414  VMA_HEAVY_ASSERT(pMetadata->Validate());
14415  }
14416 }
14417 
14418 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14419 {
14420  // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14421  VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14422  while(it != pMetadata->m_Suballocations.end())
14423  {
14424  if(it->offset < suballoc.offset)
14425  {
14426  ++it;
14427  }
14428  }
14429  pMetadata->m_Suballocations.insert(it, suballoc);
14430 }
14431 
14433 // VmaBlockVectorDefragmentationContext
14434 
14435 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14436  VmaAllocator hAllocator,
14437  VmaPool hCustomPool,
14438  VmaBlockVector* pBlockVector,
14439  uint32_t currFrameIndex) :
14440  res(VK_SUCCESS),
14441  mutexLocked(false),
14442  blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14443  defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14444  defragmentationMovesProcessed(0),
14445  defragmentationMovesCommitted(0),
14446  hasDefragmentationPlan(0),
14447  m_hAllocator(hAllocator),
14448  m_hCustomPool(hCustomPool),
14449  m_pBlockVector(pBlockVector),
14450  m_CurrFrameIndex(currFrameIndex),
14451  m_pAlgorithm(VMA_NULL),
14452  m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14453  m_AllAllocations(false)
14454 {
14455 }
14456 
14457 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14458 {
14459  vma_delete(m_hAllocator, m_pAlgorithm);
14460 }
14461 
14462 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14463 {
14464  AllocInfo info = { hAlloc, pChanged };
14465  m_Allocations.push_back(info);
14466 }
14467 
14468 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14469 {
14470  const bool allAllocations = m_AllAllocations ||
14471  m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14472 
14473  /********************************
14474  HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14475  ********************************/
14476 
14477  /*
14478  Fast algorithm is supported only when certain criteria are met:
14479  - VMA_DEBUG_MARGIN is 0.
14480  - All allocations in this block vector are moveable.
14481  - There is no possibility of image/buffer granularity conflict.
14482  - The defragmentation is not incremental
14483  */
14484  if(VMA_DEBUG_MARGIN == 0 &&
14485  allAllocations &&
14486  !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14488  {
14489  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14490  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14491  }
14492  else
14493  {
14494  m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14495  m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14496  }
14497 
14498  if(allAllocations)
14499  {
14500  m_pAlgorithm->AddAll();
14501  }
14502  else
14503  {
14504  for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14505  {
14506  m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14507  }
14508  }
14509 }
14510 
14512 // VmaDefragmentationContext
14513 
14514 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14515  VmaAllocator hAllocator,
14516  uint32_t currFrameIndex,
14517  uint32_t flags,
14518  VmaDefragmentationStats* pStats) :
14519  m_hAllocator(hAllocator),
14520  m_CurrFrameIndex(currFrameIndex),
14521  m_Flags(flags),
14522  m_pStats(pStats),
14523  m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14524 {
14525  memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14526 }
14527 
14528 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14529 {
14530  for(size_t i = m_CustomPoolContexts.size(); i--; )
14531  {
14532  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14533  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14534  vma_delete(m_hAllocator, pBlockVectorCtx);
14535  }
14536  for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14537  {
14538  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14539  if(pBlockVectorCtx)
14540  {
14541  pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14542  vma_delete(m_hAllocator, pBlockVectorCtx);
14543  }
14544  }
14545 }
14546 
14547 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14548 {
14549  for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14550  {
14551  VmaPool pool = pPools[poolIndex];
14552  VMA_ASSERT(pool);
14553  // Pools with algorithm other than default are not defragmented.
14554  if(pool->m_BlockVector.GetAlgorithm() == 0)
14555  {
14556  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14557 
14558  for(size_t i = m_CustomPoolContexts.size(); i--; )
14559  {
14560  if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14561  {
14562  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14563  break;
14564  }
14565  }
14566 
14567  if(!pBlockVectorDefragCtx)
14568  {
14569  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14570  m_hAllocator,
14571  pool,
14572  &pool->m_BlockVector,
14573  m_CurrFrameIndex);
14574  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14575  }
14576 
14577  pBlockVectorDefragCtx->AddAll();
14578  }
14579  }
14580 }
14581 
14582 void VmaDefragmentationContext_T::AddAllocations(
14583  uint32_t allocationCount,
14584  const VmaAllocation* pAllocations,
14585  VkBool32* pAllocationsChanged)
14586 {
14587  // Dispatch pAllocations among defragmentators. Create them when necessary.
14588  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14589  {
14590  const VmaAllocation hAlloc = pAllocations[allocIndex];
14591  VMA_ASSERT(hAlloc);
14592  // DedicatedAlloc cannot be defragmented.
14593  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14594  // Lost allocation cannot be defragmented.
14595  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14596  {
14597  VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14598 
14599  const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14600  // This allocation belongs to custom pool.
14601  if(hAllocPool != VK_NULL_HANDLE)
14602  {
14603  // Pools with algorithm other than default are not defragmented.
14604  if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14605  {
14606  for(size_t i = m_CustomPoolContexts.size(); i--; )
14607  {
14608  if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14609  {
14610  pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14611  break;
14612  }
14613  }
14614  if(!pBlockVectorDefragCtx)
14615  {
14616  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14617  m_hAllocator,
14618  hAllocPool,
14619  &hAllocPool->m_BlockVector,
14620  m_CurrFrameIndex);
14621  m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14622  }
14623  }
14624  }
14625  // This allocation belongs to default pool.
14626  else
14627  {
14628  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14629  pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14630  if(!pBlockVectorDefragCtx)
14631  {
14632  pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14633  m_hAllocator,
14634  VMA_NULL, // hCustomPool
14635  m_hAllocator->m_pBlockVectors[memTypeIndex],
14636  m_CurrFrameIndex);
14637  m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14638  }
14639  }
14640 
14641  if(pBlockVectorDefragCtx)
14642  {
14643  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14644  &pAllocationsChanged[allocIndex] : VMA_NULL;
14645  pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14646  }
14647  }
14648  }
14649 }
14650 
14651 VkResult VmaDefragmentationContext_T::Defragment(
14652  VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14653  VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14654  VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14655 {
14656  if(pStats)
14657  {
14658  memset(pStats, 0, sizeof(VmaDefragmentationStats));
14659  }
14660 
14662  {
14663  // For incremental defragmetnations, we just earmark how much we can move
14664  // The real meat is in the defragmentation steps
14665  m_MaxCpuBytesToMove = maxCpuBytesToMove;
14666  m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14667 
14668  m_MaxGpuBytesToMove = maxGpuBytesToMove;
14669  m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14670 
14671  if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14672  m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14673  return VK_SUCCESS;
14674 
14675  return VK_NOT_READY;
14676  }
14677 
14678  if(commandBuffer == VK_NULL_HANDLE)
14679  {
14680  maxGpuBytesToMove = 0;
14681  maxGpuAllocationsToMove = 0;
14682  }
14683 
14684  VkResult res = VK_SUCCESS;
14685 
14686  // Process default pools.
14687  for(uint32_t memTypeIndex = 0;
14688  memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14689  ++memTypeIndex)
14690  {
14691  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14692  if(pBlockVectorCtx)
14693  {
14694  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14695  pBlockVectorCtx->GetBlockVector()->Defragment(
14696  pBlockVectorCtx,
14697  pStats, flags,
14698  maxCpuBytesToMove, maxCpuAllocationsToMove,
14699  maxGpuBytesToMove, maxGpuAllocationsToMove,
14700  commandBuffer);
14701  if(pBlockVectorCtx->res != VK_SUCCESS)
14702  {
14703  res = pBlockVectorCtx->res;
14704  }
14705  }
14706  }
14707 
14708  // Process custom pools.
14709  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14710  customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14711  ++customCtxIndex)
14712  {
14713  VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14714  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14715  pBlockVectorCtx->GetBlockVector()->Defragment(
14716  pBlockVectorCtx,
14717  pStats, flags,
14718  maxCpuBytesToMove, maxCpuAllocationsToMove,
14719  maxGpuBytesToMove, maxGpuAllocationsToMove,
14720  commandBuffer);
14721  if(pBlockVectorCtx->res != VK_SUCCESS)
14722  {
14723  res = pBlockVectorCtx->res;
14724  }
14725  }
14726 
14727  return res;
14728 }
14729 
14730 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14731 {
14732  VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14733  uint32_t movesLeft = pInfo->moveCount;
14734 
14735  // Process default pools.
14736  for(uint32_t memTypeIndex = 0;
14737  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14738  ++memTypeIndex)
14739  {
14740  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14741  if(pBlockVectorCtx)
14742  {
14743  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14744 
14745  if(!pBlockVectorCtx->hasDefragmentationPlan)
14746  {
14747  pBlockVectorCtx->GetBlockVector()->Defragment(
14748  pBlockVectorCtx,
14749  m_pStats, m_Flags,
14750  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14751  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14752  VK_NULL_HANDLE);
14753 
14754  if(pBlockVectorCtx->res < VK_SUCCESS)
14755  continue;
14756 
14757  pBlockVectorCtx->hasDefragmentationPlan = true;
14758  }
14759 
14760  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14761  pBlockVectorCtx,
14762  pCurrentMove, movesLeft);
14763 
14764  movesLeft -= processed;
14765  pCurrentMove += processed;
14766  }
14767  }
14768 
14769  // Process custom pools.
14770  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14771  customCtxIndex < customCtxCount;
14772  ++customCtxIndex)
14773  {
14774  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14775  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14776 
14777  if(!pBlockVectorCtx->hasDefragmentationPlan)
14778  {
14779  pBlockVectorCtx->GetBlockVector()->Defragment(
14780  pBlockVectorCtx,
14781  m_pStats, m_Flags,
14782  m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14783  m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14784  VK_NULL_HANDLE);
14785 
14786  if(pBlockVectorCtx->res < VK_SUCCESS)
14787  continue;
14788 
14789  pBlockVectorCtx->hasDefragmentationPlan = true;
14790  }
14791 
14792  const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14793  pBlockVectorCtx,
14794  pCurrentMove, movesLeft);
14795 
14796  movesLeft -= processed;
14797  pCurrentMove += processed;
14798  }
14799 
14800  pInfo->moveCount = pInfo->moveCount - movesLeft;
14801 
14802  return VK_SUCCESS;
14803 }
14804 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
14805 {
14806  VkResult res = VK_SUCCESS;
14807 
14808  // Process default pools.
14809  for(uint32_t memTypeIndex = 0;
14810  memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14811  ++memTypeIndex)
14812  {
14813  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14814  if(pBlockVectorCtx)
14815  {
14816  VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14817 
14818  if(!pBlockVectorCtx->hasDefragmentationPlan)
14819  {
14820  res = VK_NOT_READY;
14821  continue;
14822  }
14823 
14824  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14825  pBlockVectorCtx, m_pStats);
14826 
14827  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14828  res = VK_NOT_READY;
14829  }
14830  }
14831 
14832  // Process custom pools.
14833  for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14834  customCtxIndex < customCtxCount;
14835  ++customCtxIndex)
14836  {
14837  VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14838  VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14839 
14840  if(!pBlockVectorCtx->hasDefragmentationPlan)
14841  {
14842  res = VK_NOT_READY;
14843  continue;
14844  }
14845 
14846  pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14847  pBlockVectorCtx, m_pStats);
14848 
14849  if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14850  res = VK_NOT_READY;
14851  }
14852 
14853  return res;
14854 }
14855 
14857 // VmaRecorder
14858 
14859 #if VMA_RECORDING_ENABLED
14860 
14861 VmaRecorder::VmaRecorder() :
14862  m_UseMutex(true),
14863  m_Flags(0),
14864  m_File(VMA_NULL),
14865  m_RecordingStartTime(std::chrono::high_resolution_clock::now())
14866 {
14867 }
14868 
14869 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
14870 {
14871  m_UseMutex = useMutex;
14872  m_Flags = settings.flags;
14873 
14874 #if defined(_WIN32)
14875  // Open file for writing.
14876  errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14877 
14878  if(err != 0)
14879  {
14880  return VK_ERROR_INITIALIZATION_FAILED;
14881  }
14882 #else
14883  // Open file for writing.
14884  m_File = fopen(settings.pFilePath, "wb");
14885 
14886  if(m_File == 0)
14887  {
14888  return VK_ERROR_INITIALIZATION_FAILED;
14889  }
14890 #endif
14891 
14892  // Write header.
14893  fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14894  fprintf(m_File, "%s\n", "1,8");
14895 
14896  return VK_SUCCESS;
14897 }
14898 
14899 VmaRecorder::~VmaRecorder()
14900 {
14901  if(m_File != VMA_NULL)
14902  {
14903  fclose(m_File);
14904  }
14905 }
14906 
14907 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14908 {
14909  CallParams callParams;
14910  GetBasicParams(callParams);
14911 
14912  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14913  fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14914  Flush();
14915 }
14916 
14917 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14918 {
14919  CallParams callParams;
14920  GetBasicParams(callParams);
14921 
14922  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14923  fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14924  Flush();
14925 }
14926 
14927 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14928 {
14929  CallParams callParams;
14930  GetBasicParams(callParams);
14931 
14932  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14933  fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14934  createInfo.memoryTypeIndex,
14935  createInfo.flags,
14936  createInfo.blockSize,
14937  (uint64_t)createInfo.minBlockCount,
14938  (uint64_t)createInfo.maxBlockCount,
14939  createInfo.frameInUseCount,
14940  pool);
14941  Flush();
14942 }
14943 
14944 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14945 {
14946  CallParams callParams;
14947  GetBasicParams(callParams);
14948 
14949  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14950  fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14951  pool);
14952  Flush();
14953 }
14954 
14955 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14956  const VkMemoryRequirements& vkMemReq,
14957  const VmaAllocationCreateInfo& createInfo,
14958  VmaAllocation allocation)
14959 {
14960  CallParams callParams;
14961  GetBasicParams(callParams);
14962 
14963  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14964  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14965  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14966  vkMemReq.size,
14967  vkMemReq.alignment,
14968  vkMemReq.memoryTypeBits,
14969  createInfo.flags,
14970  createInfo.usage,
14971  createInfo.requiredFlags,
14972  createInfo.preferredFlags,
14973  createInfo.memoryTypeBits,
14974  createInfo.pool,
14975  allocation,
14976  userDataStr.GetString());
14977  Flush();
14978 }
14979 
14980 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14981  const VkMemoryRequirements& vkMemReq,
14982  const VmaAllocationCreateInfo& createInfo,
14983  uint64_t allocationCount,
14984  const VmaAllocation* pAllocations)
14985 {
14986  CallParams callParams;
14987  GetBasicParams(callParams);
14988 
14989  VmaMutexLock lock(m_FileMutex, m_UseMutex);
14990  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14991  fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14992  vkMemReq.size,
14993  vkMemReq.alignment,
14994  vkMemReq.memoryTypeBits,
14995  createInfo.flags,
14996  createInfo.usage,
14997  createInfo.requiredFlags,
14998  createInfo.preferredFlags,
14999  createInfo.memoryTypeBits,
15000  createInfo.pool);
15001  PrintPointerList(allocationCount, pAllocations);
15002  fprintf(m_File, ",%s\n", userDataStr.GetString());
15003  Flush();
15004 }
15005 
15006 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15007  const VkMemoryRequirements& vkMemReq,
15008  bool requiresDedicatedAllocation,
15009  bool prefersDedicatedAllocation,
15010  const VmaAllocationCreateInfo& createInfo,
15011  VmaAllocation allocation)
15012 {
15013  CallParams callParams;
15014  GetBasicParams(callParams);
15015 
15016  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15017  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15018  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,
15019  vkMemReq.size,
15020  vkMemReq.alignment,
15021  vkMemReq.memoryTypeBits,
15022  requiresDedicatedAllocation ? 1 : 0,
15023  prefersDedicatedAllocation ? 1 : 0,
15024  createInfo.flags,
15025  createInfo.usage,
15026  createInfo.requiredFlags,
15027  createInfo.preferredFlags,
15028  createInfo.memoryTypeBits,
15029  createInfo.pool,
15030  allocation,
15031  userDataStr.GetString());
15032  Flush();
15033 }
15034 
15035 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15036  const VkMemoryRequirements& vkMemReq,
15037  bool requiresDedicatedAllocation,
15038  bool prefersDedicatedAllocation,
15039  const VmaAllocationCreateInfo& createInfo,
15040  VmaAllocation allocation)
15041 {
15042  CallParams callParams;
15043  GetBasicParams(callParams);
15044 
15045  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15046  UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15047  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,
15048  vkMemReq.size,
15049  vkMemReq.alignment,
15050  vkMemReq.memoryTypeBits,
15051  requiresDedicatedAllocation ? 1 : 0,
15052  prefersDedicatedAllocation ? 1 : 0,
15053  createInfo.flags,
15054  createInfo.usage,
15055  createInfo.requiredFlags,
15056  createInfo.preferredFlags,
15057  createInfo.memoryTypeBits,
15058  createInfo.pool,
15059  allocation,
15060  userDataStr.GetString());
15061  Flush();
15062 }
15063 
15064 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15065  VmaAllocation allocation)
15066 {
15067  CallParams callParams;
15068  GetBasicParams(callParams);
15069 
15070  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15071  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15072  allocation);
15073  Flush();
15074 }
15075 
15076 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15077  uint64_t allocationCount,
15078  const VmaAllocation* pAllocations)
15079 {
15080  CallParams callParams;
15081  GetBasicParams(callParams);
15082 
15083  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15084  fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15085  PrintPointerList(allocationCount, pAllocations);
15086  fprintf(m_File, "\n");
15087  Flush();
15088 }
15089 
15090 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15091  VmaAllocation allocation,
15092  const void* pUserData)
15093 {
15094  CallParams callParams;
15095  GetBasicParams(callParams);
15096 
15097  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15098  UserDataString userDataStr(
15099  allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15100  pUserData);
15101  fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15102  allocation,
15103  userDataStr.GetString());
15104  Flush();
15105 }
15106 
15107 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15108  VmaAllocation allocation)
15109 {
15110  CallParams callParams;
15111  GetBasicParams(callParams);
15112 
15113  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15114  fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15115  allocation);
15116  Flush();
15117 }
15118 
15119 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15120  VmaAllocation allocation)
15121 {
15122  CallParams callParams;
15123  GetBasicParams(callParams);
15124 
15125  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15126  fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15127  allocation);
15128  Flush();
15129 }
15130 
15131 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15132  VmaAllocation allocation)
15133 {
15134  CallParams callParams;
15135  GetBasicParams(callParams);
15136 
15137  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15138  fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15139  allocation);
15140  Flush();
15141 }
15142 
15143 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15144  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15145 {
15146  CallParams callParams;
15147  GetBasicParams(callParams);
15148 
15149  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15150  fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15151  allocation,
15152  offset,
15153  size);
15154  Flush();
15155 }
15156 
15157 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15158  VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15159 {
15160  CallParams callParams;
15161  GetBasicParams(callParams);
15162 
15163  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15164  fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15165  allocation,
15166  offset,
15167  size);
15168  Flush();
15169 }
15170 
15171 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15172  const VkBufferCreateInfo& bufCreateInfo,
15173  const VmaAllocationCreateInfo& allocCreateInfo,
15174  VmaAllocation allocation)
15175 {
15176  CallParams callParams;
15177  GetBasicParams(callParams);
15178 
15179  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15180  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15181  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,
15182  bufCreateInfo.flags,
15183  bufCreateInfo.size,
15184  bufCreateInfo.usage,
15185  bufCreateInfo.sharingMode,
15186  allocCreateInfo.flags,
15187  allocCreateInfo.usage,
15188  allocCreateInfo.requiredFlags,
15189  allocCreateInfo.preferredFlags,
15190  allocCreateInfo.memoryTypeBits,
15191  allocCreateInfo.pool,
15192  allocation,
15193  userDataStr.GetString());
15194  Flush();
15195 }
15196 
15197 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15198  const VkImageCreateInfo& imageCreateInfo,
15199  const VmaAllocationCreateInfo& allocCreateInfo,
15200  VmaAllocation allocation)
15201 {
15202  CallParams callParams;
15203  GetBasicParams(callParams);
15204 
15205  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15206  UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15207  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,
15208  imageCreateInfo.flags,
15209  imageCreateInfo.imageType,
15210  imageCreateInfo.format,
15211  imageCreateInfo.extent.width,
15212  imageCreateInfo.extent.height,
15213  imageCreateInfo.extent.depth,
15214  imageCreateInfo.mipLevels,
15215  imageCreateInfo.arrayLayers,
15216  imageCreateInfo.samples,
15217  imageCreateInfo.tiling,
15218  imageCreateInfo.usage,
15219  imageCreateInfo.sharingMode,
15220  imageCreateInfo.initialLayout,
15221  allocCreateInfo.flags,
15222  allocCreateInfo.usage,
15223  allocCreateInfo.requiredFlags,
15224  allocCreateInfo.preferredFlags,
15225  allocCreateInfo.memoryTypeBits,
15226  allocCreateInfo.pool,
15227  allocation,
15228  userDataStr.GetString());
15229  Flush();
15230 }
15231 
15232 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15233  VmaAllocation allocation)
15234 {
15235  CallParams callParams;
15236  GetBasicParams(callParams);
15237 
15238  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15239  fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15240  allocation);
15241  Flush();
15242 }
15243 
15244 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15245  VmaAllocation allocation)
15246 {
15247  CallParams callParams;
15248  GetBasicParams(callParams);
15249 
15250  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15251  fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15252  allocation);
15253  Flush();
15254 }
15255 
15256 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15257  VmaAllocation allocation)
15258 {
15259  CallParams callParams;
15260  GetBasicParams(callParams);
15261 
15262  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15263  fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15264  allocation);
15265  Flush();
15266 }
15267 
15268 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15269  VmaAllocation allocation)
15270 {
15271  CallParams callParams;
15272  GetBasicParams(callParams);
15273 
15274  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15275  fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15276  allocation);
15277  Flush();
15278 }
15279 
15280 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15281  VmaPool pool)
15282 {
15283  CallParams callParams;
15284  GetBasicParams(callParams);
15285 
15286  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15287  fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15288  pool);
15289  Flush();
15290 }
15291 
15292 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15293  const VmaDefragmentationInfo2& info,
15295 {
15296  CallParams callParams;
15297  GetBasicParams(callParams);
15298 
15299  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15300  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15301  info.flags);
15302  PrintPointerList(info.allocationCount, info.pAllocations);
15303  fprintf(m_File, ",");
15304  PrintPointerList(info.poolCount, info.pPools);
15305  fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15306  info.maxCpuBytesToMove,
15308  info.maxGpuBytesToMove,
15310  info.commandBuffer,
15311  ctx);
15312  Flush();
15313 }
15314 
15315 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15317 {
15318  CallParams callParams;
15319  GetBasicParams(callParams);
15320 
15321  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15322  fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15323  ctx);
15324  Flush();
15325 }
15326 
15327 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15328  VmaPool pool,
15329  const char* name)
15330 {
15331  CallParams callParams;
15332  GetBasicParams(callParams);
15333 
15334  VmaMutexLock lock(m_FileMutex, m_UseMutex);
15335  fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15336  pool, name != VMA_NULL ? name : "");
15337  Flush();
15338 }
15339 
15340 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15341 {
15342  if(pUserData != VMA_NULL)
15343  {
15344  if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15345  {
15346  m_Str = (const char*)pUserData;
15347  }
15348  else
15349  {
15350  // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15351  snprintf(m_PtrStr, 17, "%p", pUserData);
15352  m_Str = m_PtrStr;
15353  }
15354  }
15355  else
15356  {
15357  m_Str = "";
15358  }
15359 }
15360 
15361 void VmaRecorder::WriteConfiguration(
15362  const VkPhysicalDeviceProperties& devProps,
15363  const VkPhysicalDeviceMemoryProperties& memProps,
15364  uint32_t vulkanApiVersion,
15365  bool dedicatedAllocationExtensionEnabled,
15366  bool bindMemory2ExtensionEnabled,
15367  bool memoryBudgetExtensionEnabled,
15368  bool deviceCoherentMemoryExtensionEnabled)
15369 {
15370  fprintf(m_File, "Config,Begin\n");
15371 
15372  fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15373 
15374  fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15375  fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15376  fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15377  fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15378  fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15379  fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15380 
15381  fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15382  fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15383  fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15384 
15385  fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15386  for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15387  {
15388  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15389  fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15390  }
15391  fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15392  for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15393  {
15394  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15395  fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15396  }
15397 
15398  fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15399  fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15400  fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15401  fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15402 
15403  fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15404  fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15405  fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15406  fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15407  fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15408  fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15409  fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15410  fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15411  fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15412 
15413  fprintf(m_File, "Config,End\n");
15414 }
15415 
15416 void VmaRecorder::GetBasicParams(CallParams& outParams)
15417 {
15418  #if defined(_WIN32)
15419  outParams.threadId = GetCurrentThreadId();
15420  #else
15421  // Use C++11 features to get thread id and convert it to uint32_t.
15422  // There is room for optimization since sstream is quite slow.
15423  // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15424  std::thread::id thread_id = std::this_thread::get_id();
15425  stringstream thread_id_to_string_converter;
15426  thread_id_to_string_converter << thread_id;
15427  string thread_id_as_string = thread_id_to_string_converter.str();
15428  outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15429  #endif
15430 
15431  auto current_time = std::chrono::high_resolution_clock::now();
15432 
15433  outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15434 }
15435 
15436 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15437 {
15438  if(count)
15439  {
15440  fprintf(m_File, "%p", pItems[0]);
15441  for(uint64_t i = 1; i < count; ++i)
15442  {
15443  fprintf(m_File, " %p", pItems[i]);
15444  }
15445  }
15446 }
15447 
15448 void VmaRecorder::Flush()
15449 {
15450  if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15451  {
15452  fflush(m_File);
15453  }
15454 }
15455 
15456 #endif // #if VMA_RECORDING_ENABLED
15457 
15459 // VmaAllocationObjectAllocator
15460 
15461 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15462  m_Allocator(pAllocationCallbacks, 1024)
15463 {
15464 }
15465 
15466 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15467 {
15468  VmaMutexLock mutexLock(m_Mutex);
15469  return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15470 }
15471 
15472 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15473 {
15474  VmaMutexLock mutexLock(m_Mutex);
15475  m_Allocator.Free(hAlloc);
15476 }
15477 
15479 // VmaAllocator_T
15480 
15481 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15482  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15483  m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15484  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15485  m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15486  m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15487  m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15488  m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15489  m_hDevice(pCreateInfo->device),
15490  m_hInstance(pCreateInfo->instance),
15491  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15492  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15493  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15494  m_AllocationObjectAllocator(&m_AllocationCallbacks),
15495  m_HeapSizeLimitMask(0),
15496  m_PreferredLargeHeapBlockSize(0),
15497  m_PhysicalDevice(pCreateInfo->physicalDevice),
15498  m_CurrentFrameIndex(0),
15499  m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15500  m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15501  m_NextPoolId(0),
15502  m_GlobalMemoryTypeBits(UINT32_MAX)
15504  ,m_pRecorder(VMA_NULL)
15505 #endif
15506 {
15507  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15508  {
15509  m_UseKhrDedicatedAllocation = false;
15510  m_UseKhrBindMemory2 = false;
15511  }
15512 
15513  if(VMA_DEBUG_DETECT_CORRUPTION)
15514  {
15515  // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15516  VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15517  }
15518 
15519  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15520 
15521  if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15522  {
15523 #if !(VMA_DEDICATED_ALLOCATION)
15525  {
15526  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15527  }
15528 #endif
15529 #if !(VMA_BIND_MEMORY2)
15530  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15531  {
15532  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15533  }
15534 #endif
15535  }
15536 #if !(VMA_MEMORY_BUDGET)
15537  if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15538  {
15539  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15540  }
15541 #endif
15542 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15543  if(m_UseKhrBufferDeviceAddress)
15544  {
15545  VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
15546  }
15547 #endif
15548 #if VMA_VULKAN_VERSION < 1002000
15549  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15550  {
15551  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15552  }
15553 #endif
15554 #if VMA_VULKAN_VERSION < 1001000
15555  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15556  {
15557  VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15558  }
15559 #endif
15560 
15561  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15562  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15563  memset(&m_MemProps, 0, sizeof(m_MemProps));
15564 
15565  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15566  memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15567  memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15568 
15569  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15570  {
15571  m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15572  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15573  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15574  }
15575 
15576  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15577 
15578  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15579  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15580 
15581  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15582  VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15583  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15584  VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15585 
15586  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15587  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15588 
15589  m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15590 
15591  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15592  {
15593  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15594  {
15595  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15596  if(limit != VK_WHOLE_SIZE)
15597  {
15598  m_HeapSizeLimitMask |= 1u << heapIndex;
15599  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15600  {
15601  m_MemProps.memoryHeaps[heapIndex].size = limit;
15602  }
15603  }
15604  }
15605  }
15606 
15607  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15608  {
15609  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15610 
15611  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15612  this,
15613  VK_NULL_HANDLE, // hParentPool
15614  memTypeIndex,
15615  preferredBlockSize,
15616  0,
15617  SIZE_MAX,
15618  GetBufferImageGranularity(),
15619  pCreateInfo->frameInUseCount,
15620  false, // explicitBlockSize
15621  false); // linearAlgorithm
15622  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15623  // becase minBlockCount is 0.
15624  m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15625 
15626  }
15627 }
15628 
15629 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15630 {
15631  VkResult res = VK_SUCCESS;
15632 
15633  if(pCreateInfo->pRecordSettings != VMA_NULL &&
15634  !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15635  {
15636 #if VMA_RECORDING_ENABLED
15637  m_pRecorder = vma_new(this, VmaRecorder)();
15638  res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15639  if(res != VK_SUCCESS)
15640  {
15641  return res;
15642  }
15643  m_pRecorder->WriteConfiguration(
15644  m_PhysicalDeviceProperties,
15645  m_MemProps,
15646  m_VulkanApiVersion,
15647  m_UseKhrDedicatedAllocation,
15648  m_UseKhrBindMemory2,
15649  m_UseExtMemoryBudget,
15650  m_UseAmdDeviceCoherentMemory);
15651  m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15652 #else
15653  VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15654  return VK_ERROR_FEATURE_NOT_PRESENT;
15655 #endif
15656  }
15657 
15658 #if VMA_MEMORY_BUDGET
15659  if(m_UseExtMemoryBudget)
15660  {
15661  UpdateVulkanBudget();
15662  }
15663 #endif // #if VMA_MEMORY_BUDGET
15664 
15665  return res;
15666 }
15667 
15668 VmaAllocator_T::~VmaAllocator_T()
15669 {
15670 #if VMA_RECORDING_ENABLED
15671  if(m_pRecorder != VMA_NULL)
15672  {
15673  m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15674  vma_delete(this, m_pRecorder);
15675  }
15676 #endif
15677 
15678  VMA_ASSERT(m_Pools.empty());
15679 
15680  for(size_t i = GetMemoryTypeCount(); i--; )
15681  {
15682  if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15683  {
15684  VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15685  }
15686 
15687  vma_delete(this, m_pDedicatedAllocations[i]);
15688  vma_delete(this, m_pBlockVectors[i]);
15689  }
15690 }
15691 
15692 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15693 {
15694 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15695  ImportVulkanFunctions_Static();
15696 #endif
15697 
15698  if(pVulkanFunctions != VMA_NULL)
15699  {
15700  ImportVulkanFunctions_Custom(pVulkanFunctions);
15701  }
15702 
15703 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15704  ImportVulkanFunctions_Dynamic();
15705 #endif
15706 
15707  ValidateVulkanFunctions();
15708 }
15709 
15710 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15711 
15712 void VmaAllocator_T::ImportVulkanFunctions_Static()
15713 {
15714  // Vulkan 1.0
15715  m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15716  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15717  m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15718  m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15719  m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15720  m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15721  m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15722  m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15723  m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15724  m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15725  m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15726  m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15727  m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15728  m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15729  m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15730  m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15731  m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15732 
15733  // Vulkan 1.1
15734 #if VMA_VULKAN_VERSION >= 1001000
15735  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15736  {
15737  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15738  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15739  m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15740  m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15741  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15742  }
15743 #endif
15744 }
15745 
15746 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15747 
15748 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15749 {
15750  VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15751 
15752 #define VMA_COPY_IF_NOT_NULL(funcName) \
15753  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15754 
15755  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15756  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15757  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15758  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15759  VMA_COPY_IF_NOT_NULL(vkMapMemory);
15760  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15761  VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15762  VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15763  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15764  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15765  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15766  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15767  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15768  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15769  VMA_COPY_IF_NOT_NULL(vkCreateImage);
15770  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15771  VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15772 
15773 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15774  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15775  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15776 #endif
15777 
15778 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15779  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15780  VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15781 #endif
15782 
15783 #if VMA_MEMORY_BUDGET
15784  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15785 #endif
15786 
15787 #undef VMA_COPY_IF_NOT_NULL
15788 }
15789 
15790 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15791 
15792 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
15793 {
15794 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
15795  if(m_VulkanFunctions.memberName == VMA_NULL) \
15796  m_VulkanFunctions.memberName = \
15797  (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
15798 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
15799  if(m_VulkanFunctions.memberName == VMA_NULL) \
15800  m_VulkanFunctions.memberName = \
15801  (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
15802 
15803  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
15804  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
15805  VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
15806  VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
15807  VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
15808  VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
15809  VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
15810  VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
15811  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
15812  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
15813  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
15814  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
15815  VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
15816  VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
15817  VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
15818  VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
15819  VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
15820 
15821 #if VMA_VULKAN_VERSION >= 1001000
15822  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15823  {
15824  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
15825  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
15826  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
15827  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
15828  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
15829  }
15830 #endif
15831 
15832 #if VMA_DEDICATED_ALLOCATION
15833  if(m_UseKhrDedicatedAllocation)
15834  {
15835  VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
15836  VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
15837  }
15838 #endif
15839 
15840 #if VMA_BIND_MEMORY2
15841  if(m_UseKhrBindMemory2)
15842  {
15843  VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
15844  VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
15845  }
15846 #endif // #if VMA_BIND_MEMORY2
15847 
15848 #if VMA_MEMORY_BUDGET
15849  if(m_UseExtMemoryBudget)
15850  {
15851  VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
15852  }
15853 #endif // #if VMA_MEMORY_BUDGET
15854 
15855 #undef VMA_FETCH_DEVICE_FUNC
15856 #undef VMA_FETCH_INSTANCE_FUNC
15857 }
15858 
15859 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15860 
15861 void VmaAllocator_T::ValidateVulkanFunctions()
15862 {
15863  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15864  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15865  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15866  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15867  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15868  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15869  VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15870  VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15871  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15872  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15873  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15874  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15875  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15876  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15877  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15878  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15879  VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15880 
15881 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15882  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15883  {
15884  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15885  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15886  }
15887 #endif
15888 
15889 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15890  if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15891  {
15892  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15893  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15894  }
15895 #endif
15896 
15897 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15898  if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15899  {
15900  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15901  }
15902 #endif
15903 }
15904 
15905 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15906 {
15907  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15908  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15909  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15910  return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15911 }
15912 
15913 VkResult VmaAllocator_T::AllocateMemoryOfType(
15914  VkDeviceSize size,
15915  VkDeviceSize alignment,
15916  bool dedicatedAllocation,
15917  VkBuffer dedicatedBuffer,
15918  VkBufferUsageFlags dedicatedBufferUsage,
15919  VkImage dedicatedImage,
15920  const VmaAllocationCreateInfo& createInfo,
15921  uint32_t memTypeIndex,
15922  VmaSuballocationType suballocType,
15923  size_t allocationCount,
15924  VmaAllocation* pAllocations)
15925 {
15926  VMA_ASSERT(pAllocations != VMA_NULL);
15927  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15928 
15929  VmaAllocationCreateInfo finalCreateInfo = createInfo;
15930 
15931  // If memory type is not HOST_VISIBLE, disable MAPPED.
15932  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15933  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15934  {
15935  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15936  }
15937  // If memory is lazily allocated, it should be always dedicated.
15938  if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15939  {
15941  }
15942 
15943  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15944  VMA_ASSERT(blockVector);
15945 
15946  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15947  bool preferDedicatedMemory =
15948  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15949  dedicatedAllocation ||
15950  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15951  size > preferredBlockSize / 2;
15952 
15953  if(preferDedicatedMemory &&
15954  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15955  finalCreateInfo.pool == VK_NULL_HANDLE)
15956  {
15958  }
15959 
15960  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15961  {
15962  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15963  {
15964  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15965  }
15966  else
15967  {
15968  return AllocateDedicatedMemory(
15969  size,
15970  suballocType,
15971  memTypeIndex,
15972  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15973  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15974  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15975  finalCreateInfo.pUserData,
15976  dedicatedBuffer,
15977  dedicatedBufferUsage,
15978  dedicatedImage,
15979  allocationCount,
15980  pAllocations);
15981  }
15982  }
15983  else
15984  {
15985  VkResult res = blockVector->Allocate(
15986  m_CurrentFrameIndex.load(),
15987  size,
15988  alignment,
15989  finalCreateInfo,
15990  suballocType,
15991  allocationCount,
15992  pAllocations);
15993  if(res == VK_SUCCESS)
15994  {
15995  return res;
15996  }
15997 
15998  // 5. Try dedicated memory.
15999  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16000  {
16001  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16002  }
16003  else
16004  {
16005  res = AllocateDedicatedMemory(
16006  size,
16007  suballocType,
16008  memTypeIndex,
16009  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16010  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16011  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16012  finalCreateInfo.pUserData,
16013  dedicatedBuffer,
16014  dedicatedBufferUsage,
16015  dedicatedImage,
16016  allocationCount,
16017  pAllocations);
16018  if(res == VK_SUCCESS)
16019  {
16020  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16021  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16022  return VK_SUCCESS;
16023  }
16024  else
16025  {
16026  // Everything failed: Return error code.
16027  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16028  return res;
16029  }
16030  }
16031  }
16032 }
16033 
16034 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16035  VkDeviceSize size,
16036  VmaSuballocationType suballocType,
16037  uint32_t memTypeIndex,
16038  bool withinBudget,
16039  bool map,
16040  bool isUserDataString,
16041  void* pUserData,
16042  VkBuffer dedicatedBuffer,
16043  VkBufferUsageFlags dedicatedBufferUsage,
16044  VkImage dedicatedImage,
16045  size_t allocationCount,
16046  VmaAllocation* pAllocations)
16047 {
16048  VMA_ASSERT(allocationCount > 0 && pAllocations);
16049 
16050  if(withinBudget)
16051  {
16052  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16053  VmaBudget heapBudget = {};
16054  GetBudget(&heapBudget, heapIndex, 1);
16055  if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16056  {
16057  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16058  }
16059  }
16060 
16061  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16062  allocInfo.memoryTypeIndex = memTypeIndex;
16063  allocInfo.allocationSize = size;
16064 
16065 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16066  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16067  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16068  {
16069  if(dedicatedBuffer != VK_NULL_HANDLE)
16070  {
16071  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16072  dedicatedAllocInfo.buffer = dedicatedBuffer;
16073  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16074  }
16075  else if(dedicatedImage != VK_NULL_HANDLE)
16076  {
16077  dedicatedAllocInfo.image = dedicatedImage;
16078  VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16079  }
16080  }
16081 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16082 
16083 #if VMA_BUFFER_DEVICE_ADDRESS
16084  VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16085  if(m_UseKhrBufferDeviceAddress)
16086  {
16087  bool canContainBufferWithDeviceAddress = true;
16088  if(dedicatedBuffer != VK_NULL_HANDLE)
16089  {
16090  canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16091  (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16092  }
16093  else if(dedicatedImage != VK_NULL_HANDLE)
16094  {
16095  canContainBufferWithDeviceAddress = false;
16096  }
16097  if(canContainBufferWithDeviceAddress)
16098  {
16099  allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16100  VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16101  }
16102  }
16103 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16104 
16105  size_t allocIndex;
16106  VkResult res = VK_SUCCESS;
16107  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16108  {
16109  res = AllocateDedicatedMemoryPage(
16110  size,
16111  suballocType,
16112  memTypeIndex,
16113  allocInfo,
16114  map,
16115  isUserDataString,
16116  pUserData,
16117  pAllocations + allocIndex);
16118  if(res != VK_SUCCESS)
16119  {
16120  break;
16121  }
16122  }
16123 
16124  if(res == VK_SUCCESS)
16125  {
16126  // Register them in m_pDedicatedAllocations.
16127  {
16128  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16129  AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16130  VMA_ASSERT(pDedicatedAllocations);
16131  for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16132  {
16133  VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16134  }
16135  }
16136 
16137  VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16138  }
16139  else
16140  {
16141  // Free all already created allocations.
16142  while(allocIndex--)
16143  {
16144  VmaAllocation currAlloc = pAllocations[allocIndex];
16145  VkDeviceMemory hMemory = currAlloc->GetMemory();
16146 
16147  /*
16148  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16149  before vkFreeMemory.
16150 
16151  if(currAlloc->GetMappedData() != VMA_NULL)
16152  {
16153  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16154  }
16155  */
16156 
16157  FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16158  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16159  currAlloc->SetUserData(this, VMA_NULL);
16160  m_AllocationObjectAllocator.Free(currAlloc);
16161  }
16162 
16163  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16164  }
16165 
16166  return res;
16167 }
16168 
16169 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16170  VkDeviceSize size,
16171  VmaSuballocationType suballocType,
16172  uint32_t memTypeIndex,
16173  const VkMemoryAllocateInfo& allocInfo,
16174  bool map,
16175  bool isUserDataString,
16176  void* pUserData,
16177  VmaAllocation* pAllocation)
16178 {
16179  VkDeviceMemory hMemory = VK_NULL_HANDLE;
16180  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16181  if(res < 0)
16182  {
16183  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16184  return res;
16185  }
16186 
16187  void* pMappedData = VMA_NULL;
16188  if(map)
16189  {
16190  res = (*m_VulkanFunctions.vkMapMemory)(
16191  m_hDevice,
16192  hMemory,
16193  0,
16194  VK_WHOLE_SIZE,
16195  0,
16196  &pMappedData);
16197  if(res < 0)
16198  {
16199  VMA_DEBUG_LOG(" vkMapMemory FAILED");
16200  FreeVulkanMemory(memTypeIndex, size, hMemory);
16201  return res;
16202  }
16203  }
16204 
16205  *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16206  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16207  (*pAllocation)->SetUserData(this, pUserData);
16208  m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16209  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16210  {
16211  FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16212  }
16213 
16214  return VK_SUCCESS;
16215 }
16216 
16217 void VmaAllocator_T::GetBufferMemoryRequirements(
16218  VkBuffer hBuffer,
16219  VkMemoryRequirements& memReq,
16220  bool& requiresDedicatedAllocation,
16221  bool& prefersDedicatedAllocation) const
16222 {
16223 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16224  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16225  {
16226  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16227  memReqInfo.buffer = hBuffer;
16228 
16229  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16230 
16231  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16232  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16233 
16234  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16235 
16236  memReq = memReq2.memoryRequirements;
16237  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16238  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16239  }
16240  else
16241 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16242  {
16243  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16244  requiresDedicatedAllocation = false;
16245  prefersDedicatedAllocation = false;
16246  }
16247 }
16248 
16249 void VmaAllocator_T::GetImageMemoryRequirements(
16250  VkImage hImage,
16251  VkMemoryRequirements& memReq,
16252  bool& requiresDedicatedAllocation,
16253  bool& prefersDedicatedAllocation) const
16254 {
16255 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16256  if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16257  {
16258  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16259  memReqInfo.image = hImage;
16260 
16261  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16262 
16263  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16264  VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16265 
16266  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16267 
16268  memReq = memReq2.memoryRequirements;
16269  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16270  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16271  }
16272  else
16273 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16274  {
16275  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16276  requiresDedicatedAllocation = false;
16277  prefersDedicatedAllocation = false;
16278  }
16279 }
16280 
16281 VkResult VmaAllocator_T::AllocateMemory(
16282  const VkMemoryRequirements& vkMemReq,
16283  bool requiresDedicatedAllocation,
16284  bool prefersDedicatedAllocation,
16285  VkBuffer dedicatedBuffer,
16286  VkBufferUsageFlags dedicatedBufferUsage,
16287  VkImage dedicatedImage,
16288  const VmaAllocationCreateInfo& createInfo,
16289  VmaSuballocationType suballocType,
16290  size_t allocationCount,
16291  VmaAllocation* pAllocations)
16292 {
16293  memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16294 
16295  VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16296 
16297  if(vkMemReq.size == 0)
16298  {
16299  return VK_ERROR_VALIDATION_FAILED_EXT;
16300  }
16301  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16302  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16303  {
16304  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16305  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16306  }
16307  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16309  {
16310  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16311  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16312  }
16313  if(requiresDedicatedAllocation)
16314  {
16315  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16316  {
16317  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16318  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16319  }
16320  if(createInfo.pool != VK_NULL_HANDLE)
16321  {
16322  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16323  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16324  }
16325  }
16326  if((createInfo.pool != VK_NULL_HANDLE) &&
16327  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16328  {
16329  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16330  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16331  }
16332 
16333  if(createInfo.pool != VK_NULL_HANDLE)
16334  {
16335  const VkDeviceSize alignmentForPool = VMA_MAX(
16336  vkMemReq.alignment,
16337  GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16338 
16339  VmaAllocationCreateInfo createInfoForPool = createInfo;
16340  // If memory type is not HOST_VISIBLE, disable MAPPED.
16341  if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16342  (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16343  {
16344  createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16345  }
16346 
16347  return createInfo.pool->m_BlockVector.Allocate(
16348  m_CurrentFrameIndex.load(),
16349  vkMemReq.size,
16350  alignmentForPool,
16351  createInfoForPool,
16352  suballocType,
16353  allocationCount,
16354  pAllocations);
16355  }
16356  else
16357  {
16358  // Bit mask of memory Vulkan types acceptable for this allocation.
16359  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16360  uint32_t memTypeIndex = UINT32_MAX;
16361  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16362  if(res == VK_SUCCESS)
16363  {
16364  VkDeviceSize alignmentForMemType = VMA_MAX(
16365  vkMemReq.alignment,
16366  GetMemoryTypeMinAlignment(memTypeIndex));
16367 
16368  res = AllocateMemoryOfType(
16369  vkMemReq.size,
16370  alignmentForMemType,
16371  requiresDedicatedAllocation || prefersDedicatedAllocation,
16372  dedicatedBuffer,
16373  dedicatedBufferUsage,
16374  dedicatedImage,
16375  createInfo,
16376  memTypeIndex,
16377  suballocType,
16378  allocationCount,
16379  pAllocations);
16380  // Succeeded on first try.
16381  if(res == VK_SUCCESS)
16382  {
16383  return res;
16384  }
16385  // Allocation from this memory type failed. Try other compatible memory types.
16386  else
16387  {
16388  for(;;)
16389  {
16390  // Remove old memTypeIndex from list of possibilities.
16391  memoryTypeBits &= ~(1u << memTypeIndex);
16392  // Find alternative memTypeIndex.
16393  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16394  if(res == VK_SUCCESS)
16395  {
16396  alignmentForMemType = VMA_MAX(
16397  vkMemReq.alignment,
16398  GetMemoryTypeMinAlignment(memTypeIndex));
16399 
16400  res = AllocateMemoryOfType(
16401  vkMemReq.size,
16402  alignmentForMemType,
16403  requiresDedicatedAllocation || prefersDedicatedAllocation,
16404  dedicatedBuffer,
16405  dedicatedBufferUsage,
16406  dedicatedImage,
16407  createInfo,
16408  memTypeIndex,
16409  suballocType,
16410  allocationCount,
16411  pAllocations);
16412  // Allocation from this alternative memory type succeeded.
16413  if(res == VK_SUCCESS)
16414  {
16415  return res;
16416  }
16417  // else: Allocation from this memory type failed. Try next one - next loop iteration.
16418  }
16419  // No other matching memory type index could be found.
16420  else
16421  {
16422  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16423  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16424  }
16425  }
16426  }
16427  }
16428  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16429  else
16430  return res;
16431  }
16432 }
16433 
16434 void VmaAllocator_T::FreeMemory(
16435  size_t allocationCount,
16436  const VmaAllocation* pAllocations)
16437 {
16438  VMA_ASSERT(pAllocations);
16439 
16440  for(size_t allocIndex = allocationCount; allocIndex--; )
16441  {
16442  VmaAllocation allocation = pAllocations[allocIndex];
16443 
16444  if(allocation != VK_NULL_HANDLE)
16445  {
16446  if(TouchAllocation(allocation))
16447  {
16448  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16449  {
16450  FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16451  }
16452 
16453  switch(allocation->GetType())
16454  {
16455  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16456  {
16457  VmaBlockVector* pBlockVector = VMA_NULL;
16458  VmaPool hPool = allocation->GetBlock()->GetParentPool();
16459  if(hPool != VK_NULL_HANDLE)
16460  {
16461  pBlockVector = &hPool->m_BlockVector;
16462  }
16463  else
16464  {
16465  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16466  pBlockVector = m_pBlockVectors[memTypeIndex];
16467  }
16468  pBlockVector->Free(allocation);
16469  }
16470  break;
16471  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16472  FreeDedicatedMemory(allocation);
16473  break;
16474  default:
16475  VMA_ASSERT(0);
16476  }
16477  }
16478 
16479  // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16480  m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16481  allocation->SetUserData(this, VMA_NULL);
16482  m_AllocationObjectAllocator.Free(allocation);
16483  }
16484  }
16485 }
16486 
16487 VkResult VmaAllocator_T::ResizeAllocation(
16488  const VmaAllocation alloc,
16489  VkDeviceSize newSize)
16490 {
16491  // This function is deprecated and so it does nothing. It's left for backward compatibility.
16492  if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16493  {
16494  return VK_ERROR_VALIDATION_FAILED_EXT;
16495  }
16496  if(newSize == alloc->GetSize())
16497  {
16498  return VK_SUCCESS;
16499  }
16500  return VK_ERROR_OUT_OF_POOL_MEMORY;
16501 }
16502 
16503 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16504 {
16505  // Initialize.
16506  InitStatInfo(pStats->total);
16507  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16508  InitStatInfo(pStats->memoryType[i]);
16509  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16510  InitStatInfo(pStats->memoryHeap[i]);
16511 
16512  // Process default pools.
16513  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16514  {
16515  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16516  VMA_ASSERT(pBlockVector);
16517  pBlockVector->AddStats(pStats);
16518  }
16519 
16520  // Process custom pools.
16521  {
16522  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16523  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16524  {
16525  m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16526  }
16527  }
16528 
16529  // Process dedicated allocations.
16530  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16531  {
16532  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16533  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16534  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16535  VMA_ASSERT(pDedicatedAllocVector);
16536  for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16537  {
16538  VmaStatInfo allocationStatInfo;
16539  (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16540  VmaAddStatInfo(pStats->total, allocationStatInfo);
16541  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16542  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16543  }
16544  }
16545 
16546  // Postprocess.
16547  VmaPostprocessCalcStatInfo(pStats->total);
16548  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16549  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16550  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16551  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16552 }
16553 
16554 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16555 {
16556 #if VMA_MEMORY_BUDGET
16557  if(m_UseExtMemoryBudget)
16558  {
16559  if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16560  {
16561  VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16562  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16563  {
16564  const uint32_t heapIndex = firstHeap + i;
16565 
16566  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16567  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16568 
16569  if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16570  {
16571  outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16572  outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16573  }
16574  else
16575  {
16576  outBudget->usage = 0;
16577  }
16578 
16579  // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16580  outBudget->budget = VMA_MIN(
16581  m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16582  }
16583  }
16584  else
16585  {
16586  UpdateVulkanBudget(); // Outside of mutex lock
16587  GetBudget(outBudget, firstHeap, heapCount); // Recursion
16588  }
16589  }
16590  else
16591 #endif
16592  {
16593  for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16594  {
16595  const uint32_t heapIndex = firstHeap + i;
16596 
16597  outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16598  outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16599 
16600  outBudget->usage = outBudget->blockBytes;
16601  outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16602  }
16603  }
16604 }
16605 
16606 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16607 
16608 VkResult VmaAllocator_T::DefragmentationBegin(
16609  const VmaDefragmentationInfo2& info,
16610  VmaDefragmentationStats* pStats,
16611  VmaDefragmentationContext* pContext)
16612 {
16613  if(info.pAllocationsChanged != VMA_NULL)
16614  {
16615  memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16616  }
16617 
16618  *pContext = vma_new(this, VmaDefragmentationContext_T)(
16619  this, m_CurrentFrameIndex.load(), info.flags, pStats);
16620 
16621  (*pContext)->AddPools(info.poolCount, info.pPools);
16622  (*pContext)->AddAllocations(
16624 
16625  VkResult res = (*pContext)->Defragment(
16628  info.commandBuffer, pStats, info.flags);
16629 
16630  if(res != VK_NOT_READY)
16631  {
16632  vma_delete(this, *pContext);
16633  *pContext = VMA_NULL;
16634  }
16635 
16636  return res;
16637 }
16638 
16639 VkResult VmaAllocator_T::DefragmentationEnd(
16640  VmaDefragmentationContext context)
16641 {
16642  vma_delete(this, context);
16643  return VK_SUCCESS;
16644 }
16645 
16646 VkResult VmaAllocator_T::DefragmentationPassBegin(
16648  VmaDefragmentationContext context)
16649 {
16650  return context->DefragmentPassBegin(pInfo);
16651 }
16652 VkResult VmaAllocator_T::DefragmentationPassEnd(
16653  VmaDefragmentationContext context)
16654 {
16655  return context->DefragmentPassEnd();
16656 
16657 }
16658 
16659 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16660 {
16661  if(hAllocation->CanBecomeLost())
16662  {
16663  /*
16664  Warning: This is a carefully designed algorithm.
16665  Do not modify unless you really know what you're doing :)
16666  */
16667  const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16668  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16669  for(;;)
16670  {
16671  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16672  {
16673  pAllocationInfo->memoryType = UINT32_MAX;
16674  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
16675  pAllocationInfo->offset = 0;
16676  pAllocationInfo->size = hAllocation->GetSize();
16677  pAllocationInfo->pMappedData = VMA_NULL;
16678  pAllocationInfo->pUserData = hAllocation->GetUserData();
16679  return;
16680  }
16681  else if(localLastUseFrameIndex == localCurrFrameIndex)
16682  {
16683  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16684  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16685  pAllocationInfo->offset = hAllocation->GetOffset();
16686  pAllocationInfo->size = hAllocation->GetSize();
16687  pAllocationInfo->pMappedData = VMA_NULL;
16688  pAllocationInfo->pUserData = hAllocation->GetUserData();
16689  return;
16690  }
16691  else // Last use time earlier than current time.
16692  {
16693  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16694  {
16695  localLastUseFrameIndex = localCurrFrameIndex;
16696  }
16697  }
16698  }
16699  }
16700  else
16701  {
16702 #if VMA_STATS_STRING_ENABLED
16703  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16704  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16705  for(;;)
16706  {
16707  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16708  if(localLastUseFrameIndex == localCurrFrameIndex)
16709  {
16710  break;
16711  }
16712  else // Last use time earlier than current time.
16713  {
16714  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16715  {
16716  localLastUseFrameIndex = localCurrFrameIndex;
16717  }
16718  }
16719  }
16720 #endif
16721 
16722  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16723  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16724  pAllocationInfo->offset = hAllocation->GetOffset();
16725  pAllocationInfo->size = hAllocation->GetSize();
16726  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
16727  pAllocationInfo->pUserData = hAllocation->GetUserData();
16728  }
16729 }
16730 
16731 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
16732 {
16733  // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
16734  if(hAllocation->CanBecomeLost())
16735  {
16736  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16737  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16738  for(;;)
16739  {
16740  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16741  {
16742  return false;
16743  }
16744  else if(localLastUseFrameIndex == localCurrFrameIndex)
16745  {
16746  return true;
16747  }
16748  else // Last use time earlier than current time.
16749  {
16750  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16751  {
16752  localLastUseFrameIndex = localCurrFrameIndex;
16753  }
16754  }
16755  }
16756  }
16757  else
16758  {
16759 #if VMA_STATS_STRING_ENABLED
16760  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16761  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16762  for(;;)
16763  {
16764  VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16765  if(localLastUseFrameIndex == localCurrFrameIndex)
16766  {
16767  break;
16768  }
16769  else // Last use time earlier than current time.
16770  {
16771  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16772  {
16773  localLastUseFrameIndex = localCurrFrameIndex;
16774  }
16775  }
16776  }
16777 #endif
16778 
16779  return true;
16780  }
16781 }
16782 
16783 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
16784 {
16785  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
16786 
16787  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
16788 
16789  if(newCreateInfo.maxBlockCount == 0)
16790  {
16791  newCreateInfo.maxBlockCount = SIZE_MAX;
16792  }
16793  if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
16794  {
16795  return VK_ERROR_INITIALIZATION_FAILED;
16796  }
16797  // Memory type index out of range or forbidden.
16798  if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
16799  ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
16800  {
16801  return VK_ERROR_FEATURE_NOT_PRESENT;
16802  }
16803 
16804  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
16805 
16806  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
16807 
16808  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
16809  if(res != VK_SUCCESS)
16810  {
16811  vma_delete(this, *pPool);
16812  *pPool = VMA_NULL;
16813  return res;
16814  }
16815 
16816  // Add to m_Pools.
16817  {
16818  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16819  (*pPool)->SetId(m_NextPoolId++);
16820  VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
16821  }
16822 
16823  return VK_SUCCESS;
16824 }
16825 
16826 void VmaAllocator_T::DestroyPool(VmaPool pool)
16827 {
16828  // Remove from m_Pools.
16829  {
16830  VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16831  bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
16832  VMA_ASSERT(success && "Pool not found in Allocator.");
16833  }
16834 
16835  vma_delete(this, pool);
16836 }
16837 
16838 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
16839 {
16840  pool->m_BlockVector.GetPoolStats(pPoolStats);
16841 }
16842 
16843 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
16844 {
16845  m_CurrentFrameIndex.store(frameIndex);
16846 
16847 #if VMA_MEMORY_BUDGET
16848  if(m_UseExtMemoryBudget)
16849  {
16850  UpdateVulkanBudget();
16851  }
16852 #endif // #if VMA_MEMORY_BUDGET
16853 }
16854 
16855 void VmaAllocator_T::MakePoolAllocationsLost(
16856  VmaPool hPool,
16857  size_t* pLostAllocationCount)
16858 {
16859  hPool->m_BlockVector.MakePoolAllocationsLost(
16860  m_CurrentFrameIndex.load(),
16861  pLostAllocationCount);
16862 }
16863 
16864 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16865 {
16866  return hPool->m_BlockVector.CheckCorruption();
16867 }
16868 
16869 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16870 {
16871  VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16872 
16873  // Process default pools.
16874  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16875  {
16876  if(((1u << memTypeIndex) & memoryTypeBits) != 0)
16877  {
16878  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16879  VMA_ASSERT(pBlockVector);
16880  VkResult localRes = pBlockVector->CheckCorruption();
16881  switch(localRes)
16882  {
16883  case VK_ERROR_FEATURE_NOT_PRESENT:
16884  break;
16885  case VK_SUCCESS:
16886  finalRes = VK_SUCCESS;
16887  break;
16888  default:
16889  return localRes;
16890  }
16891  }
16892  }
16893 
16894  // Process custom pools.
16895  {
16896  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16897  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16898  {
16899  if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16900  {
16901  VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
16902  switch(localRes)
16903  {
16904  case VK_ERROR_FEATURE_NOT_PRESENT:
16905  break;
16906  case VK_SUCCESS:
16907  finalRes = VK_SUCCESS;
16908  break;
16909  default:
16910  return localRes;
16911  }
16912  }
16913  }
16914  }
16915 
16916  return finalRes;
16917 }
16918 
16919 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16920 {
16921  *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16922  (*pAllocation)->InitLost();
16923 }
16924 
16925 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16926 {
16927  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16928 
16929  // HeapSizeLimit is in effect for this heap.
16930  if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16931  {
16932  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16933  VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16934  for(;;)
16935  {
16936  const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16937  if(blockBytesAfterAllocation > heapSize)
16938  {
16939  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16940  }
16941  if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16942  {
16943  break;
16944  }
16945  }
16946  }
16947  else
16948  {
16949  m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16950  }
16951 
16952  // VULKAN CALL vkAllocateMemory.
16953  VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16954 
16955  if(res == VK_SUCCESS)
16956  {
16957 #if VMA_MEMORY_BUDGET
16958  ++m_Budget.m_OperationsSinceBudgetFetch;
16959 #endif
16960 
16961  // Informative callback.
16962  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16963  {
16964  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16965  }
16966  }
16967  else
16968  {
16969  m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16970  }
16971 
16972  return res;
16973 }
16974 
16975 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16976 {
16977  // Informative callback.
16978  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16979  {
16980  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16981  }
16982 
16983  // VULKAN CALL vkFreeMemory.
16984  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16985 
16986  m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16987 }
16988 
16989 VkResult VmaAllocator_T::BindVulkanBuffer(
16990  VkDeviceMemory memory,
16991  VkDeviceSize memoryOffset,
16992  VkBuffer buffer,
16993  const void* pNext)
16994 {
16995  if(pNext != VMA_NULL)
16996  {
16997 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16998  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16999  m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17000  {
17001  VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17002  bindBufferMemoryInfo.pNext = pNext;
17003  bindBufferMemoryInfo.buffer = buffer;
17004  bindBufferMemoryInfo.memory = memory;
17005  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17006  return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17007  }
17008  else
17009 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17010  {
17011  return VK_ERROR_EXTENSION_NOT_PRESENT;
17012  }
17013  }
17014  else
17015  {
17016  return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17017  }
17018 }
17019 
17020 VkResult VmaAllocator_T::BindVulkanImage(
17021  VkDeviceMemory memory,
17022  VkDeviceSize memoryOffset,
17023  VkImage image,
17024  const void* pNext)
17025 {
17026  if(pNext != VMA_NULL)
17027  {
17028 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17029  if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17030  m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17031  {
17032  VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17033  bindBufferMemoryInfo.pNext = pNext;
17034  bindBufferMemoryInfo.image = image;
17035  bindBufferMemoryInfo.memory = memory;
17036  bindBufferMemoryInfo.memoryOffset = memoryOffset;
17037  return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17038  }
17039  else
17040 #endif // #if VMA_BIND_MEMORY2
17041  {
17042  return VK_ERROR_EXTENSION_NOT_PRESENT;
17043  }
17044  }
17045  else
17046  {
17047  return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17048  }
17049 }
17050 
17051 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17052 {
17053  if(hAllocation->CanBecomeLost())
17054  {
17055  return VK_ERROR_MEMORY_MAP_FAILED;
17056  }
17057 
17058  switch(hAllocation->GetType())
17059  {
17060  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17061  {
17062  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17063  char *pBytes = VMA_NULL;
17064  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17065  if(res == VK_SUCCESS)
17066  {
17067  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17068  hAllocation->BlockAllocMap();
17069  }
17070  return res;
17071  }
17072  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17073  return hAllocation->DedicatedAllocMap(this, ppData);
17074  default:
17075  VMA_ASSERT(0);
17076  return VK_ERROR_MEMORY_MAP_FAILED;
17077  }
17078 }
17079 
17080 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17081 {
17082  switch(hAllocation->GetType())
17083  {
17084  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17085  {
17086  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17087  hAllocation->BlockAllocUnmap();
17088  pBlock->Unmap(this, 1);
17089  }
17090  break;
17091  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17092  hAllocation->DedicatedAllocUnmap(this);
17093  break;
17094  default:
17095  VMA_ASSERT(0);
17096  }
17097 }
17098 
17099 VkResult VmaAllocator_T::BindBufferMemory(
17100  VmaAllocation hAllocation,
17101  VkDeviceSize allocationLocalOffset,
17102  VkBuffer hBuffer,
17103  const void* pNext)
17104 {
17105  VkResult res = VK_SUCCESS;
17106  switch(hAllocation->GetType())
17107  {
17108  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17109  res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17110  break;
17111  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17112  {
17113  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17114  VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17115  res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17116  break;
17117  }
17118  default:
17119  VMA_ASSERT(0);
17120  }
17121  return res;
17122 }
17123 
17124 VkResult VmaAllocator_T::BindImageMemory(
17125  VmaAllocation hAllocation,
17126  VkDeviceSize allocationLocalOffset,
17127  VkImage hImage,
17128  const void* pNext)
17129 {
17130  VkResult res = VK_SUCCESS;
17131  switch(hAllocation->GetType())
17132  {
17133  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17134  res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17135  break;
17136  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17137  {
17138  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17139  VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17140  res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17141  break;
17142  }
17143  default:
17144  VMA_ASSERT(0);
17145  }
17146  return res;
17147 }
17148 
17149 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17150  VmaAllocation hAllocation,
17151  VkDeviceSize offset, VkDeviceSize size,
17152  VMA_CACHE_OPERATION op)
17153 {
17154  VkResult res = VK_SUCCESS;
17155 
17156  VkMappedMemoryRange memRange = {};
17157  if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17158  {
17159  switch(op)
17160  {
17161  case VMA_CACHE_FLUSH:
17162  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17163  break;
17164  case VMA_CACHE_INVALIDATE:
17165  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17166  break;
17167  default:
17168  VMA_ASSERT(0);
17169  }
17170  }
17171  // else: Just ignore this call.
17172  return res;
17173 }
17174 
17175 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17176  uint32_t allocationCount,
17177  const VmaAllocation* allocations,
17178  const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17179  VMA_CACHE_OPERATION op)
17180 {
17181  typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17182  typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17183  RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17184 
17185  for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17186  {
17187  const VmaAllocation alloc = allocations[allocIndex];
17188  const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17189  const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17190  VkMappedMemoryRange newRange;
17191  if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17192  {
17193  ranges.push_back(newRange);
17194  }
17195  }
17196 
17197  VkResult res = VK_SUCCESS;
17198  if(!ranges.empty())
17199  {
17200  switch(op)
17201  {
17202  case VMA_CACHE_FLUSH:
17203  res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17204  break;
17205  case VMA_CACHE_INVALIDATE:
17206  res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17207  break;
17208  default:
17209  VMA_ASSERT(0);
17210  }
17211  }
17212  // else: Just ignore this call.
17213  return res;
17214 }
17215 
17216 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17217 {
17218  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17219 
17220  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17221  {
17222  VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17223  AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17224  VMA_ASSERT(pDedicatedAllocations);
17225  bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17226  VMA_ASSERT(success);
17227  }
17228 
17229  VkDeviceMemory hMemory = allocation->GetMemory();
17230 
17231  /*
17232  There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17233  before vkFreeMemory.
17234 
17235  if(allocation->GetMappedData() != VMA_NULL)
17236  {
17237  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17238  }
17239  */
17240 
17241  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17242 
17243  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17244 }
17245 
17246 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17247 {
17248  VkBufferCreateInfo dummyBufCreateInfo;
17249  VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17250 
17251  uint32_t memoryTypeBits = 0;
17252 
17253  // Create buffer.
17254  VkBuffer buf = VK_NULL_HANDLE;
17255  VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17256  m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17257  if(res == VK_SUCCESS)
17258  {
17259  // Query for supported memory types.
17260  VkMemoryRequirements memReq;
17261  (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17262  memoryTypeBits = memReq.memoryTypeBits;
17263 
17264  // Destroy buffer.
17265  (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17266  }
17267 
17268  return memoryTypeBits;
17269 }
17270 
17271 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17272 {
17273  // Make sure memory information is already fetched.
17274  VMA_ASSERT(GetMemoryTypeCount() > 0);
17275 
17276  uint32_t memoryTypeBits = UINT32_MAX;
17277 
17278  if(!m_UseAmdDeviceCoherentMemory)
17279  {
17280  // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17281  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17282  {
17283  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17284  {
17285  memoryTypeBits &= ~(1u << memTypeIndex);
17286  }
17287  }
17288  }
17289 
17290  return memoryTypeBits;
17291 }
17292 
17293 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17294  VmaAllocation allocation,
17295  VkDeviceSize offset, VkDeviceSize size,
17296  VkMappedMemoryRange& outRange) const
17297 {
17298  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17299  if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17300  {
17301  const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17302  const VkDeviceSize allocationSize = allocation->GetSize();
17303  VMA_ASSERT(offset <= allocationSize);
17304 
17305  outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17306  outRange.pNext = VMA_NULL;
17307  outRange.memory = allocation->GetMemory();
17308 
17309  switch(allocation->GetType())
17310  {
17311  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17312  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17313  if(size == VK_WHOLE_SIZE)
17314  {
17315  outRange.size = allocationSize - outRange.offset;
17316  }
17317  else
17318  {
17319  VMA_ASSERT(offset + size <= allocationSize);
17320  outRange.size = VMA_MIN(
17321  VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17322  allocationSize - outRange.offset);
17323  }
17324  break;
17325  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17326  {
17327  // 1. Still within this allocation.
17328  outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17329  if(size == VK_WHOLE_SIZE)
17330  {
17331  size = allocationSize - offset;
17332  }
17333  else
17334  {
17335  VMA_ASSERT(offset + size <= allocationSize);
17336  }
17337  outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17338 
17339  // 2. Adjust to whole block.
17340  const VkDeviceSize allocationOffset = allocation->GetOffset();
17341  VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17342  const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17343  outRange.offset += allocationOffset;
17344  outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17345 
17346  break;
17347  }
17348  default:
17349  VMA_ASSERT(0);
17350  }
17351  return true;
17352  }
17353  return false;
17354 }
17355 
17356 #if VMA_MEMORY_BUDGET
17357 
17358 void VmaAllocator_T::UpdateVulkanBudget()
17359 {
17360  VMA_ASSERT(m_UseExtMemoryBudget);
17361 
17362  VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17363 
17364  VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17365  VmaPnextChainPushFront(&memProps, &budgetProps);
17366 
17367  GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17368 
17369  {
17370  VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17371 
17372  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17373  {
17374  m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17375  m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17376  m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17377 
17378  // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17379  if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17380  {
17381  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17382  }
17383  else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17384  {
17385  m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17386  }
17387  if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17388  {
17389  m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17390  }
17391  }
17392  m_Budget.m_OperationsSinceBudgetFetch = 0;
17393  }
17394 }
17395 
17396 #endif // #if VMA_MEMORY_BUDGET
17397 
17398 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17399 {
17400  if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17401  !hAllocation->CanBecomeLost() &&
17402  (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17403  {
17404  void* pData = VMA_NULL;
17405  VkResult res = Map(hAllocation, &pData);
17406  if(res == VK_SUCCESS)
17407  {
17408  memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17409  FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17410  Unmap(hAllocation);
17411  }
17412  else
17413  {
17414  VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17415  }
17416  }
17417 }
17418 
17419 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17420 {
17421  uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17422  if(memoryTypeBits == UINT32_MAX)
17423  {
17424  memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17425  m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17426  }
17427  return memoryTypeBits;
17428 }
17429 
17430 #if VMA_STATS_STRING_ENABLED
17431 
17432 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17433 {
17434  bool dedicatedAllocationsStarted = false;
17435  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17436  {
17437  VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17438  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17439  VMA_ASSERT(pDedicatedAllocVector);
17440  if(pDedicatedAllocVector->empty() == false)
17441  {
17442  if(dedicatedAllocationsStarted == false)
17443  {
17444  dedicatedAllocationsStarted = true;
17445  json.WriteString("DedicatedAllocations");
17446  json.BeginObject();
17447  }
17448 
17449  json.BeginString("Type ");
17450  json.ContinueString(memTypeIndex);
17451  json.EndString();
17452 
17453  json.BeginArray();
17454 
17455  for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17456  {
17457  json.BeginObject(true);
17458  const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17459  hAlloc->PrintParameters(json);
17460  json.EndObject();
17461  }
17462 
17463  json.EndArray();
17464  }
17465  }
17466  if(dedicatedAllocationsStarted)
17467  {
17468  json.EndObject();
17469  }
17470 
17471  {
17472  bool allocationsStarted = false;
17473  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17474  {
17475  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17476  {
17477  if(allocationsStarted == false)
17478  {
17479  allocationsStarted = true;
17480  json.WriteString("DefaultPools");
17481  json.BeginObject();
17482  }
17483 
17484  json.BeginString("Type ");
17485  json.ContinueString(memTypeIndex);
17486  json.EndString();
17487 
17488  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17489  }
17490  }
17491  if(allocationsStarted)
17492  {
17493  json.EndObject();
17494  }
17495  }
17496 
17497  // Custom pools
17498  {
17499  VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17500  const size_t poolCount = m_Pools.size();
17501  if(poolCount > 0)
17502  {
17503  json.WriteString("Pools");
17504  json.BeginObject();
17505  for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17506  {
17507  json.BeginString();
17508  json.ContinueString(m_Pools[poolIndex]->GetId());
17509  json.EndString();
17510 
17511  m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17512  }
17513  json.EndObject();
17514  }
17515  }
17516 }
17517 
17518 #endif // #if VMA_STATS_STRING_ENABLED
17519 
17521 // Public interface
17522 
17523 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17524  const VmaAllocatorCreateInfo* pCreateInfo,
17525  VmaAllocator* pAllocator)
17526 {
17527  VMA_ASSERT(pCreateInfo && pAllocator);
17528  VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17529  (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17530  VMA_DEBUG_LOG("vmaCreateAllocator");
17531  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17532  return (*pAllocator)->Init(pCreateInfo);
17533 }
17534 
17535 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17536  VmaAllocator allocator)
17537 {
17538  if(allocator != VK_NULL_HANDLE)
17539  {
17540  VMA_DEBUG_LOG("vmaDestroyAllocator");
17541  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17542  vma_delete(&allocationCallbacks, allocator);
17543  }
17544 }
17545 
17546 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17547 {
17548  VMA_ASSERT(allocator && pAllocatorInfo);
17549  pAllocatorInfo->instance = allocator->m_hInstance;
17550  pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17551  pAllocatorInfo->device = allocator->m_hDevice;
17552 }
17553 
17554 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17555  VmaAllocator allocator,
17556  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17557 {
17558  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17559  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17560 }
17561 
17562 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17563  VmaAllocator allocator,
17564  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17565 {
17566  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17567  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17568 }
17569 
17570 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17571  VmaAllocator allocator,
17572  uint32_t memoryTypeIndex,
17573  VkMemoryPropertyFlags* pFlags)
17574 {
17575  VMA_ASSERT(allocator && pFlags);
17576  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17577  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17578 }
17579 
17580 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17581  VmaAllocator allocator,
17582  uint32_t frameIndex)
17583 {
17584  VMA_ASSERT(allocator);
17585  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17586 
17587  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17588 
17589  allocator->SetCurrentFrameIndex(frameIndex);
17590 }
17591 
17592 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17593  VmaAllocator allocator,
17594  VmaStats* pStats)
17595 {
17596  VMA_ASSERT(allocator && pStats);
17597  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17598  allocator->CalculateStats(pStats);
17599 }
17600 
17601 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17602  VmaAllocator allocator,
17603  VmaBudget* pBudget)
17604 {
17605  VMA_ASSERT(allocator && pBudget);
17606  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17607  allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17608 }
17609 
17610 #if VMA_STATS_STRING_ENABLED
17611 
17612 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17613  VmaAllocator allocator,
17614  char** ppStatsString,
17615  VkBool32 detailedMap)
17616 {
17617  VMA_ASSERT(allocator && ppStatsString);
17618  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17619 
17620  VmaStringBuilder sb(allocator);
17621  {
17622  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17623  json.BeginObject();
17624 
17625  VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17626  allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17627 
17628  VmaStats stats;
17629  allocator->CalculateStats(&stats);
17630 
17631  json.WriteString("Total");
17632  VmaPrintStatInfo(json, stats.total);
17633 
17634  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17635  {
17636  json.BeginString("Heap ");
17637  json.ContinueString(heapIndex);
17638  json.EndString();
17639  json.BeginObject();
17640 
17641  json.WriteString("Size");
17642  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17643 
17644  json.WriteString("Flags");
17645  json.BeginArray(true);
17646  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17647  {
17648  json.WriteString("DEVICE_LOCAL");
17649  }
17650  json.EndArray();
17651 
17652  json.WriteString("Budget");
17653  json.BeginObject();
17654  {
17655  json.WriteString("BlockBytes");
17656  json.WriteNumber(budget[heapIndex].blockBytes);
17657  json.WriteString("AllocationBytes");
17658  json.WriteNumber(budget[heapIndex].allocationBytes);
17659  json.WriteString("Usage");
17660  json.WriteNumber(budget[heapIndex].usage);
17661  json.WriteString("Budget");
17662  json.WriteNumber(budget[heapIndex].budget);
17663  }
17664  json.EndObject();
17665 
17666  if(stats.memoryHeap[heapIndex].blockCount > 0)
17667  {
17668  json.WriteString("Stats");
17669  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
17670  }
17671 
17672  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
17673  {
17674  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
17675  {
17676  json.BeginString("Type ");
17677  json.ContinueString(typeIndex);
17678  json.EndString();
17679 
17680  json.BeginObject();
17681 
17682  json.WriteString("Flags");
17683  json.BeginArray(true);
17684  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
17685  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
17686  {
17687  json.WriteString("DEVICE_LOCAL");
17688  }
17689  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17690  {
17691  json.WriteString("HOST_VISIBLE");
17692  }
17693  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
17694  {
17695  json.WriteString("HOST_COHERENT");
17696  }
17697  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
17698  {
17699  json.WriteString("HOST_CACHED");
17700  }
17701  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
17702  {
17703  json.WriteString("LAZILY_ALLOCATED");
17704  }
17705  if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
17706  {
17707  json.WriteString(" PROTECTED");
17708  }
17709  if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17710  {
17711  json.WriteString(" DEVICE_COHERENT");
17712  }
17713  if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
17714  {
17715  json.WriteString(" DEVICE_UNCACHED");
17716  }
17717  json.EndArray();
17718 
17719  if(stats.memoryType[typeIndex].blockCount > 0)
17720  {
17721  json.WriteString("Stats");
17722  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17723  }
17724 
17725  json.EndObject();
17726  }
17727  }
17728 
17729  json.EndObject();
17730  }
17731  if(detailedMap == VK_TRUE)
17732  {
17733  allocator->PrintDetailedMap(json);
17734  }
17735 
17736  json.EndObject();
17737  }
17738 
17739  const size_t len = sb.GetLength();
17740  char* const pChars = vma_new_array(allocator, char, len + 1);
17741  if(len > 0)
17742  {
17743  memcpy(pChars, sb.GetData(), len);
17744  }
17745  pChars[len] = '\0';
17746  *ppStatsString = pChars;
17747 }
17748 
17749 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17750  VmaAllocator allocator,
17751  char* pStatsString)
17752 {
17753  if(pStatsString != VMA_NULL)
17754  {
17755  VMA_ASSERT(allocator);
17756  size_t len = strlen(pStatsString);
17757  vma_delete_array(allocator, pStatsString, len + 1);
17758  }
17759 }
17760 
17761 #endif // #if VMA_STATS_STRING_ENABLED
17762 
17763 /*
17764 This function is not protected by any mutex because it just reads immutable data.
17765 */
17766 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17767  VmaAllocator allocator,
17768  uint32_t memoryTypeBits,
17769  const VmaAllocationCreateInfo* pAllocationCreateInfo,
17770  uint32_t* pMemoryTypeIndex)
17771 {
17772  VMA_ASSERT(allocator != VK_NULL_HANDLE);
17773  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17774  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17775 
17776  memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17777 
17778  if(pAllocationCreateInfo->memoryTypeBits != 0)
17779  {
17780  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17781  }
17782 
17783  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17784  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17785  uint32_t notPreferredFlags = 0;
17786 
17787  // Convert usage to requiredFlags and preferredFlags.
17788  switch(pAllocationCreateInfo->usage)
17789  {
17791  break;
17793  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17794  {
17795  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17796  }
17797  break;
17799  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17800  break;
17802  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17803  if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17804  {
17805  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17806  }
17807  break;
17809  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17810  preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17811  break;
17813  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17814  break;
17816  requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17817  break;
17818  default:
17819  VMA_ASSERT(0);
17820  break;
17821  }
17822 
17823  // Avoid DEVICE_COHERENT unless explicitly requested.
17824  if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17825  (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17826  {
17827  notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17828  }
17829 
17830  *pMemoryTypeIndex = UINT32_MAX;
17831  uint32_t minCost = UINT32_MAX;
17832  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17833  memTypeIndex < allocator->GetMemoryTypeCount();
17834  ++memTypeIndex, memTypeBit <<= 1)
17835  {
17836  // This memory type is acceptable according to memoryTypeBits bitmask.
17837  if((memTypeBit & memoryTypeBits) != 0)
17838  {
17839  const VkMemoryPropertyFlags currFlags =
17840  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17841  // This memory type contains requiredFlags.
17842  if((requiredFlags & ~currFlags) == 0)
17843  {
17844  // Calculate cost as number of bits from preferredFlags not present in this memory type.
17845  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17846  VmaCountBitsSet(currFlags & notPreferredFlags);
17847  // Remember memory type with lowest cost.
17848  if(currCost < minCost)
17849  {
17850  *pMemoryTypeIndex = memTypeIndex;
17851  if(currCost == 0)
17852  {
17853  return VK_SUCCESS;
17854  }
17855  minCost = currCost;
17856  }
17857  }
17858  }
17859  }
17860  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17861 }
17862 
17863 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17864  VmaAllocator allocator,
17865  const VkBufferCreateInfo* pBufferCreateInfo,
17866  const VmaAllocationCreateInfo* pAllocationCreateInfo,
17867  uint32_t* pMemoryTypeIndex)
17868 {
17869  VMA_ASSERT(allocator != VK_NULL_HANDLE);
17870  VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17871  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17872  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17873 
17874  const VkDevice hDev = allocator->m_hDevice;
17875  VkBuffer hBuffer = VK_NULL_HANDLE;
17876  VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
17877  hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17878  if(res == VK_SUCCESS)
17879  {
17880  VkMemoryRequirements memReq = {};
17881  allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
17882  hDev, hBuffer, &memReq);
17883 
17884  res = vmaFindMemoryTypeIndex(
17885  allocator,
17886  memReq.memoryTypeBits,
17887  pAllocationCreateInfo,
17888  pMemoryTypeIndex);
17889 
17890  allocator->GetVulkanFunctions().vkDestroyBuffer(
17891  hDev, hBuffer, allocator->GetAllocationCallbacks());
17892  }
17893  return res;
17894 }
17895 
17896 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17897  VmaAllocator allocator,
17898  const VkImageCreateInfo* pImageCreateInfo,
17899  const VmaAllocationCreateInfo* pAllocationCreateInfo,
17900  uint32_t* pMemoryTypeIndex)
17901 {
17902  VMA_ASSERT(allocator != VK_NULL_HANDLE);
17903  VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17904  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17905  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17906 
17907  const VkDevice hDev = allocator->m_hDevice;
17908  VkImage hImage = VK_NULL_HANDLE;
17909  VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
17910  hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17911  if(res == VK_SUCCESS)
17912  {
17913  VkMemoryRequirements memReq = {};
17914  allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
17915  hDev, hImage, &memReq);
17916 
17917  res = vmaFindMemoryTypeIndex(
17918  allocator,
17919  memReq.memoryTypeBits,
17920  pAllocationCreateInfo,
17921  pMemoryTypeIndex);
17922 
17923  allocator->GetVulkanFunctions().vkDestroyImage(
17924  hDev, hImage, allocator->GetAllocationCallbacks());
17925  }
17926  return res;
17927 }
17928 
17929 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17930  VmaAllocator allocator,
17931  const VmaPoolCreateInfo* pCreateInfo,
17932  VmaPool* pPool)
17933 {
17934  VMA_ASSERT(allocator && pCreateInfo && pPool);
17935 
17936  VMA_DEBUG_LOG("vmaCreatePool");
17937 
17938  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17939 
17940  VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17941 
17942 #if VMA_RECORDING_ENABLED
17943  if(allocator->GetRecorder() != VMA_NULL)
17944  {
17945  allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17946  }
17947 #endif
17948 
17949  return res;
17950 }
17951 
17952 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17953  VmaAllocator allocator,
17954  VmaPool pool)
17955 {
17956  VMA_ASSERT(allocator);
17957 
17958  if(pool == VK_NULL_HANDLE)
17959  {
17960  return;
17961  }
17962 
17963  VMA_DEBUG_LOG("vmaDestroyPool");
17964 
17965  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17966 
17967 #if VMA_RECORDING_ENABLED
17968  if(allocator->GetRecorder() != VMA_NULL)
17969  {
17970  allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17971  }
17972 #endif
17973 
17974  allocator->DestroyPool(pool);
17975 }
17976 
17977 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17978  VmaAllocator allocator,
17979  VmaPool pool,
17980  VmaPoolStats* pPoolStats)
17981 {
17982  VMA_ASSERT(allocator && pool && pPoolStats);
17983 
17984  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17985 
17986  allocator->GetPoolStats(pool, pPoolStats);
17987 }
17988 
17989 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17990  VmaAllocator allocator,
17991  VmaPool pool,
17992  size_t* pLostAllocationCount)
17993 {
17994  VMA_ASSERT(allocator && pool);
17995 
17996  VMA_DEBUG_GLOBAL_MUTEX_LOCK
17997 
17998 #if VMA_RECORDING_ENABLED
17999  if(allocator->GetRecorder() != VMA_NULL)
18000  {
18001  allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18002  }
18003 #endif
18004 
18005  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18006 }
18007 
18008 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18009 {
18010  VMA_ASSERT(allocator && pool);
18011 
18012  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18013 
18014  VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18015 
18016  return allocator->CheckPoolCorruption(pool);
18017 }
18018 
18019 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18020  VmaAllocator allocator,
18021  VmaPool pool,
18022  const char** ppName)
18023 {
18024  VMA_ASSERT(allocator && pool && ppName);
18025 
18026  VMA_DEBUG_LOG("vmaGetPoolName");
18027 
18028  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18029 
18030  *ppName = pool->GetName();
18031 }
18032 
18033 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18034  VmaAllocator allocator,
18035  VmaPool pool,
18036  const char* pName)
18037 {
18038  VMA_ASSERT(allocator && pool);
18039 
18040  VMA_DEBUG_LOG("vmaSetPoolName");
18041 
18042  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18043 
18044  pool->SetName(pName);
18045 
18046 #if VMA_RECORDING_ENABLED
18047  if(allocator->GetRecorder() != VMA_NULL)
18048  {
18049  allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18050  }
18051 #endif
18052 }
18053 
18054 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18055  VmaAllocator allocator,
18056  const VkMemoryRequirements* pVkMemoryRequirements,
18057  const VmaAllocationCreateInfo* pCreateInfo,
18058  VmaAllocation* pAllocation,
18059  VmaAllocationInfo* pAllocationInfo)
18060 {
18061  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18062 
18063  VMA_DEBUG_LOG("vmaAllocateMemory");
18064 
18065  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18066 
18067  VkResult result = allocator->AllocateMemory(
18068  *pVkMemoryRequirements,
18069  false, // requiresDedicatedAllocation
18070  false, // prefersDedicatedAllocation
18071  VK_NULL_HANDLE, // dedicatedBuffer
18072  UINT32_MAX, // dedicatedBufferUsage
18073  VK_NULL_HANDLE, // dedicatedImage
18074  *pCreateInfo,
18075  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18076  1, // allocationCount
18077  pAllocation);
18078 
18079 #if VMA_RECORDING_ENABLED
18080  if(allocator->GetRecorder() != VMA_NULL)
18081  {
18082  allocator->GetRecorder()->RecordAllocateMemory(
18083  allocator->GetCurrentFrameIndex(),
18084  *pVkMemoryRequirements,
18085  *pCreateInfo,
18086  *pAllocation);
18087  }
18088 #endif
18089 
18090  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18091  {
18092  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18093  }
18094 
18095  return result;
18096 }
18097 
18098 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18099  VmaAllocator allocator,
18100  const VkMemoryRequirements* pVkMemoryRequirements,
18101  const VmaAllocationCreateInfo* pCreateInfo,
18102  size_t allocationCount,
18103  VmaAllocation* pAllocations,
18104  VmaAllocationInfo* pAllocationInfo)
18105 {
18106  if(allocationCount == 0)
18107  {
18108  return VK_SUCCESS;
18109  }
18110 
18111  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18112 
18113  VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18114 
18115  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18116 
18117  VkResult result = allocator->AllocateMemory(
18118  *pVkMemoryRequirements,
18119  false, // requiresDedicatedAllocation
18120  false, // prefersDedicatedAllocation
18121  VK_NULL_HANDLE, // dedicatedBuffer
18122  UINT32_MAX, // dedicatedBufferUsage
18123  VK_NULL_HANDLE, // dedicatedImage
18124  *pCreateInfo,
18125  VMA_SUBALLOCATION_TYPE_UNKNOWN,
18126  allocationCount,
18127  pAllocations);
18128 
18129 #if VMA_RECORDING_ENABLED
18130  if(allocator->GetRecorder() != VMA_NULL)
18131  {
18132  allocator->GetRecorder()->RecordAllocateMemoryPages(
18133  allocator->GetCurrentFrameIndex(),
18134  *pVkMemoryRequirements,
18135  *pCreateInfo,
18136  (uint64_t)allocationCount,
18137  pAllocations);
18138  }
18139 #endif
18140 
18141  if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18142  {
18143  for(size_t i = 0; i < allocationCount; ++i)
18144  {
18145  allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18146  }
18147  }
18148 
18149  return result;
18150 }
18151 
18152 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18153  VmaAllocator allocator,
18154  VkBuffer buffer,
18155  const VmaAllocationCreateInfo* pCreateInfo,
18156  VmaAllocation* pAllocation,
18157  VmaAllocationInfo* pAllocationInfo)
18158 {
18159  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18160 
18161  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18162 
18163  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18164 
18165  VkMemoryRequirements vkMemReq = {};
18166  bool requiresDedicatedAllocation = false;
18167  bool prefersDedicatedAllocation = false;
18168  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18169  requiresDedicatedAllocation,
18170  prefersDedicatedAllocation);
18171 
18172  VkResult result = allocator->AllocateMemory(
18173  vkMemReq,
18174  requiresDedicatedAllocation,
18175  prefersDedicatedAllocation,
18176  buffer, // dedicatedBuffer
18177  UINT32_MAX, // dedicatedBufferUsage
18178  VK_NULL_HANDLE, // dedicatedImage
18179  *pCreateInfo,
18180  VMA_SUBALLOCATION_TYPE_BUFFER,
18181  1, // allocationCount
18182  pAllocation);
18183 
18184 #if VMA_RECORDING_ENABLED
18185  if(allocator->GetRecorder() != VMA_NULL)
18186  {
18187  allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18188  allocator->GetCurrentFrameIndex(),
18189  vkMemReq,
18190  requiresDedicatedAllocation,
18191  prefersDedicatedAllocation,
18192  *pCreateInfo,
18193  *pAllocation);
18194  }
18195 #endif
18196 
18197  if(pAllocationInfo && result == VK_SUCCESS)
18198  {
18199  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18200  }
18201 
18202  return result;
18203 }
18204 
18205 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18206  VmaAllocator allocator,
18207  VkImage image,
18208  const VmaAllocationCreateInfo* pCreateInfo,
18209  VmaAllocation* pAllocation,
18210  VmaAllocationInfo* pAllocationInfo)
18211 {
18212  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18213 
18214  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18215 
18216  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18217 
18218  VkMemoryRequirements vkMemReq = {};
18219  bool requiresDedicatedAllocation = false;
18220  bool prefersDedicatedAllocation = false;
18221  allocator->GetImageMemoryRequirements(image, vkMemReq,
18222  requiresDedicatedAllocation, prefersDedicatedAllocation);
18223 
18224  VkResult result = allocator->AllocateMemory(
18225  vkMemReq,
18226  requiresDedicatedAllocation,
18227  prefersDedicatedAllocation,
18228  VK_NULL_HANDLE, // dedicatedBuffer
18229  UINT32_MAX, // dedicatedBufferUsage
18230  image, // dedicatedImage
18231  *pCreateInfo,
18232  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18233  1, // allocationCount
18234  pAllocation);
18235 
18236 #if VMA_RECORDING_ENABLED
18237  if(allocator->GetRecorder() != VMA_NULL)
18238  {
18239  allocator->GetRecorder()->RecordAllocateMemoryForImage(
18240  allocator->GetCurrentFrameIndex(),
18241  vkMemReq,
18242  requiresDedicatedAllocation,
18243  prefersDedicatedAllocation,
18244  *pCreateInfo,
18245  *pAllocation);
18246  }
18247 #endif
18248 
18249  if(pAllocationInfo && result == VK_SUCCESS)
18250  {
18251  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18252  }
18253 
18254  return result;
18255 }
18256 
18257 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18258  VmaAllocator allocator,
18259  VmaAllocation allocation)
18260 {
18261  VMA_ASSERT(allocator);
18262 
18263  if(allocation == VK_NULL_HANDLE)
18264  {
18265  return;
18266  }
18267 
18268  VMA_DEBUG_LOG("vmaFreeMemory");
18269 
18270  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18271 
18272 #if VMA_RECORDING_ENABLED
18273  if(allocator->GetRecorder() != VMA_NULL)
18274  {
18275  allocator->GetRecorder()->RecordFreeMemory(
18276  allocator->GetCurrentFrameIndex(),
18277  allocation);
18278  }
18279 #endif
18280 
18281  allocator->FreeMemory(
18282  1, // allocationCount
18283  &allocation);
18284 }
18285 
18286 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18287  VmaAllocator allocator,
18288  size_t allocationCount,
18289  const VmaAllocation* pAllocations)
18290 {
18291  if(allocationCount == 0)
18292  {
18293  return;
18294  }
18295 
18296  VMA_ASSERT(allocator);
18297 
18298  VMA_DEBUG_LOG("vmaFreeMemoryPages");
18299 
18300  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18301 
18302 #if VMA_RECORDING_ENABLED
18303  if(allocator->GetRecorder() != VMA_NULL)
18304  {
18305  allocator->GetRecorder()->RecordFreeMemoryPages(
18306  allocator->GetCurrentFrameIndex(),
18307  (uint64_t)allocationCount,
18308  pAllocations);
18309  }
18310 #endif
18311 
18312  allocator->FreeMemory(allocationCount, pAllocations);
18313 }
18314 
18315 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18316  VmaAllocator allocator,
18317  VmaAllocation allocation,
18318  VkDeviceSize newSize)
18319 {
18320  VMA_ASSERT(allocator && allocation);
18321 
18322  VMA_DEBUG_LOG("vmaResizeAllocation");
18323 
18324  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18325 
18326  return allocator->ResizeAllocation(allocation, newSize);
18327 }
18328 
18329 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18330  VmaAllocator allocator,
18331  VmaAllocation allocation,
18332  VmaAllocationInfo* pAllocationInfo)
18333 {
18334  VMA_ASSERT(allocator && allocation && pAllocationInfo);
18335 
18336  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18337 
18338 #if VMA_RECORDING_ENABLED
18339  if(allocator->GetRecorder() != VMA_NULL)
18340  {
18341  allocator->GetRecorder()->RecordGetAllocationInfo(
18342  allocator->GetCurrentFrameIndex(),
18343  allocation);
18344  }
18345 #endif
18346 
18347  allocator->GetAllocationInfo(allocation, pAllocationInfo);
18348 }
18349 
18350 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18351  VmaAllocator allocator,
18352  VmaAllocation allocation)
18353 {
18354  VMA_ASSERT(allocator && allocation);
18355 
18356  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18357 
18358 #if VMA_RECORDING_ENABLED
18359  if(allocator->GetRecorder() != VMA_NULL)
18360  {
18361  allocator->GetRecorder()->RecordTouchAllocation(
18362  allocator->GetCurrentFrameIndex(),
18363  allocation);
18364  }
18365 #endif
18366 
18367  return allocator->TouchAllocation(allocation);
18368 }
18369 
18370 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18371  VmaAllocator allocator,
18372  VmaAllocation allocation,
18373  void* pUserData)
18374 {
18375  VMA_ASSERT(allocator && allocation);
18376 
18377  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18378 
18379  allocation->SetUserData(allocator, pUserData);
18380 
18381 #if VMA_RECORDING_ENABLED
18382  if(allocator->GetRecorder() != VMA_NULL)
18383  {
18384  allocator->GetRecorder()->RecordSetAllocationUserData(
18385  allocator->GetCurrentFrameIndex(),
18386  allocation,
18387  pUserData);
18388  }
18389 #endif
18390 }
18391 
18392 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18393  VmaAllocator allocator,
18394  VmaAllocation* pAllocation)
18395 {
18396  VMA_ASSERT(allocator && pAllocation);
18397 
18398  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18399 
18400  allocator->CreateLostAllocation(pAllocation);
18401 
18402 #if VMA_RECORDING_ENABLED
18403  if(allocator->GetRecorder() != VMA_NULL)
18404  {
18405  allocator->GetRecorder()->RecordCreateLostAllocation(
18406  allocator->GetCurrentFrameIndex(),
18407  *pAllocation);
18408  }
18409 #endif
18410 }
18411 
18412 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18413  VmaAllocator allocator,
18414  VmaAllocation allocation,
18415  void** ppData)
18416 {
18417  VMA_ASSERT(allocator && allocation && ppData);
18418 
18419  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18420 
18421  VkResult res = allocator->Map(allocation, ppData);
18422 
18423 #if VMA_RECORDING_ENABLED
18424  if(allocator->GetRecorder() != VMA_NULL)
18425  {
18426  allocator->GetRecorder()->RecordMapMemory(
18427  allocator->GetCurrentFrameIndex(),
18428  allocation);
18429  }
18430 #endif
18431 
18432  return res;
18433 }
18434 
18435 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18436  VmaAllocator allocator,
18437  VmaAllocation allocation)
18438 {
18439  VMA_ASSERT(allocator && allocation);
18440 
18441  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18442 
18443 #if VMA_RECORDING_ENABLED
18444  if(allocator->GetRecorder() != VMA_NULL)
18445  {
18446  allocator->GetRecorder()->RecordUnmapMemory(
18447  allocator->GetCurrentFrameIndex(),
18448  allocation);
18449  }
18450 #endif
18451 
18452  allocator->Unmap(allocation);
18453 }
18454 
18455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18456 {
18457  VMA_ASSERT(allocator && allocation);
18458 
18459  VMA_DEBUG_LOG("vmaFlushAllocation");
18460 
18461  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18462 
18463  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18464 
18465 #if VMA_RECORDING_ENABLED
18466  if(allocator->GetRecorder() != VMA_NULL)
18467  {
18468  allocator->GetRecorder()->RecordFlushAllocation(
18469  allocator->GetCurrentFrameIndex(),
18470  allocation, offset, size);
18471  }
18472 #endif
18473 
18474  return res;
18475 }
18476 
18477 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18478 {
18479  VMA_ASSERT(allocator && allocation);
18480 
18481  VMA_DEBUG_LOG("vmaInvalidateAllocation");
18482 
18483  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18484 
18485  const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18486 
18487 #if VMA_RECORDING_ENABLED
18488  if(allocator->GetRecorder() != VMA_NULL)
18489  {
18490  allocator->GetRecorder()->RecordInvalidateAllocation(
18491  allocator->GetCurrentFrameIndex(),
18492  allocation, offset, size);
18493  }
18494 #endif
18495 
18496  return res;
18497 }
18498 
18499 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18500  VmaAllocator allocator,
18501  uint32_t allocationCount,
18502  const VmaAllocation* allocations,
18503  const VkDeviceSize* offsets,
18504  const VkDeviceSize* sizes)
18505 {
18506  VMA_ASSERT(allocator);
18507 
18508  if(allocationCount == 0)
18509  {
18510  return VK_SUCCESS;
18511  }
18512 
18513  VMA_ASSERT(allocations);
18514 
18515  VMA_DEBUG_LOG("vmaFlushAllocations");
18516 
18517  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18518 
18519  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18520 
18521 #if VMA_RECORDING_ENABLED
18522  if(allocator->GetRecorder() != VMA_NULL)
18523  {
18524  //TODO
18525  }
18526 #endif
18527 
18528  return res;
18529 }
18530 
18531 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18532  VmaAllocator allocator,
18533  uint32_t allocationCount,
18534  const VmaAllocation* allocations,
18535  const VkDeviceSize* offsets,
18536  const VkDeviceSize* sizes)
18537 {
18538  VMA_ASSERT(allocator);
18539 
18540  if(allocationCount == 0)
18541  {
18542  return VK_SUCCESS;
18543  }
18544 
18545  VMA_ASSERT(allocations);
18546 
18547  VMA_DEBUG_LOG("vmaInvalidateAllocations");
18548 
18549  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18550 
18551  const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18552 
18553 #if VMA_RECORDING_ENABLED
18554  if(allocator->GetRecorder() != VMA_NULL)
18555  {
18556  //TODO
18557  }
18558 #endif
18559 
18560  return res;
18561 }
18562 
18563 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
18564 {
18565  VMA_ASSERT(allocator);
18566 
18567  VMA_DEBUG_LOG("vmaCheckCorruption");
18568 
18569  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18570 
18571  return allocator->CheckCorruption(memoryTypeBits);
18572 }
18573 
18574 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
18575  VmaAllocator allocator,
18576  const VmaAllocation* pAllocations,
18577  size_t allocationCount,
18578  VkBool32* pAllocationsChanged,
18579  const VmaDefragmentationInfo *pDefragmentationInfo,
18580  VmaDefragmentationStats* pDefragmentationStats)
18581 {
18582  // Deprecated interface, reimplemented using new one.
18583 
18584  VmaDefragmentationInfo2 info2 = {};
18585  info2.allocationCount = (uint32_t)allocationCount;
18586  info2.pAllocations = pAllocations;
18587  info2.pAllocationsChanged = pAllocationsChanged;
18588  if(pDefragmentationInfo != VMA_NULL)
18589  {
18590  info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
18591  info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
18592  }
18593  else
18594  {
18595  info2.maxCpuAllocationsToMove = UINT32_MAX;
18596  info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
18597  }
18598  // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
18599 
18601  VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
18602  if(res == VK_NOT_READY)
18603  {
18604  res = vmaDefragmentationEnd( allocator, ctx);
18605  }
18606  return res;
18607 }
18608 
18609 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
18610  VmaAllocator allocator,
18611  const VmaDefragmentationInfo2* pInfo,
18612  VmaDefragmentationStats* pStats,
18613  VmaDefragmentationContext *pContext)
18614 {
18615  VMA_ASSERT(allocator && pInfo && pContext);
18616 
18617  // Degenerate case: Nothing to defragment.
18618  if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
18619  {
18620  return VK_SUCCESS;
18621  }
18622 
18623  VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
18624  VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
18625  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
18626  VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
18627 
18628  VMA_DEBUG_LOG("vmaDefragmentationBegin");
18629 
18630  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18631 
18632  VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
18633 
18634 #if VMA_RECORDING_ENABLED
18635  if(allocator->GetRecorder() != VMA_NULL)
18636  {
18637  allocator->GetRecorder()->RecordDefragmentationBegin(
18638  allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
18639  }
18640 #endif
18641 
18642  return res;
18643 }
18644 
18645 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
18646  VmaAllocator allocator,
18647  VmaDefragmentationContext context)
18648 {
18649  VMA_ASSERT(allocator);
18650 
18651  VMA_DEBUG_LOG("vmaDefragmentationEnd");
18652 
18653  if(context != VK_NULL_HANDLE)
18654  {
18655  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18656 
18657 #if VMA_RECORDING_ENABLED
18658  if(allocator->GetRecorder() != VMA_NULL)
18659  {
18660  allocator->GetRecorder()->RecordDefragmentationEnd(
18661  allocator->GetCurrentFrameIndex(), context);
18662  }
18663 #endif
18664 
18665  return allocator->DefragmentationEnd(context);
18666  }
18667  else
18668  {
18669  return VK_SUCCESS;
18670  }
18671 }
18672 
18673 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
18674  VmaAllocator allocator,
18675  VmaDefragmentationContext context,
18677  )
18678 {
18679  VMA_ASSERT(allocator);
18680  VMA_ASSERT(pInfo);
18681 
18682  VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
18683 
18684  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18685 
18686  if(context == VK_NULL_HANDLE)
18687  {
18688  pInfo->moveCount = 0;
18689  return VK_SUCCESS;
18690  }
18691 
18692  return allocator->DefragmentationPassBegin(pInfo, context);
18693 }
18694 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
18695  VmaAllocator allocator,
18696  VmaDefragmentationContext context)
18697 {
18698  VMA_ASSERT(allocator);
18699 
18700  VMA_DEBUG_LOG("vmaEndDefragmentationPass");
18701  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18702 
18703  if(context == VK_NULL_HANDLE)
18704  return VK_SUCCESS;
18705 
18706  return allocator->DefragmentationPassEnd(context);
18707 }
18708 
18709 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
18710  VmaAllocator allocator,
18711  VmaAllocation allocation,
18712  VkBuffer buffer)
18713 {
18714  VMA_ASSERT(allocator && allocation && buffer);
18715 
18716  VMA_DEBUG_LOG("vmaBindBufferMemory");
18717 
18718  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18719 
18720  return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
18721 }
18722 
18723 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
18724  VmaAllocator allocator,
18725  VmaAllocation allocation,
18726  VkDeviceSize allocationLocalOffset,
18727  VkBuffer buffer,
18728  const void* pNext)
18729 {
18730  VMA_ASSERT(allocator && allocation && buffer);
18731 
18732  VMA_DEBUG_LOG("vmaBindBufferMemory2");
18733 
18734  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18735 
18736  return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
18737 }
18738 
18739 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
18740  VmaAllocator allocator,
18741  VmaAllocation allocation,
18742  VkImage image)
18743 {
18744  VMA_ASSERT(allocator && allocation && image);
18745 
18746  VMA_DEBUG_LOG("vmaBindImageMemory");
18747 
18748  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18749 
18750  return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18751 }
18752 
18753 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18754  VmaAllocator allocator,
18755  VmaAllocation allocation,
18756  VkDeviceSize allocationLocalOffset,
18757  VkImage image,
18758  const void* pNext)
18759 {
18760  VMA_ASSERT(allocator && allocation && image);
18761 
18762  VMA_DEBUG_LOG("vmaBindImageMemory2");
18763 
18764  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18765 
18766  return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18767 }
18768 
18769 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18770  VmaAllocator allocator,
18771  const VkBufferCreateInfo* pBufferCreateInfo,
18772  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18773  VkBuffer* pBuffer,
18774  VmaAllocation* pAllocation,
18775  VmaAllocationInfo* pAllocationInfo)
18776 {
18777  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18778 
18779  if(pBufferCreateInfo->size == 0)
18780  {
18781  return VK_ERROR_VALIDATION_FAILED_EXT;
18782  }
18783  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18784  !allocator->m_UseKhrBufferDeviceAddress)
18785  {
18786  VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18787  return VK_ERROR_VALIDATION_FAILED_EXT;
18788  }
18789 
18790  VMA_DEBUG_LOG("vmaCreateBuffer");
18791 
18792  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18793 
18794  *pBuffer = VK_NULL_HANDLE;
18795  *pAllocation = VK_NULL_HANDLE;
18796 
18797  // 1. Create VkBuffer.
18798  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18799  allocator->m_hDevice,
18800  pBufferCreateInfo,
18801  allocator->GetAllocationCallbacks(),
18802  pBuffer);
18803  if(res >= 0)
18804  {
18805  // 2. vkGetBufferMemoryRequirements.
18806  VkMemoryRequirements vkMemReq = {};
18807  bool requiresDedicatedAllocation = false;
18808  bool prefersDedicatedAllocation = false;
18809  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18810  requiresDedicatedAllocation, prefersDedicatedAllocation);
18811 
18812  // 3. Allocate memory using allocator.
18813  res = allocator->AllocateMemory(
18814  vkMemReq,
18815  requiresDedicatedAllocation,
18816  prefersDedicatedAllocation,
18817  *pBuffer, // dedicatedBuffer
18818  pBufferCreateInfo->usage, // dedicatedBufferUsage
18819  VK_NULL_HANDLE, // dedicatedImage
18820  *pAllocationCreateInfo,
18821  VMA_SUBALLOCATION_TYPE_BUFFER,
18822  1, // allocationCount
18823  pAllocation);
18824 
18825 #if VMA_RECORDING_ENABLED
18826  if(allocator->GetRecorder() != VMA_NULL)
18827  {
18828  allocator->GetRecorder()->RecordCreateBuffer(
18829  allocator->GetCurrentFrameIndex(),
18830  *pBufferCreateInfo,
18831  *pAllocationCreateInfo,
18832  *pAllocation);
18833  }
18834 #endif
18835 
18836  if(res >= 0)
18837  {
18838  // 3. Bind buffer with memory.
18839  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18840  {
18841  res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18842  }
18843  if(res >= 0)
18844  {
18845  // All steps succeeded.
18846  #if VMA_STATS_STRING_ENABLED
18847  (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18848  #endif
18849  if(pAllocationInfo != VMA_NULL)
18850  {
18851  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18852  }
18853 
18854  return VK_SUCCESS;
18855  }
18856  allocator->FreeMemory(
18857  1, // allocationCount
18858  pAllocation);
18859  *pAllocation = VK_NULL_HANDLE;
18860  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18861  *pBuffer = VK_NULL_HANDLE;
18862  return res;
18863  }
18864  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18865  *pBuffer = VK_NULL_HANDLE;
18866  return res;
18867  }
18868  return res;
18869 }
18870 
18871 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18872  VmaAllocator allocator,
18873  VkBuffer buffer,
18874  VmaAllocation allocation)
18875 {
18876  VMA_ASSERT(allocator);
18877 
18878  if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18879  {
18880  return;
18881  }
18882 
18883  VMA_DEBUG_LOG("vmaDestroyBuffer");
18884 
18885  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18886 
18887 #if VMA_RECORDING_ENABLED
18888  if(allocator->GetRecorder() != VMA_NULL)
18889  {
18890  allocator->GetRecorder()->RecordDestroyBuffer(
18891  allocator->GetCurrentFrameIndex(),
18892  allocation);
18893  }
18894 #endif
18895 
18896  if(buffer != VK_NULL_HANDLE)
18897  {
18898  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18899  }
18900 
18901  if(allocation != VK_NULL_HANDLE)
18902  {
18903  allocator->FreeMemory(
18904  1, // allocationCount
18905  &allocation);
18906  }
18907 }
18908 
18909 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18910  VmaAllocator allocator,
18911  const VkImageCreateInfo* pImageCreateInfo,
18912  const VmaAllocationCreateInfo* pAllocationCreateInfo,
18913  VkImage* pImage,
18914  VmaAllocation* pAllocation,
18915  VmaAllocationInfo* pAllocationInfo)
18916 {
18917  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18918 
18919  if(pImageCreateInfo->extent.width == 0 ||
18920  pImageCreateInfo->extent.height == 0 ||
18921  pImageCreateInfo->extent.depth == 0 ||
18922  pImageCreateInfo->mipLevels == 0 ||
18923  pImageCreateInfo->arrayLayers == 0)
18924  {
18925  return VK_ERROR_VALIDATION_FAILED_EXT;
18926  }
18927 
18928  VMA_DEBUG_LOG("vmaCreateImage");
18929 
18930  VMA_DEBUG_GLOBAL_MUTEX_LOCK
18931 
18932  *pImage = VK_NULL_HANDLE;
18933  *pAllocation = VK_NULL_HANDLE;
18934 
18935  // 1. Create VkImage.
18936  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18937  allocator->m_hDevice,
18938  pImageCreateInfo,
18939  allocator->GetAllocationCallbacks(),
18940  pImage);
18941  if(res >= 0)
18942  {
18943  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18944  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18945  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18946 
18947  // 2. Allocate memory using allocator.
18948  VkMemoryRequirements vkMemReq = {};
18949  bool requiresDedicatedAllocation = false;
18950  bool prefersDedicatedAllocation = false;
18951  allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18952  requiresDedicatedAllocation, prefersDedicatedAllocation);
18953 
18954  res = allocator->AllocateMemory(
18955  vkMemReq,
18956  requiresDedicatedAllocation,
18957  prefersDedicatedAllocation,
18958  VK_NULL_HANDLE, // dedicatedBuffer
18959  UINT32_MAX, // dedicatedBufferUsage
18960  *pImage, // dedicatedImage
18961  *pAllocationCreateInfo,
18962  suballocType,
18963  1, // allocationCount
18964  pAllocation);
18965 
18966 #if VMA_RECORDING_ENABLED
18967  if(allocator->GetRecorder() != VMA_NULL)
18968  {
18969  allocator->GetRecorder()->RecordCreateImage(
18970  allocator->GetCurrentFrameIndex(),
18971  *pImageCreateInfo,
18972  *pAllocationCreateInfo,
18973  *pAllocation);
18974  }
18975 #endif
18976 
18977  if(res >= 0)
18978  {
18979  // 3. Bind image with memory.
18980  if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18981  {
18982  res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18983  }
18984  if(res >= 0)
18985  {
18986  // All steps succeeded.
18987  #if VMA_STATS_STRING_ENABLED
18988  (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18989  #endif
18990  if(pAllocationInfo != VMA_NULL)
18991  {
18992  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18993  }
18994 
18995  return VK_SUCCESS;
18996  }
18997  allocator->FreeMemory(
18998  1, // allocationCount
18999  pAllocation);
19000  *pAllocation = VK_NULL_HANDLE;
19001  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19002  *pImage = VK_NULL_HANDLE;
19003  return res;
19004  }
19005  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19006  *pImage = VK_NULL_HANDLE;
19007  return res;
19008  }
19009  return res;
19010 }
19011 
19012 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19013  VmaAllocator allocator,
19014  VkImage image,
19015  VmaAllocation allocation)
19016 {
19017  VMA_ASSERT(allocator);
19018 
19019  if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19020  {
19021  return;
19022  }
19023 
19024  VMA_DEBUG_LOG("vmaDestroyImage");
19025 
19026  VMA_DEBUG_GLOBAL_MUTEX_LOCK
19027 
19028 #if VMA_RECORDING_ENABLED
19029  if(allocator->GetRecorder() != VMA_NULL)
19030  {
19031  allocator->GetRecorder()->RecordDestroyImage(
19032  allocator->GetCurrentFrameIndex(),
19033  allocation);
19034  }
19035 #endif
19036 
19037  if(image != VK_NULL_HANDLE)
19038  {
19039  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19040  }
19041  if(allocation != VK_NULL_HANDLE)
19042  {
19043  allocator->FreeMemory(
19044  1, // allocationCount
19045  &allocation);
19046  }
19047 }
19048 
19049 #endif // #ifdef VMA_IMPLEMENTATION
VmaStats
struct VmaStats VmaStats
General statistics from current state of Allocator.
VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
Definition: vk_mem_alloc.h:2255
VmaVulkanFunctions::vkAllocateMemory
PFN_vkAllocateMemory vkAllocateMemory
Definition: vk_mem_alloc.h:2212
VmaDeviceMemoryCallbacks::pfnFree
PFN_vmaFreeDeviceMemoryFunction pfnFree
Optional, can be null.
Definition: vk_mem_alloc.h:2100
VMA_RECORD_FLAG_BITS_MAX_ENUM
@ VMA_RECORD_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2249
VmaVulkanFunctions::vkGetPhysicalDeviceProperties
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties
Definition: vk_mem_alloc.h:2210
VmaAllocatorCreateInfo::physicalDevice
VkPhysicalDevice physicalDevice
Vulkan physical device.
Definition: vk_mem_alloc.h:2275
VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
@ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
Enables alternative, linear allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2872
VmaDefragmentationInfo2::allocationCount
uint32_t allocationCount
Number of allocations in pAllocations array.
Definition: vk_mem_alloc.h:3485
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:2301
VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
@ VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT
Definition: vk_mem_alloc.h:2163
VmaBudget
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
Definition: vk_mem_alloc.h:2474
VmaDefragmentationPassMoveInfo::memory
VkDeviceMemory memory
Definition: vk_mem_alloc.h:3553
VmaDefragmentationInfo2::pPools
const VmaPool * pPools
Either null or pointer to array of pools to be defragmented.
Definition: vk_mem_alloc.h:3519
VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
@ VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED
Definition: vk_mem_alloc.h:2618
VmaDefragmentationInfo
struct VmaDefragmentationInfo VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
VmaPoolStats
Describes parameter of existing VmaPool.
Definition: vk_mem_alloc.h:2944
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT
Definition: vk_mem_alloc.h:2701
VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
@ 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:2111
VmaPoolStats::unusedSize
VkDeviceSize unusedSize
Total number of bytes in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:2950
VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
@ VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT
Definition: vk_mem_alloc.h:2681
VmaRecordFlagBits
VmaRecordFlagBits
Flags to be used in VmaRecordSettings::flags.
Definition: vk_mem_alloc.h:2241
vmaSetPoolName
void vmaSetPoolName(VmaAllocator allocator, VmaPool pool, const char *pName)
Sets name of a custom pool.
VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
Definition: vk_mem_alloc.h:2096
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
@ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
Definition: vk_mem_alloc.h:2668
VmaAllocatorCreateInfo::preferredLargeHeapBlockSize
VkDeviceSize preferredLargeHeapBlockSize
Preferred size of a single VkDeviceMemory block to be allocated from large heaps > 1 GiB....
Definition: vk_mem_alloc.h:2281
VMA_RECORD_FLUSH_AFTER_CALL_BIT
@ VMA_RECORD_FLUSH_AFTER_CALL_BIT
Enables flush after recording every function call.
Definition: vk_mem_alloc.h:2247
VmaAllocationCreateInfo
struct VmaAllocationCreateInfo VmaAllocationCreateInfo
vmaResizeAllocation
VkResult vmaResizeAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize newSize)
Deprecated.
VmaVulkanFunctions::vkUnmapMemory
PFN_vkUnmapMemory vkUnmapMemory
Definition: vk_mem_alloc.h:2215
VmaAllocationInfo::deviceMemory
VkDeviceMemory deviceMemory
Handle to Vulkan memory object.
Definition: vk_mem_alloc.h:3087
VmaStatInfo::unusedRangeCount
uint32_t unusedRangeCount
Number of free ranges of memory between allocations.
Definition: vk_mem_alloc.h:2441
VmaAllocationCreateInfo::pUserData
void * pUserData
Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
Definition: vk_mem_alloc.h:2775
VmaStatInfo::unusedRangeSizeMax
VkDeviceSize unusedRangeSizeMax
Definition: vk_mem_alloc.h:2447
VmaVulkanFunctions::vkMapMemory
PFN_vkMapMemory vkMapMemory
Definition: vk_mem_alloc.h:2214
VMA_RECORDING_ENABLED
#define VMA_RECORDING_ENABLED
Definition: vk_mem_alloc.h:1912
VmaDefragmentationPassMoveInfo::offset
VkDeviceSize offset
Definition: vk_mem_alloc.h:3554
VmaDefragmentationPassInfo::pMoves
VmaDefragmentationPassMoveInfo * pMoves
Definition: vk_mem_alloc.h:3563
VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
Definition: vk_mem_alloc.h:2712
vmaUnmapMemory
void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
VmaAllocatorInfo::instance
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2373
VmaBudget::usage
VkDeviceSize usage
Estimated current memory usage of the program, in bytes.
Definition: vk_mem_alloc.h:2498
VmaAllocator
Represents main object of this library initialized.
VmaVulkanFunctions::vkCmdCopyBuffer
PFN_vkCmdCopyBuffer vkCmdCopyBuffer
Definition: vk_mem_alloc.h:2226
VmaAllocatorCreateInfo
Description of a Allocator to be created.
Definition: vk_mem_alloc.h:2270
VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT
@ 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:2642
VmaAllocatorInfo::device
VkDevice device
Handle to Vulkan device object.
Definition: vk_mem_alloc.h:2383
VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
@ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:3471
VmaPoolStats::unusedRangeSizeMax
VkDeviceSize unusedRangeSizeMax
Size of the largest continuous free memory region available for new allocation.
Definition: vk_mem_alloc.h:2963
VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT
Definition: vk_mem_alloc.h:2705
VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
@ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT
Enables usage of VK_KHR_dedicated_allocation extension.
Definition: vk_mem_alloc.h:2136
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:3580
VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
@ VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT
Definition: vk_mem_alloc.h:2696
VmaMemoryUsage
VmaMemoryUsage
Definition: vk_mem_alloc.h:2557
vmaFreeMemoryPages
void vmaFreeMemoryPages(VmaAllocator allocator, size_t allocationCount, const VmaAllocation *pAllocations)
Frees memory and destroys multiple allocations.
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:2437
VmaPoolCreateInfo::memoryTypeIndex
uint32_t memoryTypeIndex
Vulkan memory type index to allocate this pool from.
Definition: vk_mem_alloc.h:2900
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:2912
VmaDefragmentationInfo2::poolCount
uint32_t poolCount
Numer of pools in pPools array.
Definition: vk_mem_alloc.h:3503
VmaDefragmentationPassMoveInfo
Definition: vk_mem_alloc.h:3551
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...
VmaPoolStats::allocationCount
size_t allocationCount
Number of VmaAllocation objects created from this pool that were not destroyed or lost.
Definition: vk_mem_alloc.h:2953
VmaAllocatorCreateFlags
VkFlags VmaAllocatorCreateFlags
Definition: vk_mem_alloc.h:2203
vmaFreeStatsString
void vmaFreeStatsString(VmaAllocator allocator, char *pStatsString)
vmaAllocateMemoryForBuffer
VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaAllocationCreateInfo *pCreateInfo, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
VmaVulkanFunctions
struct VmaVulkanFunctions VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2201
VmaDefragmentationFlagBits
VmaDefragmentationFlagBits
Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
Definition: vk_mem_alloc.h:3469
VmaAllocationInfo::offset
VkDeviceSize offset
Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory,...
Definition: vk_mem_alloc.h:3092
VmaAllocationCreateFlagBits
VmaAllocationCreateFlagBits
Flags to be passed as VmaAllocationCreateInfo::flags.
Definition: vk_mem_alloc.h:2624
VmaVulkanFunctions::vkGetPhysicalDeviceMemoryProperties
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties
Definition: vk_mem_alloc.h:2211
VmaPoolCreateFlags
VkFlags VmaPoolCreateFlags
Definition: vk_mem_alloc.h:2893
vmaCreateLostAllocation
void vmaCreateLostAllocation(VmaAllocator allocator, VmaAllocation *pAllocation)
Creates new allocation that is in lost state from the beginning.
vmaInvalidateAllocations
VkResult vmaInvalidateAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Invalidates memory of given set of allocations.
VmaDeviceMemoryCallbacks
struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
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:2768
vmaGetMemoryProperties
void vmaGetMemoryProperties(VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
VmaStats::total
VmaStatInfo total
Definition: vk_mem_alloc.h:2455
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
@ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
Set this flag if the allocation should have its own memory block.
Definition: vk_mem_alloc.h:2631
vmaDefragmentationEnd
VkResult vmaDefragmentationEnd(VmaAllocator allocator, VmaDefragmentationContext context)
Ends defragmentation process.
VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
@ VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT
Definition: vk_mem_alloc.h:2151
VmaDefragmentationInfo2::flags
VmaDefragmentationFlags flags
Reserved for future use. Should be 0.
Definition: vk_mem_alloc.h:3482
VmaVulkanFunctions::vkBindImageMemory
PFN_vkBindImageMemory vkBindImageMemory
Definition: vk_mem_alloc.h:2219
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:3534
VmaDefragmentationStats
Statistics returned by function vmaDefragment().
Definition: vk_mem_alloc.h:3584
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:2947
VmaVulkanFunctions::vkFreeMemory
PFN_vkFreeMemory vkFreeMemory
Definition: vk_mem_alloc.h:2213
VmaRecordFlags
VkFlags VmaRecordFlags
Definition: vk_mem_alloc.h:2251
vmaFlushAllocation
VkResult vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Flushes memory of given allocation.
VMA_MEMORY_USAGE_CPU_ONLY
@ VMA_MEMORY_USAGE_CPU_ONLY
Definition: vk_mem_alloc.h:2588
VmaAllocation
Represents single memory allocation.
VMA_MEMORY_USAGE_CPU_COPY
@ VMA_MEMORY_USAGE_CPU_COPY
Definition: vk_mem_alloc.h:2610
vmaSetAllocationUserData
void vmaSetAllocationUserData(VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
Sets pUserData in given allocation to new value.
VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
@ VMA_DEFRAGMENTATION_FLAG_INCREMENTAL
Definition: vk_mem_alloc.h:3470
VmaAllocatorCreateInfo::pRecordSettings
const VmaRecordSettings * pRecordSettings
Parameters for recording of VMA calls. Can be null.
Definition: vk_mem_alloc.h:2339
VmaVulkanFunctions::vkBindBufferMemory
PFN_vkBindBufferMemory vkBindBufferMemory
Definition: vk_mem_alloc.h:2218
VmaVulkanFunctions::vkGetBufferMemoryRequirements
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements
Definition: vk_mem_alloc.h:2220
VmaDefragmentationInfo2::commandBuffer
VkCommandBuffer commandBuffer
Optional. Command buffer where GPU copy commands will be posted.
Definition: vk_mem_alloc.h:3548
VmaStats
General statistics from current state of Allocator.
Definition: vk_mem_alloc.h:2452
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:2917
VmaAllocatorCreateInfo::vulkanApiVersion
uint32_t vulkanApiVersion
Optional. The highest version of Vulkan that the application is designed to use.
Definition: vk_mem_alloc.h:2353
VmaStatInfo
Calculated statistics of memory usage in entire allocator.
Definition: vk_mem_alloc.h:2435
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:3588
vmaDefragment
VkResult vmaDefragment(VmaAllocator allocator, const VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
Deprecated. Compacts memory by moving allocations.
VmaDefragmentationPassInfo::moveCount
uint32_t moveCount
Definition: vk_mem_alloc.h:3562
VMA_MEMORY_USAGE_GPU_ONLY
@ VMA_MEMORY_USAGE_GPU_ONLY
Definition: vk_mem_alloc.h:2578
vmaBeginDefragmentationPass
VkResult vmaBeginDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context, VmaDefragmentationPassInfo *pInfo)
vmaFindMemoryTypeIndex
VkResult vmaFindMemoryTypeIndex(VmaAllocator allocator, uint32_t memoryTypeBits, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
vmaFlushAllocations
VkResult vmaFlushAllocations(VmaAllocator allocator, uint32_t allocationCount, const VmaAllocation *allocations, const VkDeviceSize *offsets, const VkDeviceSize *sizes)
Flushes memory of given set of allocations.
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:2445
VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
@ VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
Definition: vk_mem_alloc.h:2199
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:2443
VmaAllocatorCreateInfo::pAllocationCallbacks
const VkAllocationCallbacks * pAllocationCallbacks
Custom CPU memory allocation callbacks. Optional.
Definition: vk_mem_alloc.h:2284
VmaAllocatorCreateFlagBits
VmaAllocatorCreateFlagBits
Flags for created VmaAllocator.
Definition: vk_mem_alloc.h:2106
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:2925
VmaPoolCreateInfo
Describes parameter of created VmaPool.
Definition: vk_mem_alloc.h:2897
VmaDeviceMemoryCallbacks::pfnAllocate
PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
Optional, can be null.
Definition: vk_mem_alloc.h:2098
VmaPool
Represents custom memory pool.
VMA_MEMORY_USAGE_GPU_TO_CPU
@ VMA_MEMORY_USAGE_GPU_TO_CPU
Definition: vk_mem_alloc.h:2604
VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
@ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
Definition: vk_mem_alloc.h:2675
VmaPoolCreateInfo::flags
VmaPoolCreateFlags flags
Use combination of VmaPoolCreateFlagBits.
Definition: vk_mem_alloc.h:2903
VMA_MEMORY_USAGE_MAX_ENUM
@ VMA_MEMORY_USAGE_MAX_ENUM
Definition: vk_mem_alloc.h:2620
VmaStatInfo::allocationCount
uint32_t allocationCount
Number of VmaAllocation allocation objects allocated.
Definition: vk_mem_alloc.h:2439
VmaVulkanFunctions::vkInvalidateMappedMemoryRanges
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges
Definition: vk_mem_alloc.h:2217
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:3479
VmaDefragmentationInfo::maxBytesToMove
VkDeviceSize maxBytesToMove
Maximum total numbers of bytes that can be copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3575
VmaBudget::blockBytes
VkDeviceSize blockBytes
Sum size of all VkDeviceMemory blocks allocated from particular heap, in bytes.
Definition: vk_mem_alloc.h:2477
VmaAllocatorInfo
Information about existing VmaAllocator object.
Definition: vk_mem_alloc.h:2368
VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2891
VmaAllocationCreateInfo::requiredFlags
VkMemoryPropertyFlags requiredFlags
Flags that must be set in a Memory Type chosen for an allocation.
Definition: vk_mem_alloc.h:2749
VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT
Definition: vk_mem_alloc.h:2722
VmaStatInfo
struct VmaStatInfo VmaStatInfo
Calculated statistics of memory usage in entire allocator.
VmaStatInfo::allocationSizeAvg
VkDeviceSize allocationSizeAvg
Definition: vk_mem_alloc.h:2446
vmaDestroyAllocator
void vmaDestroyAllocator(VmaAllocator allocator)
Destroys allocator object.
VmaAllocatorCreateInfo::pDeviceMemoryCallbacks
const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
Informative callbacks for vkAllocateMemory, vkFreeMemory. Optional.
Definition: vk_mem_alloc.h:2287
VMA_ALLOCATION_CREATE_STRATEGY_MASK
@ VMA_ALLOCATION_CREATE_STRATEGY_MASK
Definition: vk_mem_alloc.h:2726
VmaAllocatorCreateInfo::device
VkDevice device
Vulkan device.
Definition: vk_mem_alloc.h:2278
vmaFindMemoryTypeIndexForImageInfo
VkResult vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator, const VkImageCreateInfo *pImageCreateInfo, const VmaAllocationCreateInfo *pAllocationCreateInfo, uint32_t *pMemoryTypeIndex)
Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
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:2326
VmaDefragmentationPassMoveInfo::allocation
VmaAllocation allocation
Definition: vk_mem_alloc.h:3552
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:2509
VmaPoolStats
struct VmaPoolStats VmaPoolStats
Describes parameter of existing VmaPool.
VmaDefragmentationPassInfo
struct VmaDefragmentationPassInfo VmaDefragmentationPassInfo
Parameters for incremental defragmentation steps.
VmaVulkanFunctions
Pointers to some Vulkan functions - a subset used by the library.
Definition: vk_mem_alloc.h:2209
VmaAllocationInfo::pMappedData
void * pMappedData
Pointer to the beginning of this allocation as mapped data.
Definition: vk_mem_alloc.h:3112
VmaAllocatorCreateInfo::flags
VmaAllocatorCreateFlags flags
Flags for created allocator. Use VmaAllocatorCreateFlagBits enum.
Definition: vk_mem_alloc.h:2272
VmaDefragmentationFlags
VkFlags VmaDefragmentationFlags
Definition: vk_mem_alloc.h:3473
VmaDefragmentationInfo2::pAllocations
const VmaAllocation * pAllocations
Pointer to array of allocations that can be defragmented.
Definition: vk_mem_alloc.h:3494
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:2224
VmaDeviceMemoryCallbacks::pUserData
void * pUserData
Optional, can be null.
Definition: vk_mem_alloc.h:2102
VmaRecordSettings
struct VmaRecordSettings VmaRecordSettings
Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSetting...
VmaStatInfo::unusedRangeSizeAvg
VkDeviceSize unusedRangeSizeAvg
Definition: vk_mem_alloc.h:2447
VMA_MEMORY_USAGE_CPU_TO_GPU
@ VMA_MEMORY_USAGE_CPU_TO_GPU
Definition: vk_mem_alloc.h:2595
VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
Definition: vk_mem_alloc.h:2719
VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
@ VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT
Definition: vk_mem_alloc.h:2716
VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
@ VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
Definition: vk_mem_alloc.h:2181
VmaDefragmentationStats
struct VmaDefragmentationStats VmaDefragmentationStats
Statistics returned by function vmaDefragment().
VmaAllocationCreateInfo::usage
VmaMemoryUsage usage
Intended usage of memory.
Definition: vk_mem_alloc.h:2744
VmaStatInfo::allocationSizeMin
VkDeviceSize allocationSizeMin
Definition: vk_mem_alloc.h:2446
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:3103
VmaRecordSettings::flags
VmaRecordFlags flags
Flags for recording. Use VmaRecordFlagBits enum.
Definition: vk_mem_alloc.h:2257
VmaVulkanFunctions::vkFlushMappedMemoryRanges
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges
Definition: vk_mem_alloc.h:2216
VmaAllocationInfo::pUserData
void * pUserData
Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vma...
Definition: vk_mem_alloc.h:3117
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
@ 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:2855
vmaInvalidateAllocation
VkResult vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
Invalidates memory of given allocation.
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:2454
VmaAllocatorCreateInfo::pVulkanFunctions
const VmaVulkanFunctions * pVulkanFunctions
Pointers to Vulkan functions. Can be null.
Definition: vk_mem_alloc.h:2332
VmaPoolStats::blockCount
size_t blockCount
Number of VkDeviceMemory blocks allocated for this pool.
Definition: vk_mem_alloc.h:2966
vmaCreateAllocator
VkResult vmaCreateAllocator(const VmaAllocatorCreateInfo *pCreateInfo, VmaAllocator *pAllocator)
Creates Allocator object.
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...
VmaDefragmentationPassInfo
Parameters for incremental defragmentation steps.
Definition: vk_mem_alloc.h:3561
VmaStats::memoryType
VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
Definition: vk_mem_alloc.h:2453
VmaAllocationCreateFlags
VkFlags VmaAllocationCreateFlags
Definition: vk_mem_alloc.h:2733
VmaAllocatorCreateInfo::instance
VkInstance instance
Handle to Vulkan instance object.
Definition: vk_mem_alloc.h:2344
VMA_MEMORY_USAGE_UNKNOWN
@ VMA_MEMORY_USAGE_UNKNOWN
Definition: vk_mem_alloc.h:2561
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:3539
VmaVulkanFunctions::vkDestroyBuffer
PFN_vkDestroyBuffer vkDestroyBuffer
Definition: vk_mem_alloc.h:2223
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:2939
VmaVulkanFunctions::vkDestroyImage
PFN_vkDestroyImage vkDestroyImage
Definition: vk_mem_alloc.h:2225
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:3524
VmaPoolCreateInfo
struct VmaPoolCreateInfo VmaPoolCreateInfo
Describes parameter of created VmaPool.
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:3078
vmaDestroyImage
void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation)
Destroys Vulkan image and frees allocated memory.
VMA_ALLOCATION_CREATE_MAPPED_BIT
@ 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:2655
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:2222
PFN_vmaAllocateDeviceMemoryFunction
void(VKAPI_PTR * PFN_vmaAllocateDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size, void *pUserData)
Callback function called after successful vkAllocateMemory.
Definition: vk_mem_alloc.h:2075
vmaGetAllocatorInfo
void vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo *pAllocatorInfo)
Returns information about existing VmaAllocator object - handle to Vulkan device etc.
VmaPoolStats::unusedRangeCount
size_t unusedRangeCount
Number of continuous memory ranges in the pool not used by any VmaAllocation.
Definition: vk_mem_alloc.h:2956
VmaPoolCreateFlagBits
VmaPoolCreateFlagBits
Flags to be passed as VmaPoolCreateInfo::flags.
Definition: vk_mem_alloc.h:2837
VmaAllocationInfo
struct VmaAllocationInfo VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
VmaDefragmentationStats::bytesMoved
VkDeviceSize bytesMoved
Total number of bytes that have been copied while moving allocations to different places.
Definition: vk_mem_alloc.h:3586
VmaStatInfo::unusedRangeSizeMin
VkDeviceSize unusedRangeSizeMin
Definition: vk_mem_alloc.h:2447
VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
@ VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
Definition: vk_mem_alloc.h:2686
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.
PFN_vmaFreeDeviceMemoryFunction
void(VKAPI_PTR * PFN_vmaFreeDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size, void *pUserData)
Callback function called before vkFreeMemory.
Definition: vk_mem_alloc.h:2082
VmaDefragmentationPassMoveInfo
struct VmaDefragmentationPassMoveInfo VmaDefragmentationPassMoveInfo
VmaAllocationCreateInfo::flags
VmaAllocationCreateFlags flags
Use VmaAllocationCreateFlagBits enum.
Definition: vk_mem_alloc.h:2738
VmaVulkanFunctions::vkGetImageMemoryRequirements
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements
Definition: vk_mem_alloc.h:2221
vmaGetBudget
void vmaGetBudget(VmaAllocator allocator, VmaBudget *pBudget)
Retrieves information about current memory budget for all memory heaps.
VmaAllocationCreateInfo
Definition: vk_mem_alloc.h:2736
VmaAllocationCreateInfo::preferredFlags
VkMemoryPropertyFlags preferredFlags
Flags that preferably should be set in a memory type chosen for an allocation.
Definition: vk_mem_alloc.h:2754
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.
VmaBudget
struct VmaBudget VmaBudget
Statistics of current memory usage and available budget, in bytes, for specific memory heap.
vmaEndDefragmentationPass
VkResult vmaEndDefragmentationPass(VmaAllocator allocator, VmaDefragmentationContext context)
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:3500
VmaDefragmentationStats::allocationsMoved
uint32_t allocationsMoved
Number of allocations that have been moved to different places.
Definition: vk_mem_alloc.h:3590
VmaAllocationCreateInfo::memoryTypeBits
uint32_t memoryTypeBits
Bitmask containing one bit set for every memory type acceptable for this allocation.
Definition: vk_mem_alloc.h:2762
VmaAllocatorInfo::physicalDevice
VkPhysicalDevice physicalDevice
Handle to Vulkan physical device object.
Definition: vk_mem_alloc.h:2378
VmaDefragmentationStats::deviceMemoryBlocksFreed
uint32_t deviceMemoryBlocksFreed
Number of empty VkDeviceMemory objects that have been released to the system.
Definition: vk_mem_alloc.h:3592
VmaRecordSettings::pFilePath
const char * pFilePath
Path to the file that should be written by the recording.
Definition: vk_mem_alloc.h:2265
VmaStatInfo::allocationSizeMax
VkDeviceSize allocationSizeMax
Definition: vk_mem_alloc.h:2446
VmaAllocationInfo
Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
Definition: vk_mem_alloc.h:3073
VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
@ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT
Enables alternative, buddy allocation algorithm in this pool.
Definition: vk_mem_alloc.h:2883
VmaAllocatorInfo
struct VmaAllocatorInfo VmaAllocatorInfo
Information about existing VmaAllocator object.
VmaBudget::allocationBytes
VkDeviceSize allocationBytes
Sum size of all allocations created in particular heap, in bytes.
Definition: vk_mem_alloc.h:2488
VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
@ VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM
Definition: vk_mem_alloc.h:2731
VmaDefragmentationContext
Represents Opaque object that represents started defragmentation process.
VMA_POOL_CREATE_ALGORITHM_MASK
@ VMA_POOL_CREATE_ALGORITHM_MASK
Definition: vk_mem_alloc.h:2887
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:3529
vmaFreeMemory
void vmaFreeMemory(VmaAllocator allocator, const VmaAllocation allocation)
Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(),...
VmaDefragmentationInfo
Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
Definition: vk_mem_alloc.h:3570
VMA_ALLOCATION_CREATE_DONT_BIND_BIT
@ VMA_ALLOCATION_CREATE_DONT_BIND_BIT
Definition: vk_mem_alloc.h:2692
VmaDefragmentationInfo2
struct VmaDefragmentationInfo2 VmaDefragmentationInfo2
Parameters for defragmentation.