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