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