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