Engine
Frameworkcreatedbymeusableforthecreationofsimplegames.CurrentlysupportsOpenGL(Verysimple)andVulkan.
vk_mem_alloc.h
Go to the documentation of this file.
1 //
2 // Copyright (c) 2017-2018 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 //
23 #ifdef USING_VULKAN
24 
25 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
26 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
738 #include <ThirdParty/Vulkan/Include/vulkan/vulkan.h>
739 
740 VK_DEFINE_HANDLE(VmaAllocator)
741 
742 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
744  VmaAllocator allocator,
745  uint32_t memoryType,
746  VkDeviceMemory memory,
747  VkDeviceSize size);
749 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
750  VmaAllocator allocator,
751  uint32_t memoryType,
752  VkDeviceMemory memory,
753  VkDeviceSize size);
754 
762 typedef struct VmaDeviceMemoryCallbacks {
764  PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
766  PFN_vmaFreeDeviceMemoryFunction pfnFree;
767 } VmaDeviceMemoryCallbacks;
768 
770 typedef enum VmaAllocatorCreateFlagBits {
775  VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
797  VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
798 
799  VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
800 } VmaAllocatorCreateFlagBits;
801 typedef VkFlags VmaAllocatorCreateFlags;
802 
807 typedef struct VmaVulkanFunctions {
808  PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
809  PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
810  PFN_vkAllocateMemory vkAllocateMemory;
811  PFN_vkFreeMemory vkFreeMemory;
812  PFN_vkMapMemory vkMapMemory;
813  PFN_vkUnmapMemory vkUnmapMemory;
814  PFN_vkBindBufferMemory vkBindBufferMemory;
815  PFN_vkBindImageMemory vkBindImageMemory;
816  PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
817  PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
818  PFN_vkCreateBuffer vkCreateBuffer;
819  PFN_vkDestroyBuffer vkDestroyBuffer;
820  PFN_vkCreateImage vkCreateImage;
821  PFN_vkDestroyImage vkDestroyImage;
822  PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
823  PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
824 } VmaVulkanFunctions;
825 
827 typedef struct VmaAllocatorCreateInfo
828 {
830  VmaAllocatorCreateFlags flags;
832 
833  VkPhysicalDevice physicalDevice;
835 
836  VkDevice device;
838 
839  VkDeviceSize preferredLargeHeapBlockSize;
841 
842  const VkAllocationCallbacks* pAllocationCallbacks;
844 
845  const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
859  uint32_t frameInUseCount;
883  const VkDeviceSize* pHeapSizeLimit;
895  const VmaVulkanFunctions* pVulkanFunctions;
896 } VmaAllocatorCreateInfo;
897 
899 VkResult vmaCreateAllocator(
900  const VmaAllocatorCreateInfo* pCreateInfo,
901  VmaAllocator* pAllocator);
902 
904 void vmaDestroyAllocator(
905  VmaAllocator allocator);
906 
911 void vmaGetPhysicalDeviceProperties(
912  VmaAllocator allocator,
913  const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
914 
919 void vmaGetMemoryProperties(
920  VmaAllocator allocator,
921  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
922 
929 void vmaGetMemoryTypeProperties(
930  VmaAllocator allocator,
931  uint32_t memoryTypeIndex,
932  VkMemoryPropertyFlags* pFlags);
933 
942 void vmaSetCurrentFrameIndex(
943  VmaAllocator allocator,
944  uint32_t frameIndex);
945 
948 typedef struct VmaStatInfo
949 {
951  uint32_t blockCount;
953  uint32_t allocationCount;
955  uint32_t unusedRangeCount;
957  VkDeviceSize usedBytes;
959  VkDeviceSize unusedBytes;
960  VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
961  VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
962 } VmaStatInfo;
963 
965 typedef struct VmaStats
966 {
967  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
968  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
969  VmaStatInfo total;
970 } VmaStats;
971 
973 void vmaCalculateStats(
974  VmaAllocator allocator,
975  VmaStats* pStats);
976 
977 #define VMA_STATS_STRING_ENABLED 1
978 
979 #if VMA_STATS_STRING_ENABLED
980 
982 
984 void vmaBuildStatsString(
985  VmaAllocator allocator,
986  char** ppStatsString,
987  VkBool32 detailedMap);
988 
989 void vmaFreeStatsString(
990  VmaAllocator allocator,
991  char* pStatsString);
992 
993 #endif // #if VMA_STATS_STRING_ENABLED
994 
995 VK_DEFINE_HANDLE(VmaPool)
996 
997 typedef enum VmaMemoryUsage
998 {
1002  VMA_MEMORY_USAGE_UNKNOWN = 0,
1019  VMA_MEMORY_USAGE_GPU_ONLY = 1,
1029  VMA_MEMORY_USAGE_CPU_ONLY = 2,
1036  VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
1045  VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
1046  VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
1047 } VmaMemoryUsage;
1048 
1050 typedef enum VmaAllocationCreateFlagBits {
1062  VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
1063 
1073  VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
1086  VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
1099  VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
1106  VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
1112  VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
1113 
1114  VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1115 } VmaAllocationCreateFlagBits;
1116 typedef VkFlags VmaAllocationCreateFlags;
1117 
1118 typedef struct VmaAllocationCreateInfo
1119 {
1121  VmaAllocationCreateFlags flags;
1127  VmaMemoryUsage usage;
1132  VkMemoryPropertyFlags requiredFlags;
1137  VkMemoryPropertyFlags preferredFlags;
1145  uint32_t memoryTypeBits;
1151  VmaPool pool;
1158  void* pUserData;
1159 } VmaAllocationCreateInfo;
1160 
1175 VkResult vmaFindMemoryTypeIndex(
1176  VmaAllocator allocator,
1177  uint32_t memoryTypeBits,
1178  const VmaAllocationCreateInfo* pAllocationCreateInfo,
1179  uint32_t* pMemoryTypeIndex);
1180 
1182 typedef enum VmaPoolCreateFlagBits {
1200  VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
1201 
1202  VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1203 } VmaPoolCreateFlagBits;
1204 typedef VkFlags VmaPoolCreateFlags;
1205 
1208 typedef struct VmaPoolCreateInfo {
1211  uint32_t memoryTypeIndex;
1214  VmaPoolCreateFlags flags;
1219  VkDeviceSize blockSize;
1224  size_t minBlockCount;
1232  size_t maxBlockCount;
1246  uint32_t frameInUseCount;
1247 } VmaPoolCreateInfo;
1248 
1251 typedef struct VmaPoolStats {
1254  VkDeviceSize size;
1257  VkDeviceSize unusedSize;
1260  size_t allocationCount;
1263  size_t unusedRangeCount;
1270  VkDeviceSize unusedRangeSizeMax;
1271 } VmaPoolStats;
1272 
1279 VkResult vmaCreatePool(
1280  VmaAllocator allocator,
1281  const VmaPoolCreateInfo* pCreateInfo,
1282  VmaPool* pPool);
1283 
1286 void vmaDestroyPool(
1287  VmaAllocator allocator,
1288  VmaPool pool);
1289 
1296 void vmaGetPoolStats(
1297  VmaAllocator allocator,
1298  VmaPool pool,
1299  VmaPoolStats* pPoolStats);
1300 
1307 void vmaMakePoolAllocationsLost(
1308  VmaAllocator allocator,
1309  VmaPool pool,
1310  size_t* pLostAllocationCount);
1311 
1312 VK_DEFINE_HANDLE(VmaAllocation)
1313 
1314 
1316 typedef struct VmaAllocationInfo {
1321  uint32_t memoryType;
1330  VkDeviceMemory deviceMemory;
1335  VkDeviceSize offset;
1340  VkDeviceSize size;
1349  void* pMappedData;
1354  void* pUserData;
1355 } VmaAllocationInfo;
1356 
1367 VkResult vmaAllocateMemory(
1368  VmaAllocator allocator,
1369  const VkMemoryRequirements* pVkMemoryRequirements,
1370  const VmaAllocationCreateInfo* pCreateInfo,
1371  VmaAllocation* pAllocation,
1372  VmaAllocationInfo* pAllocationInfo);
1373 
1380 VkResult vmaAllocateMemoryForBuffer(
1381  VmaAllocator allocator,
1382  VkBuffer buffer,
1383  const VmaAllocationCreateInfo* pCreateInfo,
1384  VmaAllocation* pAllocation,
1385  VmaAllocationInfo* pAllocationInfo);
1386 
1388 VkResult vmaAllocateMemoryForImage(
1389  VmaAllocator allocator,
1390  VkImage image,
1391  const VmaAllocationCreateInfo* pCreateInfo,
1392  VmaAllocation* pAllocation,
1393  VmaAllocationInfo* pAllocationInfo);
1394 
1396 void vmaFreeMemory(
1397  VmaAllocator allocator,
1398  VmaAllocation allocation);
1399 
1401 void vmaGetAllocationInfo(
1402  VmaAllocator allocator,
1403  VmaAllocation allocation,
1404  VmaAllocationInfo* pAllocationInfo);
1405 
1419 void vmaSetAllocationUserData(
1420  VmaAllocator allocator,
1421  VmaAllocation allocation,
1422  void* pUserData);
1423 
1434 void vmaCreateLostAllocation(
1435  VmaAllocator allocator,
1436  VmaAllocation* pAllocation);
1437 
1472 VkResult vmaMapMemory(
1473  VmaAllocator allocator,
1474  VmaAllocation allocation,
1475  void** ppData);
1476 
1481 void vmaUnmapMemory(
1482  VmaAllocator allocator,
1483  VmaAllocation allocation);
1484 
1486 typedef struct VmaDefragmentationInfo {
1491  VkDeviceSize maxBytesToMove;
1496  uint32_t maxAllocationsToMove;
1497 } VmaDefragmentationInfo;
1498 
1500 typedef struct VmaDefragmentationStats {
1502  VkDeviceSize bytesMoved;
1504  VkDeviceSize bytesFreed;
1506  uint32_t allocationsMoved;
1508  uint32_t deviceMemoryBlocksFreed;
1509 } VmaDefragmentationStats;
1510 
1587 VkResult vmaDefragment(
1588  VmaAllocator allocator,
1589  VmaAllocation* pAllocations,
1590  size_t allocationCount,
1591  VkBool32* pAllocationsChanged,
1592  const VmaDefragmentationInfo *pDefragmentationInfo,
1593  VmaDefragmentationStats* pDefragmentationStats);
1594 
1621 VkResult vmaCreateBuffer(
1622  VmaAllocator allocator,
1623  const VkBufferCreateInfo* pBufferCreateInfo,
1624  const VmaAllocationCreateInfo* pAllocationCreateInfo,
1625  VkBuffer* pBuffer,
1626  VmaAllocation* pAllocation,
1627  VmaAllocationInfo* pAllocationInfo);
1628 
1640 void vmaDestroyBuffer(
1641  VmaAllocator allocator,
1642  VkBuffer buffer,
1643  VmaAllocation allocation);
1644 
1646 VkResult vmaCreateImage(
1647  VmaAllocator allocator,
1648  const VkImageCreateInfo* pImageCreateInfo,
1649  const VmaAllocationCreateInfo* pAllocationCreateInfo,
1650  VkImage* pImage,
1651  VmaAllocation* pAllocation,
1652  VmaAllocationInfo* pAllocationInfo);
1653 
1665 void vmaDestroyImage(
1666  VmaAllocator allocator,
1667  VkImage image,
1668  VmaAllocation allocation);
1669 
1670 #ifdef __cplusplus
1671 }
1672 #endif
1673 
1674 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
1675 
1676 // For Visual Studio IntelliSense.
1677 #ifdef __INTELLISENSE__
1678 #define VMA_IMPLEMENTATION
1679 #endif
1680 
1681 #ifdef VMA_IMPLEMENTATION
1682 #undef VMA_IMPLEMENTATION
1683 
1684 #include <cstdint>
1685 #include <cstdlib>
1686 #include <cstring>
1687 
1688 /*******************************************************************************
1689 CONFIGURATION SECTION
1690 
1691 Define some of these macros before each #include of this header or change them
1692 here if you need other then default behavior depending on your environment.
1693 */
1694 
1695 /*
1696 Define this macro to 1 to make the library fetch pointers to Vulkan functions
1697 internally, like:
1698 
1699  vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1700 
1701 Define to 0 if you are going to provide you own pointers to Vulkan functions via
1702 VmaAllocatorCreateInfo::pVulkanFunctions.
1703 */
1704 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
1705 #define VMA_STATIC_VULKAN_FUNCTIONS 1
1706 #endif
1707 
1708 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
1709 //#define VMA_USE_STL_CONTAINERS 1
1710 
1711 /* Set this macro to 1 to make the library including and using STL containers:
1712 std::pair, std::vector, std::list, std::unordered_map.
1713 
1714 Set it to 0 or undefined to make the library using its own implementation of
1715 the containers.
1716 */
1717 #if VMA_USE_STL_CONTAINERS
1718  #define VMA_USE_STL_VECTOR 1
1719  #define VMA_USE_STL_UNORDERED_MAP 1
1720  #define VMA_USE_STL_LIST 1
1721 #endif
1722 
1723 #if VMA_USE_STL_VECTOR
1724  #include <vector>
1725 #endif
1726 
1727 #if VMA_USE_STL_UNORDERED_MAP
1728  #include <unordered_map>
1729 #endif
1730 
1731 #if VMA_USE_STL_LIST
1732  #include <list>
1733 #endif
1734 
1735 /*
1736 Following headers are used in this CONFIGURATION section only, so feel free to
1737 remove them if not needed.
1738 */
1739 #include <cassert> // for assert
1740 #include <algorithm> // for min, max
1741 #include <mutex> // for std::mutex
1742 #include <atomic> // for std::atomic
1743 
1744 #if !defined(_WIN32) && !defined(__APPLE__)
1745  #include <malloc.h> // for aligned_alloc()
1746 #endif
1747 
1748 #if defined(__APPLE__)
1749 #include <cstdlib>
1750 void *aligned_alloc(size_t alignment, size_t size)
1751 {
1752  void *pointer;
1753  posix_memalign(&pointer, alignment, size);
1754  return pointer;
1755 }
1756 #endif
1757 
1758 // Normal assert to check for programmer's errors, especially in Debug configuration.
1759 #ifndef VMA_ASSERT
1760  #ifdef _DEBUG
1761  #define VMA_ASSERT(expr) assert(expr)
1762  #else
1763  #define VMA_ASSERT(expr)
1764  #endif
1765 #endif
1766 
1767 // Assert that will be called very often, like inside data structures e.g. operator[].
1768 // Making it non-empty can make program slow.
1769 #ifndef VMA_HEAVY_ASSERT
1770  #ifdef _DEBUG
1771  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
1772  #else
1773  #define VMA_HEAVY_ASSERT(expr)
1774  #endif
1775 #endif
1776 
1777 #ifndef VMA_NULL
1778  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
1779  #define VMA_NULL nullptr
1780 #endif
1781 
1782 #ifndef VMA_ALIGN_OF
1783  #define VMA_ALIGN_OF(type) (__alignof(type))
1784 #endif
1785 
1786 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
1787  #if defined(_WIN32)
1788  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
1789  #else
1790  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
1791  #endif
1792 #endif
1793 
1794 #ifndef VMA_SYSTEM_FREE
1795  #if defined(_WIN32)
1796  #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
1797  #else
1798  #define VMA_SYSTEM_FREE(ptr) free(ptr)
1799  #endif
1800 #endif
1801 
1802 #ifndef VMA_MIN
1803  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
1804 #endif
1805 
1806 #ifndef VMA_MAX
1807  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
1808 #endif
1809 
1810 #ifndef VMA_SWAP
1811  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
1812 #endif
1813 
1814 #ifndef VMA_SORT
1815  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
1816 #endif
1817 
1818 #ifndef VMA_DEBUG_LOG
1819  #define VMA_DEBUG_LOG(format, ...)
1820  /*
1821  #define VMA_DEBUG_LOG(format, ...) do { \
1822  printf(format, __VA_ARGS__); \
1823  printf("\n"); \
1824  } while(false)
1825  */
1826 #endif
1827 
1828 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
1829 #if VMA_STATS_STRING_ENABLED
1830  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
1831  {
1832  snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
1833  }
1834  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
1835  {
1836  snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
1837  }
1838  static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
1839  {
1840  snprintf(outStr, strLen, "%p", ptr);
1841  }
1842 #endif
1843 
1844 #ifndef VMA_MUTEX
1845  class VmaMutex
1846  {
1847  public:
1848  VmaMutex() { }
1849  ~VmaMutex() { }
1850  void Lock() { m_Mutex.lock(); }
1851  void Unlock() { m_Mutex.unlock(); }
1852  private:
1853  std::mutex m_Mutex;
1854  };
1855  #define VMA_MUTEX VmaMutex
1856 #endif
1857 
1858 /*
1859 If providing your own implementation, you need to implement a subset of std::atomic:
1860 
1861 - Constructor(uint32_t desired)
1862 - uint32_t load() const
1863 - void store(uint32_t desired)
1864 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
1865 */
1866 #ifndef VMA_ATOMIC_UINT32
1867  #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
1868 #endif
1869 
1870 #ifndef VMA_BEST_FIT
1871 
1883  #define VMA_BEST_FIT (1)
1884 #endif
1885 
1886 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
1887 
1891  #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
1892 #endif
1893 
1894 #ifndef VMA_DEBUG_ALIGNMENT
1895 
1899  #define VMA_DEBUG_ALIGNMENT (1)
1900 #endif
1901 
1902 #ifndef VMA_DEBUG_MARGIN
1903 
1907  #define VMA_DEBUG_MARGIN (0)
1908 #endif
1909 
1910 #ifndef VMA_DEBUG_GLOBAL_MUTEX
1911 
1915  #define VMA_DEBUG_GLOBAL_MUTEX (0)
1916 #endif
1917 
1918 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
1919 
1923  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
1924 #endif
1925 
1926 #ifndef VMA_SMALL_HEAP_MAX_SIZE
1927  #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
1929 #endif
1930 
1931 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
1932  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
1934 #endif
1935 
1936 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
1937 
1938 /*******************************************************************************
1939 END OF CONFIGURATION
1940 */
1941 
1942 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
1943  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
1944 
1945 // Returns number of bits set to 1 in (v).
1946 static inline uint32_t VmaCountBitsSet(uint32_t v)
1947 {
1948  uint32_t c = v - ((v >> 1) & 0x55555555);
1949  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
1950  c = ((c >> 4) + c) & 0x0F0F0F0F;
1951  c = ((c >> 8) + c) & 0x00FF00FF;
1952  c = ((c >> 16) + c) & 0x0000FFFF;
1953  return c;
1954 }
1955 
1956 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
1957 // Use types like uint32_t, uint64_t as T.
1958 template <typename T>
1959 static inline T VmaAlignUp(T val, T align)
1960 {
1961  return (val + align - 1) / align * align;
1962 }
1963 
1964 // Division with mathematical rounding to nearest number.
1965 template <typename T>
1966 inline T VmaRoundDiv(T x, T y)
1967 {
1968  return (x + (y / (T)2)) / y;
1969 }
1970 
1971 #ifndef VMA_SORT
1972 
1973 template<typename Iterator, typename Compare>
1974 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
1975 {
1976  Iterator centerValue = end; --centerValue;
1977  Iterator insertIndex = beg;
1978  for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
1979  {
1980  if(cmp(*memTypeIndex, *centerValue))
1981  {
1982  if(insertIndex != memTypeIndex)
1983  {
1984  VMA_SWAP(*memTypeIndex, *insertIndex);
1985  }
1986  ++insertIndex;
1987  }
1988  }
1989  if(insertIndex != centerValue)
1990  {
1991  VMA_SWAP(*insertIndex, *centerValue);
1992  }
1993  return insertIndex;
1994 }
1995 
1996 template<typename Iterator, typename Compare>
1997 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
1998 {
1999  if(beg < end)
2000  {
2001  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
2002  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
2003  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
2004  }
2005 }
2006 
2007 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
2008 
2009 #endif // #ifndef VMA_SORT
2010 
2011 /*
2012 Returns true if two memory blocks occupy overlapping pages.
2013 ResourceA must be in less memory offset than ResourceB.
2014 
2015 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
2016 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
2017 */
2018 static inline bool VmaBlocksOnSamePage(
2019  VkDeviceSize resourceAOffset,
2020  VkDeviceSize resourceASize,
2021  VkDeviceSize resourceBOffset,
2022  VkDeviceSize pageSize)
2023 {
2024  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
2025  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
2026  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
2027  VkDeviceSize resourceBStart = resourceBOffset;
2028  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
2029  return resourceAEndPage == resourceBStartPage;
2030 }
2031 
2032 enum VmaSuballocationType
2033 {
2034  VMA_SUBALLOCATION_TYPE_FREE = 0,
2035  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
2036  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
2037  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
2038  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
2039  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
2040  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
2041 };
2042 
2043 /*
2044 Returns true if given suballocation types could conflict and must respect
2045 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
2046 or linear image and another one is optimal image. If type is unknown, behave
2047 conservatively.
2048 */
2049 static inline bool VmaIsBufferImageGranularityConflict(
2050  VmaSuballocationType suballocType1,
2051  VmaSuballocationType suballocType2)
2052 {
2053  if(suballocType1 > suballocType2)
2054  {
2055  VMA_SWAP(suballocType1, suballocType2);
2056  }
2057 
2058  switch(suballocType1)
2059  {
2060  case VMA_SUBALLOCATION_TYPE_FREE:
2061  return false;
2062  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
2063  return true;
2064  case VMA_SUBALLOCATION_TYPE_BUFFER:
2065  return
2066  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2067  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2068  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
2069  return
2070  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2071  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
2072  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2073  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
2074  return
2075  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2076  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
2077  return false;
2078  default:
2079  VMA_ASSERT(0);
2080  return true;
2081  }
2082 }
2083 
2084 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
2085 struct VmaMutexLock
2086 {
2087 public:
2088  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
2089  m_pMutex(useMutex ? &mutex : VMA_NULL)
2090  {
2091  if(m_pMutex)
2092  {
2093  m_pMutex->Lock();
2094  }
2095  }
2096 
2097  ~VmaMutexLock()
2098  {
2099  if(m_pMutex)
2100  {
2101  m_pMutex->Unlock();
2102  }
2103  }
2104 
2105 private:
2106  VMA_MUTEX* m_pMutex;
2107 };
2108 
2109 #if VMA_DEBUG_GLOBAL_MUTEX
2110  static VMA_MUTEX gDebugGlobalMutex;
2111  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
2112 #else
2113  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
2114 #endif
2115 
2116 // Minimum size of a free suballocation to register it in the free suballocation collection.
2117 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
2118 
2119 /*
2120 Performs binary search and returns iterator to first element that is greater or
2121 equal to (key), according to comparison (cmp).
2122 
2123 Cmp should return true if first argument is less than second argument.
2124 
2125 Returned value is the found element, if present in the collection or place where
2126 new element with value (key) should be inserted.
2127 */
2128 template <typename IterT, typename KeyT, typename CmpT>
2129 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)
2130 {
2131  size_t down = 0, up = (end - beg);
2132  while(down < up)
2133  {
2134  const size_t mid = (down + up) / 2;
2135  if(cmp(*(beg+mid), key))
2136  {
2137  down = mid + 1;
2138  }
2139  else
2140  {
2141  up = mid;
2142  }
2143  }
2144  return beg + down;
2145 }
2146 
2148 // Memory allocation
2149 
2150 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
2151 {
2152  if((pAllocationCallbacks != VMA_NULL) &&
2153  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
2154  {
2155  return (*pAllocationCallbacks->pfnAllocation)(
2156  pAllocationCallbacks->pUserData,
2157  size,
2158  alignment,
2159  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
2160  }
2161  else
2162  {
2163  return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
2164  }
2165 }
2166 
2167 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
2168 {
2169  if((pAllocationCallbacks != VMA_NULL) &&
2170  (pAllocationCallbacks->pfnFree != VMA_NULL))
2171  {
2172  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
2173  }
2174  else
2175  {
2176  VMA_SYSTEM_FREE(ptr);
2177  }
2178 }
2179 
2180 template<typename T>
2181 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
2182 {
2183  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
2184 }
2185 
2186 template<typename T>
2187 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
2188 {
2189  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
2190 }
2191 
2192 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
2193 
2194 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
2195 
2196 template<typename T>
2197 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
2198 {
2199  ptr->~T();
2200  VmaFree(pAllocationCallbacks, ptr);
2201 }
2202 
2203 template<typename T>
2204 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
2205 {
2206  if(ptr != VMA_NULL)
2207  {
2208  for(size_t i = count; i--; )
2209  {
2210  ptr[i].~T();
2211  }
2212  VmaFree(pAllocationCallbacks, ptr);
2213  }
2214 }
2215 
2216 // STL-compatible allocator.
2217 template<typename T>
2218 class VmaStlAllocator
2219 {
2220 public:
2221  const VkAllocationCallbacks* const m_pCallbacks;
2222  typedef T value_type;
2223 
2224  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
2225  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
2226 
2227  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
2228  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
2229 
2230  template<typename U>
2231  bool operator==(const VmaStlAllocator<U>& rhs) const
2232  {
2233  return m_pCallbacks == rhs.m_pCallbacks;
2234  }
2235  template<typename U>
2236  bool operator!=(const VmaStlAllocator<U>& rhs) const
2237  {
2238  return m_pCallbacks != rhs.m_pCallbacks;
2239  }
2240 
2241  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
2242 };
2243 
2244 #if VMA_USE_STL_VECTOR
2245 
2246 #define VmaVector std::vector
2247 
2248 template<typename T, typename allocatorT>
2249 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
2250 {
2251  vec.insert(vec.begin() + index, item);
2252 }
2253 
2254 template<typename T, typename allocatorT>
2255 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
2256 {
2257  vec.erase(vec.begin() + index);
2258 }
2259 
2260 #else // #if VMA_USE_STL_VECTOR
2261 
2262 /* Class with interface compatible with subset of std::vector.
2263 T must be POD because constructors and destructors are not called and memcpy is
2264 used for these objects. */
2265 template<typename T, typename AllocatorT>
2266 class VmaVector
2267 {
2268 public:
2269  typedef T value_type;
2270 
2271  VmaVector(const AllocatorT& allocator) :
2272  m_Allocator(allocator),
2273  m_pArray(VMA_NULL),
2274  m_Count(0),
2275  m_Capacity(0)
2276  {
2277  }
2278 
2279  VmaVector(size_t count, const AllocatorT& allocator) :
2280  m_Allocator(allocator),
2281  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
2282  m_Count(count),
2283  m_Capacity(count)
2284  {
2285  }
2286 
2287  VmaVector(const VmaVector<T, AllocatorT>& src) :
2288  m_Allocator(src.m_Allocator),
2289  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
2290  m_Count(src.m_Count),
2291  m_Capacity(src.m_Count)
2292  {
2293  if(m_Count != 0)
2294  {
2295  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
2296  }
2297  }
2298 
2299  ~VmaVector()
2300  {
2301  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2302  }
2303 
2304  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
2305  {
2306  if(&rhs != this)
2307  {
2308  resize(rhs.m_Count);
2309  if(m_Count != 0)
2310  {
2311  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
2312  }
2313  }
2314  return *this;
2315  }
2316 
2317  bool empty() const { return m_Count == 0; }
2318  size_t size() const { return m_Count; }
2319  T* data() { return m_pArray; }
2320  const T* data() const { return m_pArray; }
2321 
2322  T& operator[](size_t index)
2323  {
2324  VMA_HEAVY_ASSERT(index < m_Count);
2325  return m_pArray[index];
2326  }
2327  const T& operator[](size_t index) const
2328  {
2329  VMA_HEAVY_ASSERT(index < m_Count);
2330  return m_pArray[index];
2331  }
2332 
2333  T& front()
2334  {
2335  VMA_HEAVY_ASSERT(m_Count > 0);
2336  return m_pArray[0];
2337  }
2338  const T& front() const
2339  {
2340  VMA_HEAVY_ASSERT(m_Count > 0);
2341  return m_pArray[0];
2342  }
2343  T& back()
2344  {
2345  VMA_HEAVY_ASSERT(m_Count > 0);
2346  return m_pArray[m_Count - 1];
2347  }
2348  const T& back() const
2349  {
2350  VMA_HEAVY_ASSERT(m_Count > 0);
2351  return m_pArray[m_Count - 1];
2352  }
2353 
2354  void reserve(size_t newCapacity, bool freeMemory = false)
2355  {
2356  newCapacity = VMA_MAX(newCapacity, m_Count);
2357 
2358  if((newCapacity < m_Capacity) && !freeMemory)
2359  {
2360  newCapacity = m_Capacity;
2361  }
2362 
2363  if(newCapacity != m_Capacity)
2364  {
2365  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
2366  if(m_Count != 0)
2367  {
2368  memcpy(newArray, m_pArray, m_Count * sizeof(T));
2369  }
2370  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2371  m_Capacity = newCapacity;
2372  m_pArray = newArray;
2373  }
2374  }
2375 
2376  void resize(size_t newCount, bool freeMemory = false)
2377  {
2378  size_t newCapacity = m_Capacity;
2379  if(newCount > m_Capacity)
2380  {
2381  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
2382  }
2383  else if(freeMemory)
2384  {
2385  newCapacity = newCount;
2386  }
2387 
2388  if(newCapacity != m_Capacity)
2389  {
2390  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
2391  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
2392  if(elementsToCopy != 0)
2393  {
2394  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
2395  }
2396  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2397  m_Capacity = newCapacity;
2398  m_pArray = newArray;
2399  }
2400 
2401  m_Count = newCount;
2402  }
2403 
2404  void clear(bool freeMemory = false)
2405  {
2406  resize(0, freeMemory);
2407  }
2408 
2409  void insert(size_t index, const T& src)
2410  {
2411  VMA_HEAVY_ASSERT(index <= m_Count);
2412  const size_t oldCount = size();
2413  resize(oldCount + 1);
2414  if(index < oldCount)
2415  {
2416  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
2417  }
2418  m_pArray[index] = src;
2419  }
2420 
2421  void remove(size_t index)
2422  {
2423  VMA_HEAVY_ASSERT(index < m_Count);
2424  const size_t oldCount = size();
2425  if(index < oldCount - 1)
2426  {
2427  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
2428  }
2429  resize(oldCount - 1);
2430  }
2431 
2432  void push_back(const T& src)
2433  {
2434  const size_t newIndex = size();
2435  resize(newIndex + 1);
2436  m_pArray[newIndex] = src;
2437  }
2438 
2439  void pop_back()
2440  {
2441  VMA_HEAVY_ASSERT(m_Count > 0);
2442  resize(size() - 1);
2443  }
2444 
2445  void push_front(const T& src)
2446  {
2447  insert(0, src);
2448  }
2449 
2450  void pop_front()
2451  {
2452  VMA_HEAVY_ASSERT(m_Count > 0);
2453  remove(0);
2454  }
2455 
2456  typedef T* iterator;
2457 
2458  iterator begin() { return m_pArray; }
2459  iterator end() { return m_pArray + m_Count; }
2460 
2461 private:
2462  AllocatorT m_Allocator;
2463  T* m_pArray;
2464  size_t m_Count;
2465  size_t m_Capacity;
2466 };
2467 
2468 template<typename T, typename allocatorT>
2469 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
2470 {
2471  vec.insert(index, item);
2472 }
2473 
2474 template<typename T, typename allocatorT>
2475 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
2476 {
2477  vec.remove(index);
2478 }
2479 
2480 #endif // #if VMA_USE_STL_VECTOR
2481 
2482 template<typename CmpLess, typename VectorT>
2483 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
2484 {
2485  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
2486  vector.data(),
2487  vector.data() + vector.size(),
2488  value,
2489  CmpLess()) - vector.data();
2490  VmaVectorInsert(vector, indexToInsert, value);
2491  return indexToInsert;
2492 }
2493 
2494 template<typename CmpLess, typename VectorT>
2495 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
2496 {
2497  CmpLess comparator;
2498  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
2499  vector.begin(),
2500  vector.end(),
2501  value,
2502  comparator);
2503  if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
2504  {
2505  size_t indexToRemove = it - vector.begin();
2506  VmaVectorRemove(vector, indexToRemove);
2507  return true;
2508  }
2509  return false;
2510 }
2511 
2512 template<typename CmpLess, typename VectorT>
2513 size_t VmaVectorFindSorted(const VectorT& vector, const typename VectorT::value_type& value)
2514 {
2515  CmpLess comparator;
2516  typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
2517  vector.data(),
2518  vector.data() + vector.size(),
2519  value,
2520  comparator);
2521  if(it != vector.size() && !comparator(*it, value) && !comparator(value, *it))
2522  {
2523  return it - vector.begin();
2524  }
2525  else
2526  {
2527  return vector.size();
2528  }
2529 }
2530 
2532 // class VmaPoolAllocator
2533 
2534 /*
2535 Allocator for objects of type T using a list of arrays (pools) to speed up
2536 allocation. Number of elements that can be allocated is not bounded because
2537 allocator can create multiple blocks.
2538 */
2539 template<typename T>
2540 class VmaPoolAllocator
2541 {
2542 public:
2543  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
2544  ~VmaPoolAllocator();
2545  void Clear();
2546  T* Alloc();
2547  void Free(T* ptr);
2548 
2549 private:
2550  union Item
2551  {
2552  uint32_t NextFreeIndex;
2553  T Value;
2554  };
2555 
2556  struct ItemBlock
2557  {
2558  Item* pItems;
2559  uint32_t FirstFreeIndex;
2560  };
2561 
2562  const VkAllocationCallbacks* m_pAllocationCallbacks;
2563  size_t m_ItemsPerBlock;
2564  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
2565 
2566  ItemBlock& CreateNewBlock();
2567 };
2568 
2569 template<typename T>
2570 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
2571  m_pAllocationCallbacks(pAllocationCallbacks),
2572  m_ItemsPerBlock(itemsPerBlock),
2573  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
2574 {
2575  VMA_ASSERT(itemsPerBlock > 0);
2576 }
2577 
2578 template<typename T>
2579 VmaPoolAllocator<T>::~VmaPoolAllocator()
2580 {
2581  Clear();
2582 }
2583 
2584 template<typename T>
2585 void VmaPoolAllocator<T>::Clear()
2586 {
2587  for(size_t i = m_ItemBlocks.size(); i--; )
2588  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
2589  m_ItemBlocks.clear();
2590 }
2591 
2592 template<typename T>
2593 T* VmaPoolAllocator<T>::Alloc()
2594 {
2595  for(size_t i = m_ItemBlocks.size(); i--; )
2596  {
2597  ItemBlock& block = m_ItemBlocks[i];
2598  // This block has some free items: Use first one.
2599  if(block.FirstFreeIndex != UINT32_MAX)
2600  {
2601  Item* const pItem = &block.pItems[block.FirstFreeIndex];
2602  block.FirstFreeIndex = pItem->NextFreeIndex;
2603  return &pItem->Value;
2604  }
2605  }
2606 
2607  // No block has free item: Create new one and use it.
2608  ItemBlock& newBlock = CreateNewBlock();
2609  Item* const pItem = &newBlock.pItems[0];
2610  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
2611  return &pItem->Value;
2612 }
2613 
2614 template<typename T>
2615 void VmaPoolAllocator<T>::Free(T* ptr)
2616 {
2617  // Search all memory blocks to find ptr.
2618  for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
2619  {
2620  ItemBlock& block = m_ItemBlocks[i];
2621 
2622  // Casting to union.
2623  Item* pItemPtr;
2624  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
2625 
2626  // Check if pItemPtr is in address range of this block.
2627  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
2628  {
2629  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
2630  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
2631  block.FirstFreeIndex = index;
2632  return;
2633  }
2634  }
2635  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
2636 }
2637 
2638 template<typename T>
2639 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
2640 {
2641  ItemBlock newBlock = {
2642  vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
2643 
2644  m_ItemBlocks.push_back(newBlock);
2645 
2646  // Setup singly-linked list of all free items in this block.
2647  for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
2648  newBlock.pItems[i].NextFreeIndex = i + 1;
2649  newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
2650  return m_ItemBlocks.back();
2651 }
2652 
2654 // class VmaRawList, VmaList
2655 
2656 #if VMA_USE_STL_LIST
2657 
2658 #define VmaList std::list
2659 
2660 #else // #if VMA_USE_STL_LIST
2661 
2662 template<typename T>
2663 struct VmaListItem
2664 {
2665  VmaListItem* pPrev;
2666  VmaListItem* pNext;
2667  T Value;
2668 };
2669 
2670 // Doubly linked list.
2671 template<typename T>
2672 class VmaRawList
2673 {
2674 public:
2675  typedef VmaListItem<T> ItemType;
2676 
2677  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
2678  ~VmaRawList();
2679  void Clear();
2680 
2681  size_t GetCount() const { return m_Count; }
2682  bool IsEmpty() const { return m_Count == 0; }
2683 
2684  ItemType* Front() { return m_pFront; }
2685  const ItemType* Front() const { return m_pFront; }
2686  ItemType* Back() { return m_pBack; }
2687  const ItemType* Back() const { return m_pBack; }
2688 
2689  ItemType* PushBack();
2690  ItemType* PushFront();
2691  ItemType* PushBack(const T& value);
2692  ItemType* PushFront(const T& value);
2693  void PopBack();
2694  void PopFront();
2695 
2696  // Item can be null - it means PushBack.
2697  ItemType* InsertBefore(ItemType* pItem);
2698  // Item can be null - it means PushFront.
2699  ItemType* InsertAfter(ItemType* pItem);
2700 
2701  ItemType* InsertBefore(ItemType* pItem, const T& value);
2702  ItemType* InsertAfter(ItemType* pItem, const T& value);
2703 
2704  void Remove(ItemType* pItem);
2705 
2706 private:
2707  const VkAllocationCallbacks* const m_pAllocationCallbacks;
2708  VmaPoolAllocator<ItemType> m_ItemAllocator;
2709  ItemType* m_pFront;
2710  ItemType* m_pBack;
2711  size_t m_Count;
2712 
2713  // Declared not defined, to block copy constructor and assignment operator.
2714  VmaRawList(const VmaRawList<T>& src);
2715  VmaRawList<T>& operator=(const VmaRawList<T>& rhs);
2716 };
2717 
2718 template<typename T>
2719 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
2720  m_pAllocationCallbacks(pAllocationCallbacks),
2721  m_ItemAllocator(pAllocationCallbacks, 128),
2722  m_pFront(VMA_NULL),
2723  m_pBack(VMA_NULL),
2724  m_Count(0)
2725 {
2726 }
2727 
2728 template<typename T>
2729 VmaRawList<T>::~VmaRawList()
2730 {
2731  // Intentionally not calling Clear, because that would be unnecessary
2732  // computations to return all items to m_ItemAllocator as free.
2733 }
2734 
2735 template<typename T>
2736 void VmaRawList<T>::Clear()
2737 {
2738  if(IsEmpty() == false)
2739  {
2740  ItemType* pItem = m_pBack;
2741  while(pItem != VMA_NULL)
2742  {
2743  ItemType* const pPrevItem = pItem->pPrev;
2744  m_ItemAllocator.Free(pItem);
2745  pItem = pPrevItem;
2746  }
2747  m_pFront = VMA_NULL;
2748  m_pBack = VMA_NULL;
2749  m_Count = 0;
2750  }
2751 }
2752 
2753 template<typename T>
2754 VmaListItem<T>* VmaRawList<T>::PushBack()
2755 {
2756  ItemType* const pNewItem = m_ItemAllocator.Alloc();
2757  pNewItem->pNext = VMA_NULL;
2758  if(IsEmpty())
2759  {
2760  pNewItem->pPrev = VMA_NULL;
2761  m_pFront = pNewItem;
2762  m_pBack = pNewItem;
2763  m_Count = 1;
2764  }
2765  else
2766  {
2767  pNewItem->pPrev = m_pBack;
2768  m_pBack->pNext = pNewItem;
2769  m_pBack = pNewItem;
2770  ++m_Count;
2771  }
2772  return pNewItem;
2773 }
2774 
2775 template<typename T>
2776 VmaListItem<T>* VmaRawList<T>::PushFront()
2777 {
2778  ItemType* const pNewItem = m_ItemAllocator.Alloc();
2779  pNewItem->pPrev = VMA_NULL;
2780  if(IsEmpty())
2781  {
2782  pNewItem->pNext = VMA_NULL;
2783  m_pFront = pNewItem;
2784  m_pBack = pNewItem;
2785  m_Count = 1;
2786  }
2787  else
2788  {
2789  pNewItem->pNext = m_pFront;
2790  m_pFront->pPrev = pNewItem;
2791  m_pFront = pNewItem;
2792  ++m_Count;
2793  }
2794  return pNewItem;
2795 }
2796 
2797 template<typename T>
2798 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
2799 {
2800  ItemType* const pNewItem = PushBack();
2801  pNewItem->Value = value;
2802  return pNewItem;
2803 }
2804 
2805 template<typename T>
2806 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
2807 {
2808  ItemType* const pNewItem = PushFront();
2809  pNewItem->Value = value;
2810  return pNewItem;
2811 }
2812 
2813 template<typename T>
2814 void VmaRawList<T>::PopBack()
2815 {
2816  VMA_HEAVY_ASSERT(m_Count > 0);
2817  ItemType* const pBackItem = m_pBack;
2818  ItemType* const pPrevItem = pBackItem->pPrev;
2819  if(pPrevItem != VMA_NULL)
2820  {
2821  pPrevItem->pNext = VMA_NULL;
2822  }
2823  m_pBack = pPrevItem;
2824  m_ItemAllocator.Free(pBackItem);
2825  --m_Count;
2826 }
2827 
2828 template<typename T>
2829 void VmaRawList<T>::PopFront()
2830 {
2831  VMA_HEAVY_ASSERT(m_Count > 0);
2832  ItemType* const pFrontItem = m_pFront;
2833  ItemType* const pNextItem = pFrontItem->pNext;
2834  if(pNextItem != VMA_NULL)
2835  {
2836  pNextItem->pPrev = VMA_NULL;
2837  }
2838  m_pFront = pNextItem;
2839  m_ItemAllocator.Free(pFrontItem);
2840  --m_Count;
2841 }
2842 
2843 template<typename T>
2844 void VmaRawList<T>::Remove(ItemType* pItem)
2845 {
2846  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
2847  VMA_HEAVY_ASSERT(m_Count > 0);
2848 
2849  if(pItem->pPrev != VMA_NULL)
2850  {
2851  pItem->pPrev->pNext = pItem->pNext;
2852  }
2853  else
2854  {
2855  VMA_HEAVY_ASSERT(m_pFront == pItem);
2856  m_pFront = pItem->pNext;
2857  }
2858 
2859  if(pItem->pNext != VMA_NULL)
2860  {
2861  pItem->pNext->pPrev = pItem->pPrev;
2862  }
2863  else
2864  {
2865  VMA_HEAVY_ASSERT(m_pBack == pItem);
2866  m_pBack = pItem->pPrev;
2867  }
2868 
2869  m_ItemAllocator.Free(pItem);
2870  --m_Count;
2871 }
2872 
2873 template<typename T>
2874 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
2875 {
2876  if(pItem != VMA_NULL)
2877  {
2878  ItemType* const prevItem = pItem->pPrev;
2879  ItemType* const newItem = m_ItemAllocator.Alloc();
2880  newItem->pPrev = prevItem;
2881  newItem->pNext = pItem;
2882  pItem->pPrev = newItem;
2883  if(prevItem != VMA_NULL)
2884  {
2885  prevItem->pNext = newItem;
2886  }
2887  else
2888  {
2889  VMA_HEAVY_ASSERT(m_pFront == pItem);
2890  m_pFront = newItem;
2891  }
2892  ++m_Count;
2893  return newItem;
2894  }
2895  else
2896  return PushBack();
2897 }
2898 
2899 template<typename T>
2900 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
2901 {
2902  if(pItem != VMA_NULL)
2903  {
2904  ItemType* const nextItem = pItem->pNext;
2905  ItemType* const newItem = m_ItemAllocator.Alloc();
2906  newItem->pNext = nextItem;
2907  newItem->pPrev = pItem;
2908  pItem->pNext = newItem;
2909  if(nextItem != VMA_NULL)
2910  {
2911  nextItem->pPrev = newItem;
2912  }
2913  else
2914  {
2915  VMA_HEAVY_ASSERT(m_pBack == pItem);
2916  m_pBack = newItem;
2917  }
2918  ++m_Count;
2919  return newItem;
2920  }
2921  else
2922  return PushFront();
2923 }
2924 
2925 template<typename T>
2926 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
2927 {
2928  ItemType* const newItem = InsertBefore(pItem);
2929  newItem->Value = value;
2930  return newItem;
2931 }
2932 
2933 template<typename T>
2934 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
2935 {
2936  ItemType* const newItem = InsertAfter(pItem);
2937  newItem->Value = value;
2938  return newItem;
2939 }
2940 
2941 template<typename T, typename AllocatorT>
2942 class VmaList
2943 {
2944 public:
2945  class iterator
2946  {
2947  public:
2948  iterator() :
2949  m_pList(VMA_NULL),
2950  m_pItem(VMA_NULL)
2951  {
2952  }
2953 
2954  T& operator*() const
2955  {
2956  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
2957  return m_pItem->Value;
2958  }
2959  T* operator->() const
2960  {
2961  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
2962  return &m_pItem->Value;
2963  }
2964 
2965  iterator& operator++()
2966  {
2967  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
2968  m_pItem = m_pItem->pNext;
2969  return *this;
2970  }
2971  iterator& operator--()
2972  {
2973  if(m_pItem != VMA_NULL)
2974  {
2975  m_pItem = m_pItem->pPrev;
2976  }
2977  else
2978  {
2979  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
2980  m_pItem = m_pList->Back();
2981  }
2982  return *this;
2983  }
2984 
2985  iterator operator++(int)
2986  {
2987  iterator result = *this;
2988  ++*this;
2989  return result;
2990  }
2991  iterator operator--(int)
2992  {
2993  iterator result = *this;
2994  --*this;
2995  return result;
2996  }
2997 
2998  bool operator==(const iterator& rhs) const
2999  {
3000  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3001  return m_pItem == rhs.m_pItem;
3002  }
3003  bool operator!=(const iterator& rhs) const
3004  {
3005  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3006  return m_pItem != rhs.m_pItem;
3007  }
3008 
3009  private:
3010  VmaRawList<T>* m_pList;
3011  VmaListItem<T>* m_pItem;
3012 
3013  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
3014  m_pList(pList),
3015  m_pItem(pItem)
3016  {
3017  }
3018 
3019  friend class VmaList<T, AllocatorT>;
3020  };
3021 
3022  class const_iterator
3023  {
3024  public:
3025  const_iterator() :
3026  m_pList(VMA_NULL),
3027  m_pItem(VMA_NULL)
3028  {
3029  }
3030 
3031  const_iterator(const iterator& src) :
3032  m_pList(src.m_pList),
3033  m_pItem(src.m_pItem)
3034  {
3035  }
3036 
3037  const T& operator*() const
3038  {
3039  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3040  return m_pItem->Value;
3041  }
3042  const T* operator->() const
3043  {
3044  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3045  return &m_pItem->Value;
3046  }
3047 
3048  const_iterator& operator++()
3049  {
3050  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3051  m_pItem = m_pItem->pNext;
3052  return *this;
3053  }
3054  const_iterator& operator--()
3055  {
3056  if(m_pItem != VMA_NULL)
3057  {
3058  m_pItem = m_pItem->pPrev;
3059  }
3060  else
3061  {
3062  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
3063  m_pItem = m_pList->Back();
3064  }
3065  return *this;
3066  }
3067 
3068  const_iterator operator++(int)
3069  {
3070  const_iterator result = *this;
3071  ++*this;
3072  return result;
3073  }
3074  const_iterator operator--(int)
3075  {
3076  const_iterator result = *this;
3077  --*this;
3078  return result;
3079  }
3080 
3081  bool operator==(const const_iterator& rhs) const
3082  {
3083  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3084  return m_pItem == rhs.m_pItem;
3085  }
3086  bool operator!=(const const_iterator& rhs) const
3087  {
3088  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3089  return m_pItem != rhs.m_pItem;
3090  }
3091 
3092  private:
3093  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
3094  m_pList(pList),
3095  m_pItem(pItem)
3096  {
3097  }
3098 
3099  const VmaRawList<T>* m_pList;
3100  const VmaListItem<T>* m_pItem;
3101 
3102  friend class VmaList<T, AllocatorT>;
3103  };
3104 
3105  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
3106 
3107  bool empty() const { return m_RawList.IsEmpty(); }
3108  size_t size() const { return m_RawList.GetCount(); }
3109 
3110  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
3111  iterator end() { return iterator(&m_RawList, VMA_NULL); }
3112 
3113  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
3114  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
3115 
3116  void clear() { m_RawList.Clear(); }
3117  void push_back(const T& value) { m_RawList.PushBack(value); }
3118  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
3119  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
3120 
3121 private:
3122  VmaRawList<T> m_RawList;
3123 };
3124 
3125 #endif // #if VMA_USE_STL_LIST
3126 
3128 // class VmaMap
3129 
3130 // Unused in this version.
3131 #if 0
3132 
3133 #if VMA_USE_STL_UNORDERED_MAP
3134 
3135 #define VmaPair std::pair
3136 
3137 #define VMA_MAP_TYPE(KeyT, ValueT) \
3138  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
3139 
3140 #else // #if VMA_USE_STL_UNORDERED_MAP
3141 
3142 template<typename T1, typename T2>
3143 struct VmaPair
3144 {
3145  T1 first;
3146  T2 second;
3147 
3148  VmaPair() : first(), second() { }
3149  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
3150 };
3151 
3152 /* Class compatible with subset of interface of std::unordered_map.
3153 KeyT, ValueT must be POD because they will be stored in VmaVector.
3154 */
3155 template<typename KeyT, typename ValueT>
3156 class VmaMap
3157 {
3158 public:
3159  typedef VmaPair<KeyT, ValueT> PairType;
3160  typedef PairType* iterator;
3161 
3162  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
3163 
3164  iterator begin() { return m_Vector.begin(); }
3165  iterator end() { return m_Vector.end(); }
3166 
3167  void insert(const PairType& pair);
3168  iterator find(const KeyT& key);
3169  void erase(iterator it);
3170 
3171 private:
3172  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
3173 };
3174 
3175 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
3176 
3177 template<typename FirstT, typename SecondT>
3178 struct VmaPairFirstLess
3179 {
3180  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
3181  {
3182  return lhs.first < rhs.first;
3183  }
3184  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
3185  {
3186  return lhs.first < rhsFirst;
3187  }
3188 };
3189 
3190 template<typename KeyT, typename ValueT>
3191 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
3192 {
3193  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3194  m_Vector.data(),
3195  m_Vector.data() + m_Vector.size(),
3196  pair,
3197  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
3198  VmaVectorInsert(m_Vector, indexToInsert, pair);
3199 }
3200 
3201 template<typename KeyT, typename ValueT>
3202 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
3203 {
3204  PairType* it = VmaBinaryFindFirstNotLess(
3205  m_Vector.data(),
3206  m_Vector.data() + m_Vector.size(),
3207  key,
3208  VmaPairFirstLess<KeyT, ValueT>());
3209  if((it != m_Vector.end()) && (it->first == key))
3210  {
3211  return it;
3212  }
3213  else
3214  {
3215  return m_Vector.end();
3216  }
3217 }
3218 
3219 template<typename KeyT, typename ValueT>
3220 void VmaMap<KeyT, ValueT>::erase(iterator it)
3221 {
3222  VmaVectorRemove(m_Vector, it - m_Vector.begin());
3223 }
3224 
3225 #endif // #if VMA_USE_STL_UNORDERED_MAP
3226 
3227 #endif // #if 0
3228 
3230 
3231 class VmaDeviceMemoryBlock;
3232 
3233 struct VmaAllocation_T
3234 {
3235 private:
3236  static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
3237 
3238  enum FLAGS
3239  {
3240  FLAG_USER_DATA_STRING = 0x01,
3241  };
3242 
3243 public:
3244  enum ALLOCATION_TYPE
3245  {
3246  ALLOCATION_TYPE_NONE,
3247  ALLOCATION_TYPE_BLOCK,
3248  ALLOCATION_TYPE_DEDICATED,
3249  };
3250 
3251  VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
3252  m_Alignment(1),
3253  m_Size(0),
3254  m_pUserData(VMA_NULL),
3255  m_LastUseFrameIndex(currentFrameIndex),
3256  m_Type((uint8_t)ALLOCATION_TYPE_NONE),
3257  m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
3258  m_MapCount(0),
3259  m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
3260  {
3261  }
3262 
3263  ~VmaAllocation_T()
3264  {
3265  VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
3266 
3267  // Check if owned string was freed.
3268  VMA_ASSERT(m_pUserData == VMA_NULL);
3269  }
3270 
3271  void InitBlockAllocation(
3272  VmaPool hPool,
3273  VmaDeviceMemoryBlock* block,
3274  VkDeviceSize offset,
3275  VkDeviceSize alignment,
3276  VkDeviceSize size,
3277  VmaSuballocationType suballocationType,
3278  bool mapped,
3279  bool canBecomeLost)
3280  {
3281  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3282  VMA_ASSERT(block != VMA_NULL);
3283  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3284  m_Alignment = alignment;
3285  m_Size = size;
3286  m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3287  m_SuballocationType = (uint8_t)suballocationType;
3288  m_BlockAllocation.m_hPool = hPool;
3289  m_BlockAllocation.m_Block = block;
3290  m_BlockAllocation.m_Offset = offset;
3291  m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
3292  }
3293 
3294  void InitLost()
3295  {
3296  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3297  VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
3298  m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3299  m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
3300  m_BlockAllocation.m_Block = VMA_NULL;
3301  m_BlockAllocation.m_Offset = 0;
3302  m_BlockAllocation.m_CanBecomeLost = true;
3303  }
3304 
3305  void ChangeBlockAllocation(
3306  VmaAllocator hAllocator,
3307  VmaDeviceMemoryBlock* block,
3308  VkDeviceSize offset);
3309 
3310  // pMappedData not null means allocation is created with MAPPED flag.
3311  void InitDedicatedAllocation(
3312  uint32_t memoryTypeIndex,
3313  VkDeviceMemory hMemory,
3314  VmaSuballocationType suballocationType,
3315  void* pMappedData,
3316  VkDeviceSize size)
3317  {
3318  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3319  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
3320  m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
3321  m_Alignment = 0;
3322  m_Size = size;
3323  m_SuballocationType = (uint8_t)suballocationType;
3324  m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3325  m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
3326  m_DedicatedAllocation.m_hMemory = hMemory;
3327  m_DedicatedAllocation.m_pMappedData = pMappedData;
3328  }
3329 
3330  ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
3331  VkDeviceSize GetAlignment() const { return m_Alignment; }
3332  VkDeviceSize GetSize() const { return m_Size; }
3333  bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
3334  void* GetUserData() const { return m_pUserData; }
3335  void SetUserData(VmaAllocator hAllocator, void* pUserData);
3336  VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
3337 
3338  VmaDeviceMemoryBlock* GetBlock() const
3339  {
3340  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
3341  return m_BlockAllocation.m_Block;
3342  }
3343  VkDeviceSize GetOffset() const;
3344  VkDeviceMemory GetMemory() const;
3345  uint32_t GetMemoryTypeIndex() const;
3346  bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
3347  void* GetMappedData() const;
3348  bool CanBecomeLost() const;
3349  VmaPool GetPool() const;
3350 
3351  uint32_t GetLastUseFrameIndex() const
3352  {
3353  return m_LastUseFrameIndex.load();
3354  }
3355  bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
3356  {
3357  return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
3358  }
3359  /*
3360  - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
3361  makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
3362  - Else, returns false.
3363 
3364  If hAllocation is already lost, assert - you should not call it then.
3365  If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
3366  */
3367  bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
3368 
3369  void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
3370  {
3371  VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
3372  outInfo.blockCount = 1;
3373  outInfo.allocationCount = 1;
3374  outInfo.unusedRangeCount = 0;
3375  outInfo.usedBytes = m_Size;
3376  outInfo.unusedBytes = 0;
3377  outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
3378  outInfo.unusedRangeSizeMin = UINT64_MAX;
3379  outInfo.unusedRangeSizeMax = 0;
3380  }
3381 
3382  void BlockAllocMap();
3383  void BlockAllocUnmap();
3384  VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
3385  void DedicatedAllocUnmap(VmaAllocator hAllocator);
3386 
3387 private:
3388  VkDeviceSize m_Alignment;
3389  VkDeviceSize m_Size;
3390  void* m_pUserData;
3391  VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
3392  uint8_t m_Type; // ALLOCATION_TYPE
3393  uint8_t m_SuballocationType; // VmaSuballocationType
3394  // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
3395  // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
3396  uint8_t m_MapCount;
3397  uint8_t m_Flags; // enum FLAGS
3398 
3399  // Allocation out of VmaDeviceMemoryBlock.
3400  struct BlockAllocation
3401  {
3402  VmaPool m_hPool; // Null if belongs to general memory.
3403  VmaDeviceMemoryBlock* m_Block;
3404  VkDeviceSize m_Offset;
3405  bool m_CanBecomeLost;
3406  };
3407 
3408  // Allocation for an object that has its own private VkDeviceMemory.
3409  struct DedicatedAllocation
3410  {
3411  uint32_t m_MemoryTypeIndex;
3412  VkDeviceMemory m_hMemory;
3413  void* m_pMappedData; // Not null means memory is mapped.
3414  };
3415 
3416  union
3417  {
3418  // Allocation out of VmaDeviceMemoryBlock.
3419  BlockAllocation m_BlockAllocation;
3420  // Allocation for an object that has its own private VkDeviceMemory.
3421  DedicatedAllocation m_DedicatedAllocation;
3422  };
3423 
3424  void FreeUserDataString(VmaAllocator hAllocator);
3425 };
3426 
3427 /*
3428 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
3429 allocated memory block or free.
3430 */
3431 struct VmaSuballocation
3432 {
3433  VkDeviceSize offset;
3434  VkDeviceSize size;
3435  VmaAllocation hAllocation;
3436  VmaSuballocationType type;
3437 };
3438 
3439 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
3440 
3441 // Cost of one additional allocation lost, as equivalent in bytes.
3442 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
3443 
3444 /*
3445 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
3446 
3447 If canMakeOtherLost was false:
3448 - item points to a FREE suballocation.
3449 - itemsToMakeLostCount is 0.
3450 
3451 If canMakeOtherLost was true:
3452 - item points to first of sequence of suballocations, which are either FREE,
3453  or point to VmaAllocations that can become lost.
3454 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
3455  the requested allocation to succeed.
3456 */
3457 struct VmaAllocationRequest
3458 {
3459  VkDeviceSize offset;
3460  VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
3461  VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
3462  VmaSuballocationList::iterator item;
3463  size_t itemsToMakeLostCount;
3464 
3465  VkDeviceSize CalcCost() const
3466  {
3467  return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
3468  }
3469 };
3470 
3471 /*
3472 Data structure used for bookkeeping of allocations and unused ranges of memory
3473 in a single VkDeviceMemory block.
3474 */
3475 class VmaBlockMetadata
3476 {
3477 public:
3478  VmaBlockMetadata(VmaAllocator hAllocator);
3479  ~VmaBlockMetadata();
3480  void Init(VkDeviceSize size);
3481 
3482  // Validates all data structures inside this object. If not valid, returns false.
3483  bool Validate() const;
3484  VkDeviceSize GetSize() const { return m_Size; }
3485  size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
3486  VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
3487  VkDeviceSize GetUnusedRangeSizeMax() const;
3488  // Returns true if this block is empty - contains only single free suballocation.
3489  bool IsEmpty() const;
3490 
3491  void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
3492  void AddPoolStats(VmaPoolStats& inoutStats) const;
3493 
3494 #if VMA_STATS_STRING_ENABLED
3495  void PrintDetailedMap(class VmaJsonWriter& json) const;
3496 #endif
3497 
3498  // Creates trivial request for case when block is empty.
3499  void CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest);
3500 
3501  // Tries to find a place for suballocation with given parameters inside this block.
3502  // If succeeded, fills pAllocationRequest and returns true.
3503  // If failed, returns false.
3504  bool CreateAllocationRequest(
3505  uint32_t currentFrameIndex,
3506  uint32_t frameInUseCount,
3507  VkDeviceSize bufferImageGranularity,
3508  VkDeviceSize allocSize,
3509  VkDeviceSize allocAlignment,
3510  VmaSuballocationType allocType,
3511  bool canMakeOtherLost,
3512  VmaAllocationRequest* pAllocationRequest);
3513 
3514  bool MakeRequestedAllocationsLost(
3515  uint32_t currentFrameIndex,
3516  uint32_t frameInUseCount,
3517  VmaAllocationRequest* pAllocationRequest);
3518 
3519  uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
3520 
3521  // Makes actual allocation based on request. Request must already be checked and valid.
3522  void Alloc(
3523  const VmaAllocationRequest& request,
3524  VmaSuballocationType type,
3525  VkDeviceSize allocSize,
3526  VmaAllocation hAllocation);
3527 
3528  // Frees suballocation assigned to given memory region.
3529  void Free(const VmaAllocation allocation);
3530  void FreeAtOffset(VkDeviceSize offset);
3531 
3532 private:
3533  VkDeviceSize m_Size;
3534  uint32_t m_FreeCount;
3535  VkDeviceSize m_SumFreeSize;
3536  VmaSuballocationList m_Suballocations;
3537  // Suballocations that are free and have size greater than certain threshold.
3538  // Sorted by size, ascending.
3539  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
3540 
3541  bool ValidateFreeSuballocationList() const;
3542 
3543  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
3544  // If yes, fills pOffset and returns true. If no, returns false.
3545  bool CheckAllocation(
3546  uint32_t currentFrameIndex,
3547  uint32_t frameInUseCount,
3548  VkDeviceSize bufferImageGranularity,
3549  VkDeviceSize allocSize,
3550  VkDeviceSize allocAlignment,
3551  VmaSuballocationType allocType,
3552  VmaSuballocationList::const_iterator suballocItem,
3553  bool canMakeOtherLost,
3554  VkDeviceSize* pOffset,
3555  size_t* itemsToMakeLostCount,
3556  VkDeviceSize* pSumFreeSize,
3557  VkDeviceSize* pSumItemSize) const;
3558  // Given free suballocation, it merges it with following one, which must also be free.
3559  void MergeFreeWithNext(VmaSuballocationList::iterator item);
3560  // Releases given suballocation, making it free.
3561  // Merges it with adjacent free suballocations if applicable.
3562  // Returns iterator to new free suballocation at this place.
3563  VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
3564  // Given free suballocation, it inserts it into sorted list of
3565  // m_FreeSuballocationsBySize if it's suitable.
3566  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
3567  // Given free suballocation, it removes it from sorted list of
3568  // m_FreeSuballocationsBySize if it's suitable.
3569  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
3570 };
3571 
3572 // Helper class that represents mapped memory. Synchronized internally.
3573 class VmaDeviceMemoryMapping
3574 {
3575 public:
3576  VmaDeviceMemoryMapping();
3577  ~VmaDeviceMemoryMapping();
3578 
3579  void* GetMappedData() const { return m_pMappedData; }
3580 
3581  // ppData can be null.
3582  VkResult Map(VmaAllocator hAllocator, VkDeviceMemory hMemory, uint32_t count, void **ppData);
3583  void Unmap(VmaAllocator hAllocator, VkDeviceMemory hMemory, uint32_t count);
3584 
3585 private:
3586  VMA_MUTEX m_Mutex;
3587  uint32_t m_MapCount;
3588  void* m_pMappedData;
3589 };
3590 
3591 /*
3592 Represents a single block of device memory (`VkDeviceMemory`) with all the
3593 data about its regions (aka suballocations, `VmaAllocation`), assigned and free.
3594 
3595 Thread-safety: This class must be externally synchronized.
3596 */
3597 class VmaDeviceMemoryBlock
3598 {
3599 public:
3600  uint32_t m_MemoryTypeIndex;
3601  VkDeviceMemory m_hMemory;
3602  VmaDeviceMemoryMapping m_Mapping;
3603  VmaBlockMetadata m_Metadata;
3604 
3605  VmaDeviceMemoryBlock(VmaAllocator hAllocator);
3606 
3607  ~VmaDeviceMemoryBlock()
3608  {
3609  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
3610  }
3611 
3612  // Always call after construction.
3613  void Init(
3614  uint32_t newMemoryTypeIndex,
3615  VkDeviceMemory newMemory,
3616  VkDeviceSize newSize);
3617  // Always call before destruction.
3618  void Destroy(VmaAllocator allocator);
3619 
3620  // Validates all data structures inside this object. If not valid, returns false.
3621  bool Validate() const;
3622 
3623  // ppData can be null.
3624  VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
3625  void Unmap(VmaAllocator hAllocator, uint32_t count);
3626 };
3627 
3628 struct VmaPointerLess
3629 {
3630  bool operator()(const void* lhs, const void* rhs) const
3631  {
3632  return lhs < rhs;
3633  }
3634 };
3635 
3636 class VmaDefragmentator;
3637 
3638 /*
3639 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
3640 Vulkan memory type.
3641 
3642 Synchronized internally with a mutex.
3643 */
3644 struct VmaBlockVector
3645 {
3646  VmaBlockVector(
3647  VmaAllocator hAllocator,
3648  uint32_t memoryTypeIndex,
3649  VkDeviceSize preferredBlockSize,
3650  size_t minBlockCount,
3651  size_t maxBlockCount,
3652  VkDeviceSize bufferImageGranularity,
3653  uint32_t frameInUseCount,
3654  bool isCustomPool);
3655  ~VmaBlockVector();
3656 
3657  VkResult CreateMinBlocks();
3658 
3659  uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
3660  VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
3661  VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
3662  uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
3663 
3664  void GetPoolStats(VmaPoolStats* pStats);
3665 
3666  bool IsEmpty() const { return m_Blocks.empty(); }
3667 
3668  VkResult Allocate(
3669  VmaPool hCurrentPool,
3670  uint32_t currentFrameIndex,
3671  const VkMemoryRequirements& vkMemReq,
3672  const VmaAllocationCreateInfo& createInfo,
3673  VmaSuballocationType suballocType,
3674  VmaAllocation* pAllocation);
3675 
3676  void Free(
3677  VmaAllocation hAllocation);
3678 
3679  // Adds statistics of this BlockVector to pStats.
3680  void AddStats(VmaStats* pStats);
3681 
3682 #if VMA_STATS_STRING_ENABLED
3683  void PrintDetailedMap(class VmaJsonWriter& json);
3684 #endif
3685 
3686  void MakePoolAllocationsLost(
3687  uint32_t currentFrameIndex,
3688  size_t* pLostAllocationCount);
3689 
3690  VmaDefragmentator* EnsureDefragmentator(
3691  VmaAllocator hAllocator,
3692  uint32_t currentFrameIndex);
3693 
3694  VkResult Defragment(
3695  VmaDefragmentationStats* pDefragmentationStats,
3696  VkDeviceSize& maxBytesToMove,
3697  uint32_t& maxAllocationsToMove);
3698 
3699  void DestroyDefragmentator();
3700 
3701 private:
3702  friend class VmaDefragmentator;
3703 
3704  const VmaAllocator m_hAllocator;
3705  const uint32_t m_MemoryTypeIndex;
3706  const VkDeviceSize m_PreferredBlockSize;
3707  const size_t m_MinBlockCount;
3708  const size_t m_MaxBlockCount;
3709  const VkDeviceSize m_BufferImageGranularity;
3710  const uint32_t m_FrameInUseCount;
3711  const bool m_IsCustomPool;
3712  VMA_MUTEX m_Mutex;
3713  // Incrementally sorted by sumFreeSize, ascending.
3714  VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
3715  /* There can be at most one allocation that is completely empty - a
3716  hysteresis to avoid pessimistic case of alternating creation and destruction
3717  of a VkDeviceMemory. */
3718  bool m_HasEmptyBlock;
3719  VmaDefragmentator* m_pDefragmentator;
3720 
3721  size_t CalcMaxBlockSize() const;
3722 
3723  // Finds and removes given block from vector.
3724  void Remove(VmaDeviceMemoryBlock* pBlock);
3725 
3726  // Performs single step in sorting m_Blocks. They may not be fully sorted
3727  // after this call.
3728  void IncrementallySortBlocks();
3729 
3730  VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
3731 };
3732 
3733 struct VmaPool_T
3734 {
3735 public:
3736  VmaBlockVector m_BlockVector;
3737 
3738  // Takes ownership.
3739  VmaPool_T(
3740  VmaAllocator hAllocator,
3741  const VmaPoolCreateInfo& createInfo);
3742  ~VmaPool_T();
3743 
3744  VmaBlockVector& GetBlockVector() { return m_BlockVector; }
3745 
3746 #if VMA_STATS_STRING_ENABLED
3747  //void PrintDetailedMap(class VmaStringBuilder& sb);
3748 #endif
3749 };
3750 
3751 class VmaDefragmentator
3752 {
3753  const VmaAllocator m_hAllocator;
3754  VmaBlockVector* const m_pBlockVector;
3755  uint32_t m_CurrentFrameIndex;
3756  VkDeviceSize m_BytesMoved;
3757  uint32_t m_AllocationsMoved;
3758 
3759  struct AllocationInfo
3760  {
3761  VmaAllocation m_hAllocation;
3762  VkBool32* m_pChanged;
3763 
3764  AllocationInfo() :
3765  m_hAllocation(VK_NULL_HANDLE),
3766  m_pChanged(VMA_NULL)
3767  {
3768  }
3769  };
3770 
3771  struct AllocationInfoSizeGreater
3772  {
3773  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
3774  {
3775  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
3776  }
3777  };
3778 
3779  // Used between AddAllocation and Defragment.
3780  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
3781 
3782  struct BlockInfo
3783  {
3784  VmaDeviceMemoryBlock* m_pBlock;
3785  bool m_HasNonMovableAllocations;
3786  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
3787 
3788  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
3789  m_pBlock(VMA_NULL),
3790  m_HasNonMovableAllocations(true),
3791  m_Allocations(pAllocationCallbacks),
3792  m_pMappedDataForDefragmentation(VMA_NULL)
3793  {
3794  }
3795 
3796  void CalcHasNonMovableAllocations()
3797  {
3798  const size_t blockAllocCount = m_pBlock->m_Metadata.GetAllocationCount();
3799  const size_t defragmentAllocCount = m_Allocations.size();
3800  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
3801  }
3802 
3803  void SortAllocationsBySizeDescecnding()
3804  {
3805  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
3806  }
3807 
3808  VkResult EnsureMapping(VmaAllocator hAllocator, void** ppMappedData);
3809  void Unmap(VmaAllocator hAllocator);
3810 
3811  private:
3812  // Not null if mapped for defragmentation only, not originally mapped.
3813  void* m_pMappedDataForDefragmentation;
3814  };
3815 
3816  struct BlockPointerLess
3817  {
3818  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
3819  {
3820  return pLhsBlockInfo->m_pBlock < pRhsBlock;
3821  }
3822  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
3823  {
3824  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
3825  }
3826  };
3827 
3828  // 1. Blocks with some non-movable allocations go first.
3829  // 2. Blocks with smaller sumFreeSize go first.
3830  struct BlockInfoCompareMoveDestination
3831  {
3832  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
3833  {
3834  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
3835  {
3836  return true;
3837  }
3838  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
3839  {
3840  return false;
3841  }
3842  if(pLhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize())
3843  {
3844  return true;
3845  }
3846  return false;
3847  }
3848  };
3849 
3850  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
3851  BlockInfoVector m_Blocks;
3852 
3853  VkResult DefragmentRound(
3854  VkDeviceSize maxBytesToMove,
3855  uint32_t maxAllocationsToMove);
3856 
3857  static bool MoveMakesSense(
3858  size_t dstBlockIndex, VkDeviceSize dstOffset,
3859  size_t srcBlockIndex, VkDeviceSize srcOffset);
3860 
3861 public:
3862  VmaDefragmentator(
3863  VmaAllocator hAllocator,
3864  VmaBlockVector* pBlockVector,
3865  uint32_t currentFrameIndex);
3866 
3867  ~VmaDefragmentator();
3868 
3869  VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
3870  uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
3871 
3872  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
3873 
3874  VkResult Defragment(
3875  VkDeviceSize maxBytesToMove,
3876  uint32_t maxAllocationsToMove);
3877 };
3878 
3879 // Main allocator object.
3880 struct VmaAllocator_T
3881 {
3882  bool m_UseMutex;
3883  bool m_UseKhrDedicatedAllocation;
3884  VkDevice m_hDevice;
3885  bool m_AllocationCallbacksSpecified;
3886  VkAllocationCallbacks m_AllocationCallbacks;
3887  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
3888 
3889  // Number of bytes free out of limit, or VK_WHOLE_SIZE if not limit for that heap.
3890  VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
3891  VMA_MUTEX m_HeapSizeLimitMutex;
3892 
3893  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
3894  VkPhysicalDeviceMemoryProperties m_MemProps;
3895 
3896  // Default pools.
3897  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
3898 
3899  // Each vector is sorted by memory (handle value).
3900  typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
3901  AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
3902  VMA_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
3903 
3904  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
3905  ~VmaAllocator_T();
3906 
3907  const VkAllocationCallbacks* GetAllocationCallbacks() const
3908  {
3909  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
3910  }
3911  const VmaVulkanFunctions& GetVulkanFunctions() const
3912  {
3913  return m_VulkanFunctions;
3914  }
3915 
3916  VkDeviceSize GetBufferImageGranularity() const
3917  {
3918  return VMA_MAX(
3919  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
3920  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
3921  }
3922 
3923  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
3924  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
3925 
3926  uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
3927  {
3928  VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
3929  return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
3930  }
3931 
3932  void GetBufferMemoryRequirements(
3933  VkBuffer hBuffer,
3934  VkMemoryRequirements& memReq,
3935  bool& requiresDedicatedAllocation,
3936  bool& prefersDedicatedAllocation) const;
3937  void GetImageMemoryRequirements(
3938  VkImage hImage,
3939  VkMemoryRequirements& memReq,
3940  bool& requiresDedicatedAllocation,
3941  bool& prefersDedicatedAllocation) const;
3942 
3943  // Main allocation function.
3944  VkResult AllocateMemory(
3945  const VkMemoryRequirements& vkMemReq,
3946  bool requiresDedicatedAllocation,
3947  bool prefersDedicatedAllocation,
3948  VkBuffer dedicatedBuffer,
3949  VkImage dedicatedImage,
3950  const VmaAllocationCreateInfo& createInfo,
3951  VmaSuballocationType suballocType,
3952  VmaAllocation* pAllocation);
3953 
3954  // Main deallocation function.
3955  void FreeMemory(const VmaAllocation allocation);
3956 
3957  void CalculateStats(VmaStats* pStats);
3958 
3959 #if VMA_STATS_STRING_ENABLED
3960  void PrintDetailedMap(class VmaJsonWriter& json);
3961 #endif
3962 
3963  VkResult Defragment(
3964  VmaAllocation* pAllocations,
3965  size_t allocationCount,
3966  VkBool32* pAllocationsChanged,
3967  const VmaDefragmentationInfo* pDefragmentationInfo,
3968  VmaDefragmentationStats* pDefragmentationStats);
3969 
3970  void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
3971 
3972  VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
3973  void DestroyPool(VmaPool pool);
3974  void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
3975 
3976  void SetCurrentFrameIndex(uint32_t frameIndex);
3977 
3978  void MakePoolAllocationsLost(
3979  VmaPool hPool,
3980  size_t* pLostAllocationCount);
3981 
3982  void CreateLostAllocation(VmaAllocation* pAllocation);
3983 
3984  VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
3985  void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
3986 
3987  VkResult Map(VmaAllocation hAllocation, void** ppData);
3988  void Unmap(VmaAllocation hAllocation);
3989 
3990 private:
3991  VkDeviceSize m_PreferredLargeHeapBlockSize;
3992 
3993  VkPhysicalDevice m_PhysicalDevice;
3994  VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
3995 
3996  VMA_MUTEX m_PoolsMutex;
3997  // Protected by m_PoolsMutex. Sorted by pointer value.
3998  VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
3999 
4000  VmaVulkanFunctions m_VulkanFunctions;
4001 
4002  void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
4003 
4004  VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
4005 
4006  VkResult AllocateMemoryOfType(
4007  const VkMemoryRequirements& vkMemReq,
4008  bool dedicatedAllocation,
4009  VkBuffer dedicatedBuffer,
4010  VkImage dedicatedImage,
4011  const VmaAllocationCreateInfo& createInfo,
4012  uint32_t memTypeIndex,
4013  VmaSuballocationType suballocType,
4014  VmaAllocation* pAllocation);
4015 
4016  // Allocates and registers new VkDeviceMemory specifically for single allocation.
4017  VkResult AllocateDedicatedMemory(
4018  VkDeviceSize size,
4019  VmaSuballocationType suballocType,
4020  uint32_t memTypeIndex,
4021  bool map,
4022  bool isUserDataString,
4023  void* pUserData,
4024  VkBuffer dedicatedBuffer,
4025  VkImage dedicatedImage,
4026  VmaAllocation* pAllocation);
4027 
4028  // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
4029  void FreeDedicatedMemory(VmaAllocation allocation);
4030 };
4031 
4033 // Memory allocation #2 after VmaAllocator_T definition
4034 
4035 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
4036 {
4037  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
4038 }
4039 
4040 static void VmaFree(VmaAllocator hAllocator, void* ptr)
4041 {
4042  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
4043 }
4044 
4045 template<typename T>
4046 static T* VmaAllocate(VmaAllocator hAllocator)
4047 {
4048  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
4049 }
4050 
4051 template<typename T>
4052 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
4053 {
4054  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
4055 }
4056 
4057 template<typename T>
4058 static void vma_delete(VmaAllocator hAllocator, T* ptr)
4059 {
4060  if(ptr != VMA_NULL)
4061  {
4062  ptr->~T();
4063  VmaFree(hAllocator, ptr);
4064  }
4065 }
4066 
4067 template<typename T>
4068 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
4069 {
4070  if(ptr != VMA_NULL)
4071  {
4072  for(size_t i = count; i--; )
4073  ptr[i].~T();
4074  VmaFree(hAllocator, ptr);
4075  }
4076 }
4077 
4079 // VmaStringBuilder
4080 
4081 #if VMA_STATS_STRING_ENABLED
4082 
4083 class VmaStringBuilder
4084 {
4085 public:
4086  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
4087  size_t GetLength() const { return m_Data.size(); }
4088  const char* GetData() const { return m_Data.data(); }
4089 
4090  void Add(char ch) { m_Data.push_back(ch); }
4091  void Add(const char* pStr);
4092  void AddNewLine() { Add('\n'); }
4093  void AddNumber(uint32_t num);
4094  void AddNumber(uint64_t num);
4095  void AddPointer(const void* ptr);
4096 
4097 private:
4098  VmaVector< char, VmaStlAllocator<char> > m_Data;
4099 };
4100 
4101 void VmaStringBuilder::Add(const char* pStr)
4102 {
4103  const size_t strLen = strlen(pStr);
4104  if(strLen > 0)
4105  {
4106  const size_t oldCount = m_Data.size();
4107  m_Data.resize(oldCount + strLen);
4108  memcpy(m_Data.data() + oldCount, pStr, strLen);
4109  }
4110 }
4111 
4112 void VmaStringBuilder::AddNumber(uint32_t num)
4113 {
4114  char buf[11];
4115  VmaUint32ToStr(buf, sizeof(buf), num);
4116  Add(buf);
4117 }
4118 
4119 void VmaStringBuilder::AddNumber(uint64_t num)
4120 {
4121  char buf[21];
4122  VmaUint64ToStr(buf, sizeof(buf), num);
4123  Add(buf);
4124 }
4125 
4126 void VmaStringBuilder::AddPointer(const void* ptr)
4127 {
4128  char buf[21];
4129  VmaPtrToStr(buf, sizeof(buf), ptr);
4130  Add(buf);
4131 }
4132 
4133 #endif // #if VMA_STATS_STRING_ENABLED
4134 
4136 // VmaJsonWriter
4137 
4138 #if VMA_STATS_STRING_ENABLED
4139 
4140 class VmaJsonWriter
4141 {
4142 public:
4143  VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
4144  ~VmaJsonWriter();
4145 
4146  void BeginObject(bool singleLine = false);
4147  void EndObject();
4148 
4149  void BeginArray(bool singleLine = false);
4150  void EndArray();
4151 
4152  void WriteString(const char* pStr);
4153  void BeginString(const char* pStr = VMA_NULL);
4154  void ContinueString(const char* pStr);
4155  void ContinueString(uint32_t n);
4156  void ContinueString(uint64_t n);
4157  void ContinueString_Pointer(const void* ptr);
4158  void EndString(const char* pStr = VMA_NULL);
4159 
4160  void WriteNumber(uint32_t n);
4161  void WriteNumber(uint64_t n);
4162  void WriteBool(bool b);
4163  void WriteNull();
4164 
4165 private:
4166  static const char* const INDENT;
4167 
4168  enum COLLECTION_TYPE
4169  {
4170  COLLECTION_TYPE_OBJECT,
4171  COLLECTION_TYPE_ARRAY,
4172  };
4173  struct StackItem
4174  {
4175  COLLECTION_TYPE type;
4176  uint32_t valueCount;
4177  bool singleLineMode;
4178  };
4179 
4180  VmaStringBuilder& m_SB;
4181  VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
4182  bool m_InsideString;
4183 
4184  void BeginValue(bool isString);
4185  void WriteIndent(bool oneLess = false);
4186 };
4187 
4188 const char* const VmaJsonWriter::INDENT = " ";
4189 
4190 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
4191  m_SB(sb),
4192  m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
4193  m_InsideString(false)
4194 {
4195 }
4196 
4197 VmaJsonWriter::~VmaJsonWriter()
4198 {
4199  VMA_ASSERT(!m_InsideString);
4200  VMA_ASSERT(m_Stack.empty());
4201 }
4202 
4203 void VmaJsonWriter::BeginObject(bool singleLine)
4204 {
4205  VMA_ASSERT(!m_InsideString);
4206 
4207  BeginValue(false);
4208  m_SB.Add('{');
4209 
4210  StackItem item;
4211  item.type = COLLECTION_TYPE_OBJECT;
4212  item.valueCount = 0;
4213  item.singleLineMode = singleLine;
4214  m_Stack.push_back(item);
4215 }
4216 
4217 void VmaJsonWriter::EndObject()
4218 {
4219  VMA_ASSERT(!m_InsideString);
4220 
4221  WriteIndent(true);
4222  m_SB.Add('}');
4223 
4224  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
4225  m_Stack.pop_back();
4226 }
4227 
4228 void VmaJsonWriter::BeginArray(bool singleLine)
4229 {
4230  VMA_ASSERT(!m_InsideString);
4231 
4232  BeginValue(false);
4233  m_SB.Add('[');
4234 
4235  StackItem item;
4236  item.type = COLLECTION_TYPE_ARRAY;
4237  item.valueCount = 0;
4238  item.singleLineMode = singleLine;
4239  m_Stack.push_back(item);
4240 }
4241 
4242 void VmaJsonWriter::EndArray()
4243 {
4244  VMA_ASSERT(!m_InsideString);
4245 
4246  WriteIndent(true);
4247  m_SB.Add(']');
4248 
4249  VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
4250  m_Stack.pop_back();
4251 }
4252 
4253 void VmaJsonWriter::WriteString(const char* pStr)
4254 {
4255  BeginString(pStr);
4256  EndString();
4257 }
4258 
4259 void VmaJsonWriter::BeginString(const char* pStr)
4260 {
4261  VMA_ASSERT(!m_InsideString);
4262 
4263  BeginValue(true);
4264  m_SB.Add('"');
4265  m_InsideString = true;
4266  if(pStr != VMA_NULL && pStr[0] != '\0')
4267  {
4268  ContinueString(pStr);
4269  }
4270 }
4271 
4272 void VmaJsonWriter::ContinueString(const char* pStr)
4273 {
4274  VMA_ASSERT(m_InsideString);
4275 
4276  const size_t strLen = strlen(pStr);
4277  for(size_t i = 0; i < strLen; ++i)
4278  {
4279  char ch = pStr[i];
4280  if(ch == '\'')
4281  {
4282  m_SB.Add("\\\\");
4283  }
4284  else if(ch == '"')
4285  {
4286  m_SB.Add("\\\"");
4287  }
4288  else if(ch >= 32)
4289  {
4290  m_SB.Add(ch);
4291  }
4292  else switch(ch)
4293  {
4294  case '\b':
4295  m_SB.Add("\\b");
4296  break;
4297  case '\f':
4298  m_SB.Add("\\f");
4299  break;
4300  case '\n':
4301  m_SB.Add("\\n");
4302  break;
4303  case '\r':
4304  m_SB.Add("\\r");
4305  break;
4306  case '\t':
4307  m_SB.Add("\\t");
4308  break;
4309  default:
4310  VMA_ASSERT(0 && "Character not currently supported.");
4311  break;
4312  }
4313  }
4314 }
4315 
4316 void VmaJsonWriter::ContinueString(uint32_t n)
4317 {
4318  VMA_ASSERT(m_InsideString);
4319  m_SB.AddNumber(n);
4320 }
4321 
4322 void VmaJsonWriter::ContinueString(uint64_t n)
4323 {
4324  VMA_ASSERT(m_InsideString);
4325  m_SB.AddNumber(n);
4326 }
4327 
4328 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
4329 {
4330  VMA_ASSERT(m_InsideString);
4331  m_SB.AddPointer(ptr);
4332 }
4333 
4334 void VmaJsonWriter::EndString(const char* pStr)
4335 {
4336  VMA_ASSERT(m_InsideString);
4337  if(pStr != VMA_NULL && pStr[0] != '\0')
4338  {
4339  ContinueString(pStr);
4340  }
4341  m_SB.Add('"');
4342  m_InsideString = false;
4343 }
4344 
4345 void VmaJsonWriter::WriteNumber(uint32_t n)
4346 {
4347  VMA_ASSERT(!m_InsideString);
4348  BeginValue(false);
4349  m_SB.AddNumber(n);
4350 }
4351 
4352 void VmaJsonWriter::WriteNumber(uint64_t n)
4353 {
4354  VMA_ASSERT(!m_InsideString);
4355  BeginValue(false);
4356  m_SB.AddNumber(n);
4357 }
4358 
4359 void VmaJsonWriter::WriteBool(bool b)
4360 {
4361  VMA_ASSERT(!m_InsideString);
4362  BeginValue(false);
4363  m_SB.Add(b ? "true" : "false");
4364 }
4365 
4366 void VmaJsonWriter::WriteNull()
4367 {
4368  VMA_ASSERT(!m_InsideString);
4369  BeginValue(false);
4370  m_SB.Add("null");
4371 }
4372 
4373 void VmaJsonWriter::BeginValue(bool isString)
4374 {
4375  if(!m_Stack.empty())
4376  {
4377  StackItem& currItem = m_Stack.back();
4378  if(currItem.type == COLLECTION_TYPE_OBJECT &&
4379  currItem.valueCount % 2 == 0)
4380  {
4381  VMA_ASSERT(isString);
4382  }
4383 
4384  if(currItem.type == COLLECTION_TYPE_OBJECT &&
4385  currItem.valueCount % 2 != 0)
4386  {
4387  m_SB.Add(": ");
4388  }
4389  else if(currItem.valueCount > 0)
4390  {
4391  m_SB.Add(", ");
4392  WriteIndent();
4393  }
4394  else
4395  {
4396  WriteIndent();
4397  }
4398  ++currItem.valueCount;
4399  }
4400 }
4401 
4402 void VmaJsonWriter::WriteIndent(bool oneLess)
4403 {
4404  if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
4405  {
4406  m_SB.AddNewLine();
4407 
4408  size_t count = m_Stack.size();
4409  if(count > 0 && oneLess)
4410  {
4411  --count;
4412  }
4413  for(size_t i = 0; i < count; ++i)
4414  {
4415  m_SB.Add(INDENT);
4416  }
4417  }
4418 }
4419 
4420 #endif // #if VMA_STATS_STRING_ENABLED
4421 
4423 
4424 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
4425 {
4426  if(IsUserDataString())
4427  {
4428  VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
4429 
4430  FreeUserDataString(hAllocator);
4431 
4432  if(pUserData != VMA_NULL)
4433  {
4434  const char* const newStrSrc = (char*)pUserData;
4435  const size_t newStrLen = strlen(newStrSrc);
4436  char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
4437  memcpy(newStrDst, newStrSrc, newStrLen + 1);
4438  m_pUserData = newStrDst;
4439  }
4440  }
4441  else
4442  {
4443  m_pUserData = pUserData;
4444  }
4445 }
4446 
4447 void VmaAllocation_T::ChangeBlockAllocation(
4448  VmaAllocator hAllocator,
4449  VmaDeviceMemoryBlock* block,
4450  VkDeviceSize offset)
4451 {
4452  VMA_ASSERT(block != VMA_NULL);
4453  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4454 
4455  // Move mapping reference counter from old block to new block.
4456  if(block != m_BlockAllocation.m_Block)
4457  {
4458  uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
4459  if(IsPersistentMap())
4460  ++mapRefCount;
4461  m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
4462  block->Map(hAllocator, mapRefCount, VMA_NULL);
4463  }
4464 
4465  m_BlockAllocation.m_Block = block;
4466  m_BlockAllocation.m_Offset = offset;
4467 }
4468 
4469 VkDeviceSize VmaAllocation_T::GetOffset() const
4470 {
4471  switch(m_Type)
4472  {
4473  case ALLOCATION_TYPE_BLOCK:
4474  return m_BlockAllocation.m_Offset;
4475  case ALLOCATION_TYPE_DEDICATED:
4476  return 0;
4477  default:
4478  VMA_ASSERT(0);
4479  return 0;
4480  }
4481 }
4482 
4483 VkDeviceMemory VmaAllocation_T::GetMemory() const
4484 {
4485  switch(m_Type)
4486  {
4487  case ALLOCATION_TYPE_BLOCK:
4488  return m_BlockAllocation.m_Block->m_hMemory;
4489  case ALLOCATION_TYPE_DEDICATED:
4490  return m_DedicatedAllocation.m_hMemory;
4491  default:
4492  VMA_ASSERT(0);
4493  return VK_NULL_HANDLE;
4494  }
4495 }
4496 
4497 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
4498 {
4499  switch(m_Type)
4500  {
4501  case ALLOCATION_TYPE_BLOCK:
4502  return m_BlockAllocation.m_Block->m_MemoryTypeIndex;
4503  case ALLOCATION_TYPE_DEDICATED:
4504  return m_DedicatedAllocation.m_MemoryTypeIndex;
4505  default:
4506  VMA_ASSERT(0);
4507  return UINT32_MAX;
4508  }
4509 }
4510 
4511 void* VmaAllocation_T::GetMappedData() const
4512 {
4513  switch(m_Type)
4514  {
4515  case ALLOCATION_TYPE_BLOCK:
4516  if(m_MapCount != 0)
4517  {
4518  void* pBlockData = m_BlockAllocation.m_Block->m_Mapping.GetMappedData();
4519  VMA_ASSERT(pBlockData != VMA_NULL);
4520  return (char*)pBlockData + m_BlockAllocation.m_Offset;
4521  }
4522  else
4523  {
4524  return VMA_NULL;
4525  }
4526  break;
4527  case ALLOCATION_TYPE_DEDICATED:
4528  VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
4529  return m_DedicatedAllocation.m_pMappedData;
4530  default:
4531  VMA_ASSERT(0);
4532  return VMA_NULL;
4533  }
4534 }
4535 
4536 bool VmaAllocation_T::CanBecomeLost() const
4537 {
4538  switch(m_Type)
4539  {
4540  case ALLOCATION_TYPE_BLOCK:
4541  return m_BlockAllocation.m_CanBecomeLost;
4542  case ALLOCATION_TYPE_DEDICATED:
4543  return false;
4544  default:
4545  VMA_ASSERT(0);
4546  return false;
4547  }
4548 }
4549 
4550 VmaPool VmaAllocation_T::GetPool() const
4551 {
4552  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4553  return m_BlockAllocation.m_hPool;
4554 }
4555 
4556 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
4557 {
4558  VMA_ASSERT(CanBecomeLost());
4559 
4560  /*
4561  Warning: This is a carefully designed algorithm.
4562  Do not modify unless you really know what you're doing :)
4563  */
4564  uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
4565  for(;;)
4566  {
4567  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
4568  {
4569  VMA_ASSERT(0);
4570  return false;
4571  }
4572  else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
4573  {
4574  return false;
4575  }
4576  else // Last use time earlier than current time.
4577  {
4578  if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
4579  {
4580  // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
4581  // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
4582  return true;
4583  }
4584  }
4585  }
4586 }
4587 
4588 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
4589 {
4590  VMA_ASSERT(IsUserDataString());
4591  if(m_pUserData != VMA_NULL)
4592  {
4593  char* const oldStr = (char*)m_pUserData;
4594  const size_t oldStrLen = strlen(oldStr);
4595  vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
4596  m_pUserData = VMA_NULL;
4597  }
4598 }
4599 
4600 void VmaAllocation_T::BlockAllocMap()
4601 {
4602  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
4603 
4604  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
4605  {
4606  ++m_MapCount;
4607  }
4608  else
4609  {
4610  VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
4611  }
4612 }
4613 
4614 void VmaAllocation_T::BlockAllocUnmap()
4615 {
4616  VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
4617 
4618  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
4619  {
4620  --m_MapCount;
4621  }
4622  else
4623  {
4624  VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
4625  }
4626 }
4627 
4628 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
4629 {
4630  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
4631 
4632  if(m_MapCount != 0)
4633  {
4634  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
4635  {
4636  VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
4637  *ppData = m_DedicatedAllocation.m_pMappedData;
4638  ++m_MapCount;
4639  return VK_SUCCESS;
4640  }
4641  else
4642  {
4643  VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
4644  return VK_ERROR_MEMORY_MAP_FAILED;
4645  }
4646  }
4647  else
4648  {
4649  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
4650  hAllocator->m_hDevice,
4651  m_DedicatedAllocation.m_hMemory,
4652  0, // offset
4653  VK_WHOLE_SIZE,
4654  0, // flags
4655  ppData);
4656  if(result == VK_SUCCESS)
4657  {
4658  m_DedicatedAllocation.m_pMappedData = *ppData;
4659  m_MapCount = 1;
4660  }
4661  return result;
4662  }
4663 }
4664 
4665 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
4666 {
4667  VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
4668 
4669  if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
4670  {
4671  --m_MapCount;
4672  if(m_MapCount == 0)
4673  {
4674  m_DedicatedAllocation.m_pMappedData = VMA_NULL;
4675  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
4676  hAllocator->m_hDevice,
4677  m_DedicatedAllocation.m_hMemory);
4678  }
4679  }
4680  else
4681  {
4682  VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
4683  }
4684 }
4685 
4686 #if VMA_STATS_STRING_ENABLED
4687 
4688 // Correspond to values of enum VmaSuballocationType.
4689 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
4690  "FREE",
4691  "UNKNOWN",
4692  "BUFFER",
4693  "IMAGE_UNKNOWN",
4694  "IMAGE_LINEAR",
4695  "IMAGE_OPTIMAL",
4696 };
4697 
4698 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
4699 {
4700  json.BeginObject();
4701 
4702  json.WriteString("Blocks");
4703  json.WriteNumber(stat.blockCount);
4704 
4705  json.WriteString("Allocations");
4706  json.WriteNumber(stat.allocationCount);
4707 
4708  json.WriteString("UnusedRanges");
4709  json.WriteNumber(stat.unusedRangeCount);
4710 
4711  json.WriteString("UsedBytes");
4712  json.WriteNumber(stat.usedBytes);
4713 
4714  json.WriteString("UnusedBytes");
4715  json.WriteNumber(stat.unusedBytes);
4716 
4717  if(stat.allocationCount > 1)
4718  {
4719  json.WriteString("AllocationSize");
4720  json.BeginObject(true);
4721  json.WriteString("Min");
4722  json.WriteNumber(stat.allocationSizeMin);
4723  json.WriteString("Avg");
4724  json.WriteNumber(stat.allocationSizeAvg);
4725  json.WriteString("Max");
4726  json.WriteNumber(stat.allocationSizeMax);
4727  json.EndObject();
4728  }
4729 
4730  if(stat.unusedRangeCount > 1)
4731  {
4732  json.WriteString("UnusedRangeSize");
4733  json.BeginObject(true);
4734  json.WriteString("Min");
4735  json.WriteNumber(stat.unusedRangeSizeMin);
4736  json.WriteString("Avg");
4737  json.WriteNumber(stat.unusedRangeSizeAvg);
4738  json.WriteString("Max");
4739  json.WriteNumber(stat.unusedRangeSizeMax);
4740  json.EndObject();
4741  }
4742 
4743  json.EndObject();
4744 }
4745 
4746 #endif // #if VMA_STATS_STRING_ENABLED
4747 
4748 struct VmaSuballocationItemSizeLess
4749 {
4750  bool operator()(
4751  const VmaSuballocationList::iterator lhs,
4752  const VmaSuballocationList::iterator rhs) const
4753  {
4754  return lhs->size < rhs->size;
4755  }
4756  bool operator()(
4757  const VmaSuballocationList::iterator lhs,
4758  VkDeviceSize rhsSize) const
4759  {
4760  return lhs->size < rhsSize;
4761  }
4762 };
4763 
4765 // class VmaBlockMetadata
4766 
4767 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
4768  m_Size(0),
4769  m_FreeCount(0),
4770  m_SumFreeSize(0),
4771  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
4772  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
4773 {
4774 }
4775 
4776 VmaBlockMetadata::~VmaBlockMetadata()
4777 {
4778 }
4779 
4780 void VmaBlockMetadata::Init(VkDeviceSize size)
4781 {
4782  m_Size = size;
4783  m_FreeCount = 1;
4784  m_SumFreeSize = size;
4785 
4786  VmaSuballocation suballoc = {};
4787  suballoc.offset = 0;
4788  suballoc.size = size;
4789  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
4790  suballoc.hAllocation = VK_NULL_HANDLE;
4791 
4792  m_Suballocations.push_back(suballoc);
4793  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
4794  --suballocItem;
4795  m_FreeSuballocationsBySize.push_back(suballocItem);
4796 }
4797 
4798 bool VmaBlockMetadata::Validate() const
4799 {
4800  if(m_Suballocations.empty())
4801  {
4802  return false;
4803  }
4804 
4805  // Expected offset of new suballocation as calculates from previous ones.
4806  VkDeviceSize calculatedOffset = 0;
4807  // Expected number of free suballocations as calculated from traversing their list.
4808  uint32_t calculatedFreeCount = 0;
4809  // Expected sum size of free suballocations as calculated from traversing their list.
4810  VkDeviceSize calculatedSumFreeSize = 0;
4811  // Expected number of free suballocations that should be registered in
4812  // m_FreeSuballocationsBySize calculated from traversing their list.
4813  size_t freeSuballocationsToRegister = 0;
4814  // True if previous visisted suballocation was free.
4815  bool prevFree = false;
4816 
4817  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
4818  suballocItem != m_Suballocations.cend();
4819  ++suballocItem)
4820  {
4821  const VmaSuballocation& subAlloc = *suballocItem;
4822 
4823  // Actual offset of this suballocation doesn't match expected one.
4824  if(subAlloc.offset != calculatedOffset)
4825  {
4826  return false;
4827  }
4828 
4829  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
4830  // Two adjacent free suballocations are invalid. They should be merged.
4831  if(prevFree && currFree)
4832  {
4833  return false;
4834  }
4835 
4836  if(currFree != (subAlloc.hAllocation == VK_NULL_HANDLE))
4837  {
4838  return false;
4839  }
4840 
4841  if(currFree)
4842  {
4843  calculatedSumFreeSize += subAlloc.size;
4844  ++calculatedFreeCount;
4845  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
4846  {
4847  ++freeSuballocationsToRegister;
4848  }
4849  }
4850  else
4851  {
4852  if(subAlloc.hAllocation->GetOffset() != subAlloc.offset)
4853  {
4854  return false;
4855  }
4856  if(subAlloc.hAllocation->GetSize() != subAlloc.size)
4857  {
4858  return false;
4859  }
4860  }
4861 
4862  calculatedOffset += subAlloc.size;
4863  prevFree = currFree;
4864  }
4865 
4866  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
4867  // match expected one.
4868  if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)
4869  {
4870  return false;
4871  }
4872 
4873  VkDeviceSize lastSize = 0;
4874  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
4875  {
4876  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
4877 
4878  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
4879  if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
4880  {
4881  return false;
4882  }
4883  // They must be sorted by size ascending.
4884  if(suballocItem->size < lastSize)
4885  {
4886  return false;
4887  }
4888 
4889  lastSize = suballocItem->size;
4890  }
4891 
4892  // Check if totals match calculacted values.
4893  if(!ValidateFreeSuballocationList() ||
4894  (calculatedOffset != m_Size) ||
4895  (calculatedSumFreeSize != m_SumFreeSize) ||
4896  (calculatedFreeCount != m_FreeCount))
4897  {
4898  return false;
4899  }
4900 
4901  return true;
4902 }
4903 
4904 VkDeviceSize VmaBlockMetadata::GetUnusedRangeSizeMax() const
4905 {
4906  if(!m_FreeSuballocationsBySize.empty())
4907  {
4908  return m_FreeSuballocationsBySize.back()->size;
4909  }
4910  else
4911  {
4912  return 0;
4913  }
4914 }
4915 
4916 bool VmaBlockMetadata::IsEmpty() const
4917 {
4918  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
4919 }
4920 
4921 void VmaBlockMetadata::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
4922 {
4923  outInfo.blockCount = 1;
4924 
4925  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
4926  outInfo.allocationCount = rangeCount - m_FreeCount;
4927  outInfo.unusedRangeCount = m_FreeCount;
4928 
4929  outInfo.unusedBytes = m_SumFreeSize;
4930  outInfo.usedBytes = m_Size - outInfo.unusedBytes;
4931 
4932  outInfo.allocationSizeMin = UINT64_MAX;
4933  outInfo.allocationSizeMax = 0;
4934  outInfo.unusedRangeSizeMin = UINT64_MAX;
4935  outInfo.unusedRangeSizeMax = 0;
4936 
4937  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
4938  suballocItem != m_Suballocations.cend();
4939  ++suballocItem)
4940  {
4941  const VmaSuballocation& suballoc = *suballocItem;
4942  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
4943  {
4944  outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
4945  outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
4946  }
4947  else
4948  {
4949  outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
4950  outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
4951  }
4952  }
4953 }
4954 
4955 void VmaBlockMetadata::AddPoolStats(VmaPoolStats& inoutStats) const
4956 {
4957  const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
4958 
4959  inoutStats.size += m_Size;
4960  inoutStats.unusedSize += m_SumFreeSize;
4961  inoutStats.allocationCount += rangeCount - m_FreeCount;
4962  inoutStats.unusedRangeCount += m_FreeCount;
4963  inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
4964 }
4965 
4966 #if VMA_STATS_STRING_ENABLED
4967 
4968 void VmaBlockMetadata::PrintDetailedMap(class VmaJsonWriter& json) const
4969 {
4970  json.BeginObject();
4971 
4972  json.WriteString("TotalBytes");
4973  json.WriteNumber(m_Size);
4974 
4975  json.WriteString("UnusedBytes");
4976  json.WriteNumber(m_SumFreeSize);
4977 
4978  json.WriteString("Allocations");
4979  json.WriteNumber((uint64_t)m_Suballocations.size() - m_FreeCount);
4980 
4981  json.WriteString("UnusedRanges");
4982  json.WriteNumber(m_FreeCount);
4983 
4984  json.WriteString("Suballocations");
4985  json.BeginArray();
4986  size_t i = 0;
4987  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
4988  suballocItem != m_Suballocations.cend();
4989  ++suballocItem, ++i)
4990  {
4991  json.BeginObject(true);
4992 
4993  json.WriteString("Type");
4994  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]);
4995 
4996  json.WriteString("Size");
4997  json.WriteNumber(suballocItem->size);
4998 
4999  json.WriteString("Offset");
5000  json.WriteNumber(suballocItem->offset);
5001 
5002  if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
5003  {
5004  const void* pUserData = suballocItem->hAllocation->GetUserData();
5005  if(pUserData != VMA_NULL)
5006  {
5007  json.WriteString("UserData");
5008  if(suballocItem->hAllocation->IsUserDataString())
5009  {
5010  json.WriteString((const char*)pUserData);
5011  }
5012  else
5013  {
5014  json.BeginString();
5015  json.ContinueString_Pointer(pUserData);
5016  json.EndString();
5017  }
5018  }
5019  }
5020 
5021  json.EndObject();
5022  }
5023  json.EndArray();
5024 
5025  json.EndObject();
5026 }
5027 
5028 #endif // #if VMA_STATS_STRING_ENABLED
5029 
5030 /*
5031 How many suitable free suballocations to analyze before choosing best one.
5032 - Set to 1 to use First-Fit algorithm - first suitable free suballocation will
5033  be chosen.
5034 - Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
5035  suballocations will be analized and best one will be chosen.
5036 - Any other value is also acceptable.
5037 */
5038 //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
5039 
5040 void VmaBlockMetadata::CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest)
5041 {
5042  VMA_ASSERT(IsEmpty());
5043  pAllocationRequest->offset = 0;
5044  pAllocationRequest->sumFreeSize = m_SumFreeSize;
5045  pAllocationRequest->sumItemSize = 0;
5046  pAllocationRequest->item = m_Suballocations.begin();
5047  pAllocationRequest->itemsToMakeLostCount = 0;
5048 }
5049 
5050 bool VmaBlockMetadata::CreateAllocationRequest(
5051  uint32_t currentFrameIndex,
5052  uint32_t frameInUseCount,
5053  VkDeviceSize bufferImageGranularity,
5054  VkDeviceSize allocSize,
5055  VkDeviceSize allocAlignment,
5056  VmaSuballocationType allocType,
5057  bool canMakeOtherLost,
5058  VmaAllocationRequest* pAllocationRequest)
5059 {
5060  VMA_ASSERT(allocSize > 0);
5061  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5062  VMA_ASSERT(pAllocationRequest != VMA_NULL);
5063  VMA_HEAVY_ASSERT(Validate());
5064 
5065  // There is not enough total free space in this block to fullfill the request: Early return.
5066  if(canMakeOtherLost == false && m_SumFreeSize < allocSize)
5067  {
5068  return false;
5069  }
5070 
5071  // New algorithm, efficiently searching freeSuballocationsBySize.
5072  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
5073  if(freeSuballocCount > 0)
5074  {
5075  if(VMA_BEST_FIT)
5076  {
5077  // Find first free suballocation with size not less than allocSize.
5078  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
5079  m_FreeSuballocationsBySize.data(),
5080  m_FreeSuballocationsBySize.data() + freeSuballocCount,
5081  allocSize,
5082  VmaSuballocationItemSizeLess());
5083  size_t index = it - m_FreeSuballocationsBySize.data();
5084  for(; index < freeSuballocCount; ++index)
5085  {
5086  if(CheckAllocation(
5087  currentFrameIndex,
5088  frameInUseCount,
5089  bufferImageGranularity,
5090  allocSize,
5091  allocAlignment,
5092  allocType,
5093  m_FreeSuballocationsBySize[index],
5094  false, // canMakeOtherLost
5095  &pAllocationRequest->offset,
5096  &pAllocationRequest->itemsToMakeLostCount,
5097  &pAllocationRequest->sumFreeSize,
5098  &pAllocationRequest->sumItemSize))
5099  {
5100  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5101  return true;
5102  }
5103  }
5104  }
5105  else
5106  {
5107  // Search staring from biggest suballocations.
5108  for(size_t index = freeSuballocCount; index--; )
5109  {
5110  if(CheckAllocation(
5111  currentFrameIndex,
5112  frameInUseCount,
5113  bufferImageGranularity,
5114  allocSize,
5115  allocAlignment,
5116  allocType,
5117  m_FreeSuballocationsBySize[index],
5118  false, // canMakeOtherLost
5119  &pAllocationRequest->offset,
5120  &pAllocationRequest->itemsToMakeLostCount,
5121  &pAllocationRequest->sumFreeSize,
5122  &pAllocationRequest->sumItemSize))
5123  {
5124  pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5125  return true;
5126  }
5127  }
5128  }
5129  }
5130 
5131  if(canMakeOtherLost)
5132  {
5133  // Brute-force algorithm. TODO: Come up with something better.
5134 
5135  pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
5136  pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
5137 
5138  VmaAllocationRequest tmpAllocRequest = {};
5139  for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
5140  suballocIt != m_Suballocations.end();
5141  ++suballocIt)
5142  {
5143  if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
5144  suballocIt->hAllocation->CanBecomeLost())
5145  {
5146  if(CheckAllocation(
5147  currentFrameIndex,
5148  frameInUseCount,
5149  bufferImageGranularity,
5150  allocSize,
5151  allocAlignment,
5152  allocType,
5153  suballocIt,
5154  canMakeOtherLost,
5155  &tmpAllocRequest.offset,
5156  &tmpAllocRequest.itemsToMakeLostCount,
5157  &tmpAllocRequest.sumFreeSize,
5158  &tmpAllocRequest.sumItemSize))
5159  {
5160  tmpAllocRequest.item = suballocIt;
5161 
5162  if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
5163  {
5164  *pAllocationRequest = tmpAllocRequest;
5165  }
5166  }
5167  }
5168  }
5169 
5170  if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
5171  {
5172  return true;
5173  }
5174  }
5175 
5176  return false;
5177 }
5178 
5179 bool VmaBlockMetadata::MakeRequestedAllocationsLost(
5180  uint32_t currentFrameIndex,
5181  uint32_t frameInUseCount,
5182  VmaAllocationRequest* pAllocationRequest)
5183 {
5184  while(pAllocationRequest->itemsToMakeLostCount > 0)
5185  {
5186  if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
5187  {
5188  ++pAllocationRequest->item;
5189  }
5190  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5191  VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
5192  VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
5193  if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5194  {
5195  pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
5196  --pAllocationRequest->itemsToMakeLostCount;
5197  }
5198  else
5199  {
5200  return false;
5201  }
5202  }
5203 
5204  VMA_HEAVY_ASSERT(Validate());
5205  VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5206  VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
5207 
5208  return true;
5209 }
5210 
5211 uint32_t VmaBlockMetadata::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
5212 {
5213  uint32_t lostAllocationCount = 0;
5214  for(VmaSuballocationList::iterator it = m_Suballocations.begin();
5215  it != m_Suballocations.end();
5216  ++it)
5217  {
5218  if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
5219  it->hAllocation->CanBecomeLost() &&
5220  it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5221  {
5222  it = FreeSuballocation(it);
5223  ++lostAllocationCount;
5224  }
5225  }
5226  return lostAllocationCount;
5227 }
5228 
5229 void VmaBlockMetadata::Alloc(
5230  const VmaAllocationRequest& request,
5231  VmaSuballocationType type,
5232  VkDeviceSize allocSize,
5233  VmaAllocation hAllocation)
5234 {
5235  VMA_ASSERT(request.item != m_Suballocations.end());
5236  VmaSuballocation& suballoc = *request.item;
5237  // Given suballocation is a free block.
5238  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
5239  // Given offset is inside this suballocation.
5240  VMA_ASSERT(request.offset >= suballoc.offset);
5241  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
5242  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
5243  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
5244 
5245  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
5246  // it to become used.
5247  UnregisterFreeSuballocation(request.item);
5248 
5249  suballoc.offset = request.offset;
5250  suballoc.size = allocSize;
5251  suballoc.type = type;
5252  suballoc.hAllocation = hAllocation;
5253 
5254  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
5255  if(paddingEnd)
5256  {
5257  VmaSuballocation paddingSuballoc = {};
5258  paddingSuballoc.offset = request.offset + allocSize;
5259  paddingSuballoc.size = paddingEnd;
5260  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5261  VmaSuballocationList::iterator next = request.item;
5262  ++next;
5263  const VmaSuballocationList::iterator paddingEndItem =
5264  m_Suballocations.insert(next, paddingSuballoc);
5265  RegisterFreeSuballocation(paddingEndItem);
5266  }
5267 
5268  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
5269  if(paddingBegin)
5270  {
5271  VmaSuballocation paddingSuballoc = {};
5272  paddingSuballoc.offset = request.offset - paddingBegin;
5273  paddingSuballoc.size = paddingBegin;
5274  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5275  const VmaSuballocationList::iterator paddingBeginItem =
5276  m_Suballocations.insert(request.item, paddingSuballoc);
5277  RegisterFreeSuballocation(paddingBeginItem);
5278  }
5279 
5280  // Update totals.
5281  m_FreeCount = m_FreeCount - 1;
5282  if(paddingBegin > 0)
5283  {
5284  ++m_FreeCount;
5285  }
5286  if(paddingEnd > 0)
5287  {
5288  ++m_FreeCount;
5289  }
5290  m_SumFreeSize -= allocSize;
5291 }
5292 
5293 void VmaBlockMetadata::Free(const VmaAllocation allocation)
5294 {
5295  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5296  suballocItem != m_Suballocations.end();
5297  ++suballocItem)
5298  {
5299  VmaSuballocation& suballoc = *suballocItem;
5300  if(suballoc.hAllocation == allocation)
5301  {
5302  FreeSuballocation(suballocItem);
5303  VMA_HEAVY_ASSERT(Validate());
5304  return;
5305  }
5306  }
5307  VMA_ASSERT(0 && "Not found!");
5308 }
5309 
5310 void VmaBlockMetadata::FreeAtOffset(VkDeviceSize offset)
5311 {
5312  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5313  suballocItem != m_Suballocations.end();
5314  ++suballocItem)
5315  {
5316  VmaSuballocation& suballoc = *suballocItem;
5317  if(suballoc.offset == offset)
5318  {
5319  FreeSuballocation(suballocItem);
5320  return;
5321  }
5322  }
5323  VMA_ASSERT(0 && "Not found!");
5324 }
5325 
5326 bool VmaBlockMetadata::ValidateFreeSuballocationList() const
5327 {
5328  VkDeviceSize lastSize = 0;
5329  for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
5330  {
5331  const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
5332 
5333  if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
5334  {
5335  VMA_ASSERT(0);
5336  return false;
5337  }
5338  if(it->size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5339  {
5340  VMA_ASSERT(0);
5341  return false;
5342  }
5343  if(it->size < lastSize)
5344  {
5345  VMA_ASSERT(0);
5346  return false;
5347  }
5348 
5349  lastSize = it->size;
5350  }
5351  return true;
5352 }
5353 
5354 bool VmaBlockMetadata::CheckAllocation(
5355  uint32_t currentFrameIndex,
5356  uint32_t frameInUseCount,
5357  VkDeviceSize bufferImageGranularity,
5358  VkDeviceSize allocSize,
5359  VkDeviceSize allocAlignment,
5360  VmaSuballocationType allocType,
5361  VmaSuballocationList::const_iterator suballocItem,
5362  bool canMakeOtherLost,
5363  VkDeviceSize* pOffset,
5364  size_t* itemsToMakeLostCount,
5365  VkDeviceSize* pSumFreeSize,
5366  VkDeviceSize* pSumItemSize) const
5367 {
5368  VMA_ASSERT(allocSize > 0);
5369  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5370  VMA_ASSERT(suballocItem != m_Suballocations.cend());
5371  VMA_ASSERT(pOffset != VMA_NULL);
5372 
5373  *itemsToMakeLostCount = 0;
5374  *pSumFreeSize = 0;
5375  *pSumItemSize = 0;
5376 
5377  if(canMakeOtherLost)
5378  {
5379  if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5380  {
5381  *pSumFreeSize = suballocItem->size;
5382  }
5383  else
5384  {
5385  if(suballocItem->hAllocation->CanBecomeLost() &&
5386  suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5387  {
5388  ++*itemsToMakeLostCount;
5389  *pSumItemSize = suballocItem->size;
5390  }
5391  else
5392  {
5393  return false;
5394  }
5395  }
5396 
5397  // Remaining size is too small for this request: Early return.
5398  if(m_Size - suballocItem->offset < allocSize)
5399  {
5400  return false;
5401  }
5402 
5403  // Start from offset equal to beginning of this suballocation.
5404  *pOffset = suballocItem->offset;
5405 
5406  // Apply VMA_DEBUG_MARGIN at the beginning.
5407  if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
5408  {
5409  *pOffset += VMA_DEBUG_MARGIN;
5410  }
5411 
5412  // Apply alignment.
5413  const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
5414  *pOffset = VmaAlignUp(*pOffset, alignment);
5415 
5416  // Check previous suballocations for BufferImageGranularity conflicts.
5417  // Make bigger alignment if necessary.
5418  if(bufferImageGranularity > 1)
5419  {
5420  bool bufferImageGranularityConflict = false;
5421  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
5422  while(prevSuballocItem != m_Suballocations.cbegin())
5423  {
5424  --prevSuballocItem;
5425  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
5426  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
5427  {
5428  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
5429  {
5430  bufferImageGranularityConflict = true;
5431  break;
5432  }
5433  }
5434  else
5435  // Already on previous page.
5436  break;
5437  }
5438  if(bufferImageGranularityConflict)
5439  {
5440  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
5441  }
5442  }
5443 
5444  // Now that we have final *pOffset, check if we are past suballocItem.
5445  // If yes, return false - this function should be called for another suballocItem as starting point.
5446  if(*pOffset >= suballocItem->offset + suballocItem->size)
5447  {
5448  return false;
5449  }
5450 
5451  // Calculate padding at the beginning based on current offset.
5452  const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
5453 
5454  // Calculate required margin at the end if this is not last suballocation.
5455  VmaSuballocationList::const_iterator next = suballocItem;
5456  ++next;
5457  const VkDeviceSize requiredEndMargin =
5458  (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
5459 
5460  const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
5461  // Another early return check.
5462  if(suballocItem->offset + totalSize > m_Size)
5463  {
5464  return false;
5465  }
5466 
5467  // Advance lastSuballocItem until desired size is reached.
5468  // Update itemsToMakeLostCount.
5469  VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
5470  if(totalSize > suballocItem->size)
5471  {
5472  VkDeviceSize remainingSize = totalSize - suballocItem->size;
5473  while(remainingSize > 0)
5474  {
5475  ++lastSuballocItem;
5476  if(lastSuballocItem == m_Suballocations.cend())
5477  {
5478  return false;
5479  }
5480  if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5481  {
5482  *pSumFreeSize += lastSuballocItem->size;
5483  }
5484  else
5485  {
5486  VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
5487  if(lastSuballocItem->hAllocation->CanBecomeLost() &&
5488  lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5489  {
5490  ++*itemsToMakeLostCount;
5491  *pSumItemSize += lastSuballocItem->size;
5492  }
5493  else
5494  {
5495  return false;
5496  }
5497  }
5498  remainingSize = (lastSuballocItem->size < remainingSize) ?
5499  remainingSize - lastSuballocItem->size : 0;
5500  }
5501  }
5502 
5503  // Check next suballocations for BufferImageGranularity conflicts.
5504  // If conflict exists, we must mark more allocations lost or fail.
5505  if(bufferImageGranularity > 1)
5506  {
5507  VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
5508  ++nextSuballocItem;
5509  while(nextSuballocItem != m_Suballocations.cend())
5510  {
5511  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
5512  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
5513  {
5514  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
5515  {
5516  VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
5517  if(nextSuballoc.hAllocation->CanBecomeLost() &&
5518  nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5519  {
5520  ++*itemsToMakeLostCount;
5521  }
5522  else
5523  {
5524  return false;
5525  }
5526  }
5527  }
5528  else
5529  {
5530  // Already on next page.
5531  break;
5532  }
5533  ++nextSuballocItem;
5534  }
5535  }
5536  }
5537  else
5538  {
5539  const VmaSuballocation& suballoc = *suballocItem;
5540  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
5541 
5542  *pSumFreeSize = suballoc.size;
5543 
5544  // Size of this suballocation is too small for this request: Early return.
5545  if(suballoc.size < allocSize)
5546  {
5547  return false;
5548  }
5549 
5550  // Start from offset equal to beginning of this suballocation.
5551  *pOffset = suballoc.offset;
5552 
5553  // Apply VMA_DEBUG_MARGIN at the beginning.
5554  if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
5555  {
5556  *pOffset += VMA_DEBUG_MARGIN;
5557  }
5558 
5559  // Apply alignment.
5560  const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
5561  *pOffset = VmaAlignUp(*pOffset, alignment);
5562 
5563  // Check previous suballocations for BufferImageGranularity conflicts.
5564  // Make bigger alignment if necessary.
5565  if(bufferImageGranularity > 1)
5566  {
5567  bool bufferImageGranularityConflict = false;
5568  VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
5569  while(prevSuballocItem != m_Suballocations.cbegin())
5570  {
5571  --prevSuballocItem;
5572  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
5573  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
5574  {
5575  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
5576  {
5577  bufferImageGranularityConflict = true;
5578  break;
5579  }
5580  }
5581  else
5582  // Already on previous page.
5583  break;
5584  }
5585  if(bufferImageGranularityConflict)
5586  {
5587  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
5588  }
5589  }
5590 
5591  // Calculate padding at the beginning based on current offset.
5592  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
5593 
5594  // Calculate required margin at the end if this is not last suballocation.
5595  VmaSuballocationList::const_iterator next = suballocItem;
5596  ++next;
5597  const VkDeviceSize requiredEndMargin =
5598  (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
5599 
5600  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
5601  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
5602  {
5603  return false;
5604  }
5605 
5606  // Check next suballocations for BufferImageGranularity conflicts.
5607  // If conflict exists, allocation cannot be made here.
5608  if(bufferImageGranularity > 1)
5609  {
5610  VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
5611  ++nextSuballocItem;
5612  while(nextSuballocItem != m_Suballocations.cend())
5613  {
5614  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
5615  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
5616  {
5617  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
5618  {
5619  return false;
5620  }
5621  }
5622  else
5623  {
5624  // Already on next page.
5625  break;
5626  }
5627  ++nextSuballocItem;
5628  }
5629  }
5630  }
5631 
5632  // All tests passed: Success. pOffset is already filled.
5633  return true;
5634 }
5635 
5636 void VmaBlockMetadata::MergeFreeWithNext(VmaSuballocationList::iterator item)
5637 {
5638  VMA_ASSERT(item != m_Suballocations.end());
5639  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
5640 
5641  VmaSuballocationList::iterator nextItem = item;
5642  ++nextItem;
5643  VMA_ASSERT(nextItem != m_Suballocations.end());
5644  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
5645 
5646  item->size += nextItem->size;
5647  --m_FreeCount;
5648  m_Suballocations.erase(nextItem);
5649 }
5650 
5651 VmaSuballocationList::iterator VmaBlockMetadata::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
5652 {
5653  // Change this suballocation to be marked as free.
5654  VmaSuballocation& suballoc = *suballocItem;
5655  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5656  suballoc.hAllocation = VK_NULL_HANDLE;
5657 
5658  // Update totals.
5659  ++m_FreeCount;
5660  m_SumFreeSize += suballoc.size;
5661 
5662  // Merge with previous and/or next suballocation if it's also free.
5663  bool mergeWithNext = false;
5664  bool mergeWithPrev = false;
5665 
5666  VmaSuballocationList::iterator nextItem = suballocItem;
5667  ++nextItem;
5668  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
5669  {
5670  mergeWithNext = true;
5671  }
5672 
5673  VmaSuballocationList::iterator prevItem = suballocItem;
5674  if(suballocItem != m_Suballocations.begin())
5675  {
5676  --prevItem;
5677  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5678  {
5679  mergeWithPrev = true;
5680  }
5681  }
5682 
5683  if(mergeWithNext)
5684  {
5685  UnregisterFreeSuballocation(nextItem);
5686  MergeFreeWithNext(suballocItem);
5687  }
5688 
5689  if(mergeWithPrev)
5690  {
5691  UnregisterFreeSuballocation(prevItem);
5692  MergeFreeWithNext(prevItem);
5693  RegisterFreeSuballocation(prevItem);
5694  return prevItem;
5695  }
5696  else
5697  {
5698  RegisterFreeSuballocation(suballocItem);
5699  return suballocItem;
5700  }
5701 }
5702 
5703 void VmaBlockMetadata::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
5704 {
5705  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
5706  VMA_ASSERT(item->size > 0);
5707 
5708  // You may want to enable this validation at the beginning or at the end of
5709  // this function, depending on what do you want to check.
5710  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
5711 
5712  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5713  {
5714  if(m_FreeSuballocationsBySize.empty())
5715  {
5716  m_FreeSuballocationsBySize.push_back(item);
5717  }
5718  else
5719  {
5720  VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
5721  }
5722  }
5723 
5724  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
5725 }
5726 
5727 
5728 void VmaBlockMetadata::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
5729 {
5730  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
5731  VMA_ASSERT(item->size > 0);
5732 
5733  // You may want to enable this validation at the beginning or at the end of
5734  // this function, depending on what do you want to check.
5735  VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
5736 
5737  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5738  {
5739  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
5740  m_FreeSuballocationsBySize.data(),
5741  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
5742  item,
5743  VmaSuballocationItemSizeLess());
5744  for(size_t index = it - m_FreeSuballocationsBySize.data();
5745  index < m_FreeSuballocationsBySize.size();
5746  ++index)
5747  {
5748  if(m_FreeSuballocationsBySize[index] == item)
5749  {
5750  VmaVectorRemove(m_FreeSuballocationsBySize, index);
5751  return;
5752  }
5753  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
5754  }
5755  VMA_ASSERT(0 && "Not found.");
5756  }
5757 
5758  //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
5759 }
5760 
5762 // class VmaDeviceMemoryMapping
5763 
5764 VmaDeviceMemoryMapping::VmaDeviceMemoryMapping() :
5765  m_MapCount(0),
5766  m_pMappedData(VMA_NULL)
5767 {
5768 }
5769 
5770 VmaDeviceMemoryMapping::~VmaDeviceMemoryMapping()
5771 {
5772  VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5773 }
5774 
5775 VkResult VmaDeviceMemoryMapping::Map(VmaAllocator hAllocator, VkDeviceMemory hMemory, uint32_t count, void **ppData)
5776 {
5777  if(count == 0)
5778  {
5779  return VK_SUCCESS;
5780  }
5781 
5782  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
5783  if(m_MapCount != 0)
5784  {
5785  m_MapCount += count;
5786  VMA_ASSERT(m_pMappedData != VMA_NULL);
5787  if(ppData != VMA_NULL)
5788  {
5789  *ppData = m_pMappedData;
5790  }
5791  return VK_SUCCESS;
5792  }
5793  else
5794  {
5795  VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
5796  hAllocator->m_hDevice,
5797  hMemory,
5798  0, // offset
5799  VK_WHOLE_SIZE,
5800  0, // flags
5801  &m_pMappedData);
5802  if(result == VK_SUCCESS)
5803  {
5804  if(ppData != VMA_NULL)
5805  {
5806  *ppData = m_pMappedData;
5807  }
5808  m_MapCount = count;
5809  }
5810  return result;
5811  }
5812 }
5813 
5814 void VmaDeviceMemoryMapping::Unmap(VmaAllocator hAllocator, VkDeviceMemory hMemory, uint32_t count)
5815 {
5816  if(count == 0)
5817  {
5818  return;
5819  }
5820 
5821  VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
5822  if(m_MapCount >= count)
5823  {
5824  m_MapCount -= count;
5825  if(m_MapCount == 0)
5826  {
5827  m_pMappedData = VMA_NULL;
5828  (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, hMemory);
5829  }
5830  }
5831  else
5832  {
5833  VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
5834  }
5835 }
5836 
5838 // class VmaDeviceMemoryBlock
5839 
5840 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
5841  m_MemoryTypeIndex(UINT32_MAX),
5842  m_hMemory(VK_NULL_HANDLE),
5843  m_Metadata(hAllocator)
5844 {
5845 }
5846 
5847 void VmaDeviceMemoryBlock::Init(
5848  uint32_t newMemoryTypeIndex,
5849  VkDeviceMemory newMemory,
5850  VkDeviceSize newSize)
5851 {
5852  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5853 
5854  m_MemoryTypeIndex = newMemoryTypeIndex;
5855  m_hMemory = newMemory;
5856 
5857  m_Metadata.Init(newSize);
5858 }
5859 
5860 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
5861 {
5862  // This is the most important assert in the entire library.
5863  // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
5864  VMA_ASSERT(m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
5865 
5866  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
5867  allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_Metadata.GetSize(), m_hMemory);
5868  m_hMemory = VK_NULL_HANDLE;
5869 }
5870 
5871 bool VmaDeviceMemoryBlock::Validate() const
5872 {
5873  if((m_hMemory == VK_NULL_HANDLE) ||
5874  (m_Metadata.GetSize() == 0))
5875  {
5876  return false;
5877  }
5878 
5879  return m_Metadata.Validate();
5880 }
5881 
5882 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
5883 {
5884  return m_Mapping.Map(hAllocator, m_hMemory, count, ppData);
5885 }
5886 
5887 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
5888 {
5889  m_Mapping.Unmap(hAllocator, m_hMemory, count);
5890 }
5891 
5892 static void InitStatInfo(VmaStatInfo& outInfo)
5893 {
5894  memset(&outInfo, 0, sizeof(outInfo));
5895  outInfo.allocationSizeMin = UINT64_MAX;
5896  outInfo.unusedRangeSizeMin = UINT64_MAX;
5897 }
5898 
5899 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
5900 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
5901 {
5902  inoutInfo.blockCount += srcInfo.blockCount;
5903  inoutInfo.allocationCount += srcInfo.allocationCount;
5904  inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
5905  inoutInfo.usedBytes += srcInfo.usedBytes;
5906  inoutInfo.unusedBytes += srcInfo.unusedBytes;
5907  inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
5908  inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
5909  inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
5910  inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
5911 }
5912 
5913 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
5914 {
5915  inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
5916  VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
5917  inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
5918  VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
5919 }
5920 
5921 VmaPool_T::VmaPool_T(
5922  VmaAllocator hAllocator,
5923  const VmaPoolCreateInfo& createInfo) :
5924  m_BlockVector(
5925  hAllocator,
5926  createInfo.memoryTypeIndex,
5927  createInfo.blockSize,
5928  createInfo.minBlockCount,
5929  createInfo.maxBlockCount,
5930  (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
5931  createInfo.frameInUseCount,
5932  true) // isCustomPool
5933 {
5934 }
5935 
5936 VmaPool_T::~VmaPool_T()
5937 {
5938 }
5939 
5940 #if VMA_STATS_STRING_ENABLED
5941 
5942 #endif // #if VMA_STATS_STRING_ENABLED
5943 
5944 VmaBlockVector::VmaBlockVector(
5945  VmaAllocator hAllocator,
5946  uint32_t memoryTypeIndex,
5947  VkDeviceSize preferredBlockSize,
5948  size_t minBlockCount,
5949  size_t maxBlockCount,
5950  VkDeviceSize bufferImageGranularity,
5951  uint32_t frameInUseCount,
5952  bool isCustomPool) :
5953  m_hAllocator(hAllocator),
5954  m_MemoryTypeIndex(memoryTypeIndex),
5955  m_PreferredBlockSize(preferredBlockSize),
5956  m_MinBlockCount(minBlockCount),
5957  m_MaxBlockCount(maxBlockCount),
5958  m_BufferImageGranularity(bufferImageGranularity),
5959  m_FrameInUseCount(frameInUseCount),
5960  m_IsCustomPool(isCustomPool),
5961  m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
5962  m_HasEmptyBlock(false),
5963  m_pDefragmentator(VMA_NULL)
5964 {
5965 }
5966 
5967 VmaBlockVector::~VmaBlockVector()
5968 {
5969  VMA_ASSERT(m_pDefragmentator == VMA_NULL);
5970 
5971  for(size_t i = m_Blocks.size(); i--; )
5972  {
5973  m_Blocks[i]->Destroy(m_hAllocator);
5974  vma_delete(m_hAllocator, m_Blocks[i]);
5975  }
5976 }
5977 
5978 VkResult VmaBlockVector::CreateMinBlocks()
5979 {
5980  for(size_t i = 0; i < m_MinBlockCount; ++i)
5981  {
5982  VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
5983  if(res != VK_SUCCESS)
5984  {
5985  return res;
5986  }
5987  }
5988  return VK_SUCCESS;
5989 }
5990 
5991 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
5992 {
5993  pStats->size = 0;
5994  pStats->unusedSize = 0;
5995  pStats->allocationCount = 0;
5996  pStats->unusedRangeCount = 0;
5997  pStats->unusedRangeSizeMax = 0;
5998 
5999  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6000 
6001  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6002  {
6003  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
6004  VMA_ASSERT(pBlock);
6005  VMA_HEAVY_ASSERT(pBlock->Validate());
6006  pBlock->m_Metadata.AddPoolStats(*pStats);
6007  }
6008 }
6009 
6010 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
6011 
6012 VkResult VmaBlockVector::Allocate(
6013  VmaPool hCurrentPool,
6014  uint32_t currentFrameIndex,
6015  const VkMemoryRequirements& vkMemReq,
6016  const VmaAllocationCreateInfo& createInfo,
6017  VmaSuballocationType suballocType,
6018  VmaAllocation* pAllocation)
6019 {
6020  const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
6021  const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
6022 
6023  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6024 
6025  // 1. Search existing allocations. Try to allocate without making other allocations lost.
6026  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6027  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6028  {
6029  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6030  VMA_ASSERT(pCurrBlock);
6031  VmaAllocationRequest currRequest = {};
6032  if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6033  currentFrameIndex,
6034  m_FrameInUseCount,
6035  m_BufferImageGranularity,
6036  vkMemReq.size,
6037  vkMemReq.alignment,
6038  suballocType,
6039  false, // canMakeOtherLost
6040  &currRequest))
6041  {
6042  // Allocate from pCurrBlock.
6043  VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
6044 
6045  if(mapped)
6046  {
6047  VkResult res = pCurrBlock->Map(m_hAllocator, 1, VMA_NULL);
6048  if(res != VK_SUCCESS)
6049  {
6050  return res;
6051  }
6052  }
6053 
6054  // We no longer have an empty Allocation.
6055  if(pCurrBlock->m_Metadata.IsEmpty())
6056  {
6057  m_HasEmptyBlock = false;
6058  }
6059 
6060  *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6061  pCurrBlock->m_Metadata.Alloc(currRequest, suballocType, vkMemReq.size, *pAllocation);
6062  (*pAllocation)->InitBlockAllocation(
6063  hCurrentPool,
6064  pCurrBlock,
6065  currRequest.offset,
6066  vkMemReq.alignment,
6067  vkMemReq.size,
6068  suballocType,
6069  mapped,
6070  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6071  VMA_HEAVY_ASSERT(pCurrBlock->Validate());
6072  VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
6073  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6074  return VK_SUCCESS;
6075  }
6076  }
6077 
6078  const bool canCreateNewBlock =
6079  ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
6080  (m_Blocks.size() < m_MaxBlockCount);
6081 
6082  // 2. Try to create new block.
6083  if(canCreateNewBlock)
6084  {
6085  // Calculate optimal size for new block.
6086  VkDeviceSize newBlockSize = m_PreferredBlockSize;
6087  uint32_t newBlockSizeShift = 0;
6088  const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
6089 
6090  // Allocating blocks of other sizes is allowed only in default pools.
6091  // In custom pools block size is fixed.
6092  if(m_IsCustomPool == false)
6093  {
6094  // Allocate 1/8, 1/4, 1/2 as first blocks.
6095  const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
6096  for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
6097  {
6098  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6099  if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= vkMemReq.size * 2)
6100  {
6101  newBlockSize = smallerNewBlockSize;
6102  ++newBlockSizeShift;
6103  }
6104  else
6105  {
6106  break;
6107  }
6108  }
6109  }
6110 
6111  size_t newBlockIndex = 0;
6112  VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
6113  // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
6114  if(m_IsCustomPool == false)
6115  {
6116  while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
6117  {
6118  const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6119  if(smallerNewBlockSize >= vkMemReq.size)
6120  {
6121  newBlockSize = smallerNewBlockSize;
6122  ++newBlockSizeShift;
6123  res = CreateBlock(newBlockSize, &newBlockIndex);
6124  }
6125  else
6126  {
6127  break;
6128  }
6129  }
6130  }
6131 
6132  if(res == VK_SUCCESS)
6133  {
6134  VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
6135  VMA_ASSERT(pBlock->m_Metadata.GetSize() >= vkMemReq.size);
6136 
6137  if(mapped)
6138  {
6139  res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
6140  if(res != VK_SUCCESS)
6141  {
6142  return res;
6143  }
6144  }
6145 
6146  // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled.
6147  VmaAllocationRequest allocRequest;
6148  pBlock->m_Metadata.CreateFirstAllocationRequest(&allocRequest);
6149  *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6150  pBlock->m_Metadata.Alloc(allocRequest, suballocType, vkMemReq.size, *pAllocation);
6151  (*pAllocation)->InitBlockAllocation(
6152  hCurrentPool,
6153  pBlock,
6154  allocRequest.offset,
6155  vkMemReq.alignment,
6156  vkMemReq.size,
6157  suballocType,
6158  mapped,
6159  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6160  VMA_HEAVY_ASSERT(pBlock->Validate());
6161  VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
6162  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6163  return VK_SUCCESS;
6164  }
6165  }
6166 
6167  const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
6168 
6169  // 3. Try to allocate from existing blocks with making other allocations lost.
6170  if(canMakeOtherLost)
6171  {
6172  uint32_t tryIndex = 0;
6173  for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
6174  {
6175  VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
6176  VmaAllocationRequest bestRequest = {};
6177  VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
6178 
6179  // 1. Search existing allocations.
6180  // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6181  for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6182  {
6183  VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6184  VMA_ASSERT(pCurrBlock);
6185  VmaAllocationRequest currRequest = {};
6186  if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6187  currentFrameIndex,
6188  m_FrameInUseCount,
6189  m_BufferImageGranularity,
6190  vkMemReq.size,
6191  vkMemReq.alignment,
6192  suballocType,
6193  canMakeOtherLost,
6194  &currRequest))
6195  {
6196  const VkDeviceSize currRequestCost = currRequest.CalcCost();
6197  if(pBestRequestBlock == VMA_NULL ||
6198  currRequestCost < bestRequestCost)
6199  {
6200  pBestRequestBlock = pCurrBlock;
6201  bestRequest = currRequest;
6202  bestRequestCost = currRequestCost;
6203 
6204  if(bestRequestCost == 0)
6205  {
6206  break;
6207  }
6208  }
6209  }
6210  }
6211 
6212  if(pBestRequestBlock != VMA_NULL)
6213  {
6214  if(mapped)
6215  {
6216  VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
6217  if(res != VK_SUCCESS)
6218  {
6219  return res;
6220  }
6221  }
6222 
6223  if(pBestRequestBlock->m_Metadata.MakeRequestedAllocationsLost(
6224  currentFrameIndex,
6225  m_FrameInUseCount,
6226  &bestRequest))
6227  {
6228  // We no longer have an empty Allocation.
6229  if(pBestRequestBlock->m_Metadata.IsEmpty())
6230  {
6231  m_HasEmptyBlock = false;
6232  }
6233  // Allocate from this pBlock.
6234  *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6235  pBestRequestBlock->m_Metadata.Alloc(bestRequest, suballocType, vkMemReq.size, *pAllocation);
6236  (*pAllocation)->InitBlockAllocation(
6237  hCurrentPool,
6238  pBestRequestBlock,
6239  bestRequest.offset,
6240  vkMemReq.alignment,
6241  vkMemReq.size,
6242  suballocType,
6243  mapped,
6244  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6245  VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
6246  VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
6247  (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6248  return VK_SUCCESS;
6249  }
6250  // else: Some allocations must have been touched while we are here. Next try.
6251  }
6252  else
6253  {
6254  // Could not find place in any of the blocks - break outer loop.
6255  break;
6256  }
6257  }
6258  /* Maximum number of tries exceeded - a very unlike event when many other
6259  threads are simultaneously touching allocations making it impossible to make
6260  lost at the same time as we try to allocate. */
6261  if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
6262  {
6263  return VK_ERROR_TOO_MANY_OBJECTS;
6264  }
6265  }
6266 
6267  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
6268 }
6269 
6270 void VmaBlockVector::Free(
6271  VmaAllocation hAllocation)
6272 {
6273  VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
6274 
6275  // Scope for lock.
6276  {
6277  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6278 
6279  VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
6280 
6281  if(hAllocation->IsPersistentMap())
6282  {
6283  pBlock->m_Mapping.Unmap(m_hAllocator, pBlock->m_hMemory, 1);
6284  }
6285 
6286  pBlock->m_Metadata.Free(hAllocation);
6287  VMA_HEAVY_ASSERT(pBlock->Validate());
6288 
6289  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
6290 
6291  // pBlock became empty after this deallocation.
6292  if(pBlock->m_Metadata.IsEmpty())
6293  {
6294  // Already has empty Allocation. We don't want to have two, so delete this one.
6295  if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
6296  {
6297  pBlockToDelete = pBlock;
6298  Remove(pBlock);
6299  }
6300  // We now have first empty Allocation.
6301  else
6302  {
6303  m_HasEmptyBlock = true;
6304  }
6305  }
6306  // pBlock didn't become empty, but we have another empty block - find and free that one.
6307  // (This is optional, heuristics.)
6308  else if(m_HasEmptyBlock)
6309  {
6310  VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
6311  if(pLastBlock->m_Metadata.IsEmpty() && m_Blocks.size() > m_MinBlockCount)
6312  {
6313  pBlockToDelete = pLastBlock;
6314  m_Blocks.pop_back();
6315  m_HasEmptyBlock = false;
6316  }
6317  }
6318 
6319  IncrementallySortBlocks();
6320  }
6321 
6322  // Destruction of a free Allocation. Deferred until this point, outside of mutex
6323  // lock, for performance reason.
6324  if(pBlockToDelete != VMA_NULL)
6325  {
6326  VMA_DEBUG_LOG(" Deleted empty allocation");
6327  pBlockToDelete->Destroy(m_hAllocator);
6328  vma_delete(m_hAllocator, pBlockToDelete);
6329  }
6330 }
6331 
6332 size_t VmaBlockVector::CalcMaxBlockSize() const
6333 {
6334  size_t result = 0;
6335  for(size_t i = m_Blocks.size(); i--; )
6336  {
6337  result = VMA_MAX((uint64_t)result, (uint64_t)m_Blocks[i]->m_Metadata.GetSize());
6338  if(result >= m_PreferredBlockSize)
6339  {
6340  break;
6341  }
6342  }
6343  return result;
6344 }
6345 
6346 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
6347 {
6348  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6349  {
6350  if(m_Blocks[blockIndex] == pBlock)
6351  {
6352  VmaVectorRemove(m_Blocks, blockIndex);
6353  return;
6354  }
6355  }
6356  VMA_ASSERT(0);
6357 }
6358 
6359 void VmaBlockVector::IncrementallySortBlocks()
6360 {
6361  // Bubble sort only until first swap.
6362  for(size_t i = 1; i < m_Blocks.size(); ++i)
6363  {
6364  if(m_Blocks[i - 1]->m_Metadata.GetSumFreeSize() > m_Blocks[i]->m_Metadata.GetSumFreeSize())
6365  {
6366  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
6367  return;
6368  }
6369  }
6370 }
6371 
6372 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
6373 {
6374  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
6375  allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
6376  allocInfo.allocationSize = blockSize;
6377  VkDeviceMemory mem = VK_NULL_HANDLE;
6378  VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
6379  if(res < 0)
6380  {
6381  return res;
6382  }
6383 
6384  // New VkDeviceMemory successfully created.
6385 
6386  // Create new Allocation for it.
6387  VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
6388  pBlock->Init(
6389  m_MemoryTypeIndex,
6390  mem,
6391  allocInfo.allocationSize);
6392 
6393  m_Blocks.push_back(pBlock);
6394  if(pNewBlockIndex != VMA_NULL)
6395  {
6396  *pNewBlockIndex = m_Blocks.size() - 1;
6397  }
6398 
6399  return VK_SUCCESS;
6400 }
6401 
6402 #if VMA_STATS_STRING_ENABLED
6403 
6404 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
6405 {
6406  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6407 
6408  json.BeginObject();
6409 
6410  if(m_IsCustomPool)
6411  {
6412  json.WriteString("MemoryTypeIndex");
6413  json.WriteNumber(m_MemoryTypeIndex);
6414 
6415  json.WriteString("BlockSize");
6416  json.WriteNumber(m_PreferredBlockSize);
6417 
6418  json.WriteString("BlockCount");
6419  json.BeginObject(true);
6420  if(m_MinBlockCount > 0)
6421  {
6422  json.WriteString("Min");
6423  json.WriteNumber((uint64_t)m_MinBlockCount);
6424  }
6425  if(m_MaxBlockCount < SIZE_MAX)
6426  {
6427  json.WriteString("Max");
6428  json.WriteNumber((uint64_t)m_MaxBlockCount);
6429  }
6430  json.WriteString("Cur");
6431  json.WriteNumber((uint64_t)m_Blocks.size());
6432  json.EndObject();
6433 
6434  if(m_FrameInUseCount > 0)
6435  {
6436  json.WriteString("FrameInUseCount");
6437  json.WriteNumber(m_FrameInUseCount);
6438  }
6439  }
6440  else
6441  {
6442  json.WriteString("PreferredBlockSize");
6443  json.WriteNumber(m_PreferredBlockSize);
6444  }
6445 
6446  json.WriteString("Blocks");
6447  json.BeginArray();
6448  for(size_t i = 0; i < m_Blocks.size(); ++i)
6449  {
6450  m_Blocks[i]->m_Metadata.PrintDetailedMap(json);
6451  }
6452  json.EndArray();
6453 
6454  json.EndObject();
6455 }
6456 
6457 #endif // #if VMA_STATS_STRING_ENABLED
6458 
6459 VmaDefragmentator* VmaBlockVector::EnsureDefragmentator(
6460  VmaAllocator hAllocator,
6461  uint32_t currentFrameIndex)
6462 {
6463  if(m_pDefragmentator == VMA_NULL)
6464  {
6465  m_pDefragmentator = vma_new(m_hAllocator, VmaDefragmentator)(
6466  hAllocator,
6467  this,
6468  currentFrameIndex);
6469  }
6470 
6471  return m_pDefragmentator;
6472 }
6473 
6474 VkResult VmaBlockVector::Defragment(
6475  VmaDefragmentationStats* pDefragmentationStats,
6476  VkDeviceSize& maxBytesToMove,
6477  uint32_t& maxAllocationsToMove)
6478 {
6479  if(m_pDefragmentator == VMA_NULL)
6480  {
6481  return VK_SUCCESS;
6482  }
6483 
6484  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6485 
6486  // Defragment.
6487  VkResult result = m_pDefragmentator->Defragment(maxBytesToMove, maxAllocationsToMove);
6488 
6489  // Accumulate statistics.
6490  if(pDefragmentationStats != VMA_NULL)
6491  {
6492  const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved();
6493  const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved();
6494  pDefragmentationStats->bytesMoved += bytesMoved;
6495  pDefragmentationStats->allocationsMoved += allocationsMoved;
6496  VMA_ASSERT(bytesMoved <= maxBytesToMove);
6497  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
6498  maxBytesToMove -= bytesMoved;
6499  maxAllocationsToMove -= allocationsMoved;
6500  }
6501 
6502  // Free empty blocks.
6503  m_HasEmptyBlock = false;
6504  for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
6505  {
6506  VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
6507  if(pBlock->m_Metadata.IsEmpty())
6508  {
6509  if(m_Blocks.size() > m_MinBlockCount)
6510  {
6511  if(pDefragmentationStats != VMA_NULL)
6512  {
6513  ++pDefragmentationStats->deviceMemoryBlocksFreed;
6514  pDefragmentationStats->bytesFreed += pBlock->m_Metadata.GetSize();
6515  }
6516 
6517  VmaVectorRemove(m_Blocks, blockIndex);
6518  pBlock->Destroy(m_hAllocator);
6519  vma_delete(m_hAllocator, pBlock);
6520  }
6521  else
6522  {
6523  m_HasEmptyBlock = true;
6524  }
6525  }
6526  }
6527 
6528  return result;
6529 }
6530 
6531 void VmaBlockVector::DestroyDefragmentator()
6532 {
6533  if(m_pDefragmentator != VMA_NULL)
6534  {
6535  vma_delete(m_hAllocator, m_pDefragmentator);
6536  m_pDefragmentator = VMA_NULL;
6537  }
6538 }
6539 
6540 void VmaBlockVector::MakePoolAllocationsLost(
6541  uint32_t currentFrameIndex,
6542  size_t* pLostAllocationCount)
6543 {
6544  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6545  size_t lostAllocationCount = 0;
6546  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6547  {
6548  VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
6549  VMA_ASSERT(pBlock);
6550  lostAllocationCount += pBlock->m_Metadata.MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
6551  }
6552  if(pLostAllocationCount != VMA_NULL)
6553  {
6554  *pLostAllocationCount = lostAllocationCount;
6555  }
6556 }
6557 
6558 void VmaBlockVector::AddStats(VmaStats* pStats)
6559 {
6560  const uint32_t memTypeIndex = m_MemoryTypeIndex;
6561  const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
6562 
6563  VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6564 
6565  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6566  {
6567  const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
6568  VMA_ASSERT(pBlock);
6569  VMA_HEAVY_ASSERT(pBlock->Validate());
6570  VmaStatInfo allocationStatInfo;
6571  pBlock->m_Metadata.CalcAllocationStatInfo(allocationStatInfo);
6572  VmaAddStatInfo(pStats->total, allocationStatInfo);
6573  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
6574  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
6575  }
6576 }
6577 
6579 // VmaDefragmentator members definition
6580 
6581 VmaDefragmentator::VmaDefragmentator(
6582  VmaAllocator hAllocator,
6583  VmaBlockVector* pBlockVector,
6584  uint32_t currentFrameIndex) :
6585  m_hAllocator(hAllocator),
6586  m_pBlockVector(pBlockVector),
6587  m_CurrentFrameIndex(currentFrameIndex),
6588  m_BytesMoved(0),
6589  m_AllocationsMoved(0),
6590  m_Allocations(VmaStlAllocator<AllocationInfo>(hAllocator->GetAllocationCallbacks())),
6591  m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
6592 {
6593 }
6594 
6595 VmaDefragmentator::~VmaDefragmentator()
6596 {
6597  for(size_t i = m_Blocks.size(); i--; )
6598  {
6599  vma_delete(m_hAllocator, m_Blocks[i]);
6600  }
6601 }
6602 
6603 void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
6604 {
6605  AllocationInfo allocInfo;
6606  allocInfo.m_hAllocation = hAlloc;
6607  allocInfo.m_pChanged = pChanged;
6608  m_Allocations.push_back(allocInfo);
6609 }
6610 
6611 VkResult VmaDefragmentator::BlockInfo::EnsureMapping(VmaAllocator hAllocator, void** ppMappedData)
6612 {
6613  // It has already been mapped for defragmentation.
6614  if(m_pMappedDataForDefragmentation)
6615  {
6616  *ppMappedData = m_pMappedDataForDefragmentation;
6617  return VK_SUCCESS;
6618  }
6619 
6620  // It is originally mapped.
6621  if(m_pBlock->m_Mapping.GetMappedData())
6622  {
6623  *ppMappedData = m_pBlock->m_Mapping.GetMappedData();
6624  return VK_SUCCESS;
6625  }
6626 
6627  // Map on first usage.
6628  VkResult res = m_pBlock->Map(hAllocator, 1, &m_pMappedDataForDefragmentation);
6629  *ppMappedData = m_pMappedDataForDefragmentation;
6630  return res;
6631 }
6632 
6633 void VmaDefragmentator::BlockInfo::Unmap(VmaAllocator hAllocator)
6634 {
6635  if(m_pMappedDataForDefragmentation != VMA_NULL)
6636  {
6637  m_pBlock->Unmap(hAllocator, 1);
6638  }
6639 }
6640 
6641 VkResult VmaDefragmentator::DefragmentRound(
6642  VkDeviceSize maxBytesToMove,
6643  uint32_t maxAllocationsToMove)
6644 {
6645  if(m_Blocks.empty())
6646  {
6647  return VK_SUCCESS;
6648  }
6649 
6650  size_t srcBlockIndex = m_Blocks.size() - 1;
6651  size_t srcAllocIndex = SIZE_MAX;
6652  for(;;)
6653  {
6654  // 1. Find next allocation to move.
6655  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
6656  // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest.
6657  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
6658  {
6659  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
6660  {
6661  // Finished: no more allocations to process.
6662  if(srcBlockIndex == 0)
6663  {
6664  return VK_SUCCESS;
6665  }
6666  else
6667  {
6668  --srcBlockIndex;
6669  srcAllocIndex = SIZE_MAX;
6670  }
6671  }
6672  else
6673  {
6674  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
6675  }
6676  }
6677 
6678  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
6679  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
6680 
6681  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
6682  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
6683  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
6684  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
6685 
6686  // 2. Try to find new place for this allocation in preceding or current block.
6687  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
6688  {
6689  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
6690  VmaAllocationRequest dstAllocRequest;
6691  if(pDstBlockInfo->m_pBlock->m_Metadata.CreateAllocationRequest(
6692  m_CurrentFrameIndex,
6693  m_pBlockVector->GetFrameInUseCount(),
6694  m_pBlockVector->GetBufferImageGranularity(),
6695  size,
6696  alignment,
6697  suballocType,
6698  false, // canMakeOtherLost
6699  &dstAllocRequest) &&
6700  MoveMakesSense(
6701  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
6702  {
6703  VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
6704 
6705  // Reached limit on number of allocations or bytes to move.
6706  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
6707  (m_BytesMoved + size > maxBytesToMove))
6708  {
6709  return VK_INCOMPLETE;
6710  }
6711 
6712  void* pDstMappedData = VMA_NULL;
6713  VkResult res = pDstBlockInfo->EnsureMapping(m_hAllocator, &pDstMappedData);
6714  if(res != VK_SUCCESS)
6715  {
6716  return res;
6717  }
6718 
6719  void* pSrcMappedData = VMA_NULL;
6720  res = pSrcBlockInfo->EnsureMapping(m_hAllocator, &pSrcMappedData);
6721  if(res != VK_SUCCESS)
6722  {
6723  return res;
6724  }
6725 
6726  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
6727  memcpy(
6728  reinterpret_cast<char*>(pDstMappedData) + dstAllocRequest.offset,
6729  reinterpret_cast<char*>(pSrcMappedData) + srcOffset,
6730  static_cast<size_t>(size));
6731 
6732  pDstBlockInfo->m_pBlock->m_Metadata.Alloc(dstAllocRequest, suballocType, size, allocInfo.m_hAllocation);
6733  pSrcBlockInfo->m_pBlock->m_Metadata.FreeAtOffset(srcOffset);
6734 
6735  allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
6736 
6737  if(allocInfo.m_pChanged != VMA_NULL)
6738  {
6739  *allocInfo.m_pChanged = VK_TRUE;
6740  }
6741 
6742  ++m_AllocationsMoved;
6743  m_BytesMoved += size;
6744 
6745  VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
6746 
6747  break;
6748  }
6749  }
6750 
6751  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
6752 
6753  if(srcAllocIndex > 0)
6754  {
6755  --srcAllocIndex;
6756  }
6757  else
6758  {
6759  if(srcBlockIndex > 0)
6760  {
6761  --srcBlockIndex;
6762  srcAllocIndex = SIZE_MAX;
6763  }
6764  else
6765  {
6766  return VK_SUCCESS;
6767  }
6768  }
6769  }
6770 }
6771 
6772 VkResult VmaDefragmentator::Defragment(
6773  VkDeviceSize maxBytesToMove,
6774  uint32_t maxAllocationsToMove)
6775 {
6776  if(m_Allocations.empty())
6777  {
6778  return VK_SUCCESS;
6779  }
6780 
6781  // Create block info for each block.
6782  const size_t blockCount = m_pBlockVector->m_Blocks.size();
6783  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
6784  {
6785  BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
6786  pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
6787  m_Blocks.push_back(pBlockInfo);
6788  }
6789 
6790  // Sort them by m_pBlock pointer value.
6791  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
6792 
6793  // Move allocation infos from m_Allocations to appropriate m_Blocks[memTypeIndex].m_Allocations.
6794  for(size_t blockIndex = 0, allocCount = m_Allocations.size(); blockIndex < allocCount; ++blockIndex)
6795  {
6796  AllocationInfo& allocInfo = m_Allocations[blockIndex];
6797  // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
6798  if(allocInfo.m_hAllocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
6799  {
6800  VmaDeviceMemoryBlock* pBlock = allocInfo.m_hAllocation->GetBlock();
6801  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
6802  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
6803  {
6804  (*it)->m_Allocations.push_back(allocInfo);
6805  }
6806  else
6807  {
6808  VMA_ASSERT(0);
6809  }
6810  }
6811  }
6812  m_Allocations.clear();
6813 
6814  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
6815  {
6816  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
6817  pBlockInfo->CalcHasNonMovableAllocations();
6818  pBlockInfo->SortAllocationsBySizeDescecnding();
6819  }
6820 
6821  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
6822  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
6823 
6824  // Execute defragmentation rounds (the main part).
6825  VkResult result = VK_SUCCESS;
6826  for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round)
6827  {
6828  result = DefragmentRound(maxBytesToMove, maxAllocationsToMove);
6829  }
6830 
6831  // Unmap blocks that were mapped for defragmentation.
6832  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
6833  {
6834  m_Blocks[blockIndex]->Unmap(m_hAllocator);
6835  }
6836 
6837  return result;
6838 }
6839 
6840 bool VmaDefragmentator::MoveMakesSense(
6841  size_t dstBlockIndex, VkDeviceSize dstOffset,
6842  size_t srcBlockIndex, VkDeviceSize srcOffset)
6843 {
6844  if(dstBlockIndex < srcBlockIndex)
6845  {
6846  return true;
6847  }
6848  if(dstBlockIndex > srcBlockIndex)
6849  {
6850  return false;
6851  }
6852  if(dstOffset < srcOffset)
6853  {
6854  return true;
6855  }
6856  return false;
6857 }
6858 
6860 // VmaAllocator_T
6861 
6862 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
6863  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
6864  m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
6865  m_hDevice(pCreateInfo->device),
6866  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
6867  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
6868  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
6869  m_PreferredLargeHeapBlockSize(0),
6870  m_PhysicalDevice(pCreateInfo->physicalDevice),
6871  m_CurrentFrameIndex(0),
6872  m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks()))
6873 {
6874  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
6875 
6876  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
6877  memset(&m_MemProps, 0, sizeof(m_MemProps));
6878  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
6879 
6880  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
6881  memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
6882 
6883  for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
6884  {
6885  m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
6886  }
6887 
6888  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
6889  {
6890  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
6891  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
6892  }
6893 
6894  ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
6895 
6896  (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
6897  (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
6898 
6899  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
6900  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
6901 
6902  if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
6903  {
6904  for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
6905  {
6906  const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
6907  if(limit != VK_WHOLE_SIZE)
6908  {
6909  m_HeapSizeLimit[heapIndex] = limit;
6910  if(limit < m_MemProps.memoryHeaps[heapIndex].size)
6911  {
6912  m_MemProps.memoryHeaps[heapIndex].size = limit;
6913  }
6914  }
6915  }
6916  }
6917 
6918  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
6919  {
6920  const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
6921 
6922  m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
6923  this,
6924  memTypeIndex,
6925  preferredBlockSize,
6926  0,
6927  SIZE_MAX,
6928  GetBufferImageGranularity(),
6929  pCreateInfo->frameInUseCount,
6930  false); // isCustomPool
6931  // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
6932  // becase minBlockCount is 0.
6933  m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
6934  }
6935 }
6936 
6937 VmaAllocator_T::~VmaAllocator_T()
6938 {
6939  VMA_ASSERT(m_Pools.empty());
6940 
6941  for(size_t i = GetMemoryTypeCount(); i--; )
6942  {
6943  vma_delete(this, m_pDedicatedAllocations[i]);
6944  vma_delete(this, m_pBlockVectors[i]);
6945  }
6946 }
6947 
6948 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
6949 {
6950 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
6951  m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
6952  m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
6953  m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
6954  m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
6955  m_VulkanFunctions.vkMapMemory = &vkMapMemory;
6956  m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
6957  m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
6958  m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
6959  m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
6960  m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
6961  m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
6962  m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
6963  m_VulkanFunctions.vkCreateImage = &vkCreateImage;
6964  m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
6965  if(m_UseKhrDedicatedAllocation)
6966  {
6967  m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
6968  (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
6969  m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
6970  (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
6971  }
6972 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
6973 
6974 #define VMA_COPY_IF_NOT_NULL(funcName) \
6975  if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
6976 
6977  if(pVulkanFunctions != VMA_NULL)
6978  {
6979  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
6980  VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
6981  VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
6982  VMA_COPY_IF_NOT_NULL(vkFreeMemory);
6983  VMA_COPY_IF_NOT_NULL(vkMapMemory);
6984  VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
6985  VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
6986  VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
6987  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
6988  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
6989  VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
6990  VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
6991  VMA_COPY_IF_NOT_NULL(vkCreateImage);
6992  VMA_COPY_IF_NOT_NULL(vkDestroyImage);
6993  VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
6994  VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
6995  }
6996 
6997 #undef VMA_COPY_IF_NOT_NULL
6998 
6999  // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
7000  // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
7001  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
7002  VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
7003  VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
7004  VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
7005  VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
7006  VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
7007  VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
7008  VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
7009  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
7010  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
7011  VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
7012  VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
7013  VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
7014  VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
7015  if(m_UseKhrDedicatedAllocation)
7016  {
7017  VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
7018  VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
7019  }
7020 }
7021 
7022 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
7023 {
7024  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7025  const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
7026  const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
7027  return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
7028 }
7029 
7030 VkResult VmaAllocator_T::AllocateMemoryOfType(
7031  const VkMemoryRequirements& vkMemReq,
7032  bool dedicatedAllocation,
7033  VkBuffer dedicatedBuffer,
7034  VkImage dedicatedImage,
7035  const VmaAllocationCreateInfo& createInfo,
7036  uint32_t memTypeIndex,
7037  VmaSuballocationType suballocType,
7038  VmaAllocation* pAllocation)
7039 {
7040  VMA_ASSERT(pAllocation != VMA_NULL);
7041  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size);
7042 
7043  VmaAllocationCreateInfo finalCreateInfo = createInfo;
7044 
7045  // If memory type is not HOST_VISIBLE, disable MAPPED.
7046  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7047  (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
7048  {
7049  finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
7050  }
7051 
7052  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
7053  VMA_ASSERT(blockVector);
7054 
7055  const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
7056  bool preferDedicatedMemory =
7057  VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
7058  dedicatedAllocation ||
7059  // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
7060  vkMemReq.size > preferredBlockSize / 2;
7061 
7062  if(preferDedicatedMemory &&
7063  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
7064  finalCreateInfo.pool == VK_NULL_HANDLE)
7065  {
7066  finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
7067  }
7068 
7069  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
7070  {
7071  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7072  {
7073  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7074  }
7075  else
7076  {
7077  return AllocateDedicatedMemory(
7078  vkMemReq.size,
7079  suballocType,
7080  memTypeIndex,
7081  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7082  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7083  finalCreateInfo.pUserData,
7084  dedicatedBuffer,
7085  dedicatedImage,
7086  pAllocation);
7087  }
7088  }
7089  else
7090  {
7091  VkResult res = blockVector->Allocate(
7092  VK_NULL_HANDLE, // hCurrentPool
7093  m_CurrentFrameIndex.load(),
7094  vkMemReq,
7095  finalCreateInfo,
7096  suballocType,
7097  pAllocation);
7098  if(res == VK_SUCCESS)
7099  {
7100  return res;
7101  }
7102 
7103  // 5. Try dedicated memory.
7104  if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7105  {
7106  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7107  }
7108  else
7109  {
7110  res = AllocateDedicatedMemory(
7111  vkMemReq.size,
7112  suballocType,
7113  memTypeIndex,
7114  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7115  (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7116  finalCreateInfo.pUserData,
7117  dedicatedBuffer,
7118  dedicatedImage,
7119  pAllocation);
7120  if(res == VK_SUCCESS)
7121  {
7122  // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
7123  VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
7124  return VK_SUCCESS;
7125  }
7126  else
7127  {
7128  // Everything failed: Return error code.
7129  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
7130  return res;
7131  }
7132  }
7133  }
7134 }
7135 
7136 VkResult VmaAllocator_T::AllocateDedicatedMemory(
7137  VkDeviceSize size,
7138  VmaSuballocationType suballocType,
7139  uint32_t memTypeIndex,
7140  bool map,
7141  bool isUserDataString,
7142  void* pUserData,
7143  VkBuffer dedicatedBuffer,
7144  VkImage dedicatedImage,
7145  VmaAllocation* pAllocation)
7146 {
7147  VMA_ASSERT(pAllocation);
7148 
7149  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
7150  allocInfo.memoryTypeIndex = memTypeIndex;
7151  allocInfo.allocationSize = size;
7152 
7153  VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
7154  if(m_UseKhrDedicatedAllocation)
7155  {
7156  if(dedicatedBuffer != VK_NULL_HANDLE)
7157  {
7158  VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
7159  dedicatedAllocInfo.buffer = dedicatedBuffer;
7160  allocInfo.pNext = &dedicatedAllocInfo;
7161  }
7162  else if(dedicatedImage != VK_NULL_HANDLE)
7163  {
7164  dedicatedAllocInfo.image = dedicatedImage;
7165  allocInfo.pNext = &dedicatedAllocInfo;
7166  }
7167  }
7168 
7169  // Allocate VkDeviceMemory.
7170  VkDeviceMemory hMemory = VK_NULL_HANDLE;
7171  VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
7172  if(res < 0)
7173  {
7174  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
7175  return res;
7176  }
7177 
7178  void* pMappedData = VMA_NULL;
7179  if(map)
7180  {
7181  res = (*m_VulkanFunctions.vkMapMemory)(
7182  m_hDevice,
7183  hMemory,
7184  0,
7185  VK_WHOLE_SIZE,
7186  0,
7187  &pMappedData);
7188  if(res < 0)
7189  {
7190  VMA_DEBUG_LOG(" vkMapMemory FAILED");
7191  FreeVulkanMemory(memTypeIndex, size, hMemory);
7192  return res;
7193  }
7194  }
7195 
7196  *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
7197  (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
7198  (*pAllocation)->SetUserData(this, pUserData);
7199 
7200  // Register it in m_pDedicatedAllocations.
7201  {
7202  VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7203  AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
7204  VMA_ASSERT(pDedicatedAllocations);
7205  VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, *pAllocation);
7206  }
7207 
7208  VMA_DEBUG_LOG(" Allocated DedicatedMemory MemoryTypeIndex=#%u", memTypeIndex);
7209 
7210  return VK_SUCCESS;
7211 }
7212 
7213 void VmaAllocator_T::GetBufferMemoryRequirements(
7214  VkBuffer hBuffer,
7215  VkMemoryRequirements& memReq,
7216  bool& requiresDedicatedAllocation,
7217  bool& prefersDedicatedAllocation) const
7218 {
7219  if(m_UseKhrDedicatedAllocation)
7220  {
7221  VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
7222  memReqInfo.buffer = hBuffer;
7223 
7224  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7225 
7226  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7227  memReq2.pNext = &memDedicatedReq;
7228 
7229  (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7230 
7231  memReq = memReq2.memoryRequirements;
7232  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7233  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
7234  }
7235  else
7236  {
7237  (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
7238  requiresDedicatedAllocation = false;
7239  prefersDedicatedAllocation = false;
7240  }
7241 }
7242 
7243 void VmaAllocator_T::GetImageMemoryRequirements(
7244  VkImage hImage,
7245  VkMemoryRequirements& memReq,
7246  bool& requiresDedicatedAllocation,
7247  bool& prefersDedicatedAllocation) const
7248 {
7249  if(m_UseKhrDedicatedAllocation)
7250  {
7251  VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
7252  memReqInfo.image = hImage;
7253 
7254  VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7255 
7256  VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7257  memReq2.pNext = &memDedicatedReq;
7258 
7259  (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7260 
7261  memReq = memReq2.memoryRequirements;
7262  requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7263  prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
7264  }
7265  else
7266  {
7267  (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
7268  requiresDedicatedAllocation = false;
7269  prefersDedicatedAllocation = false;
7270  }
7271 }
7272 
7273 VkResult VmaAllocator_T::AllocateMemory(
7274  const VkMemoryRequirements& vkMemReq,
7275  bool requiresDedicatedAllocation,
7276  bool prefersDedicatedAllocation,
7277  VkBuffer dedicatedBuffer,
7278  VkImage dedicatedImage,
7279  const VmaAllocationCreateInfo& createInfo,
7280  VmaSuballocationType suballocType,
7281  VmaAllocation* pAllocation)
7282 {
7283  if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
7284  (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7285  {
7286  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
7287  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7288  }
7289  if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7290  (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
7291  {
7292  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
7293  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7294  }
7295  if(requiresDedicatedAllocation)
7296  {
7297  if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7298  {
7299  VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
7300  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7301  }
7302  if(createInfo.pool != VK_NULL_HANDLE)
7303  {
7304  VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
7305  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7306  }
7307  }
7308  if((createInfo.pool != VK_NULL_HANDLE) &&
7309  ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
7310  {
7311  VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
7312  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7313  }
7314 
7315  if(createInfo.pool != VK_NULL_HANDLE)
7316  {
7317  return createInfo.pool->m_BlockVector.Allocate(
7318  createInfo.pool,
7319  m_CurrentFrameIndex.load(),
7320  vkMemReq,
7321  createInfo,
7322  suballocType,
7323  pAllocation);
7324  }
7325  else
7326  {
7327  // Bit mask of memory Vulkan types acceptable for this allocation.
7328  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
7329  uint32_t memTypeIndex = UINT32_MAX;
7330  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7331  if(res == VK_SUCCESS)
7332  {
7333  res = AllocateMemoryOfType(
7334  vkMemReq,
7335  requiresDedicatedAllocation || prefersDedicatedAllocation,
7336  dedicatedBuffer,
7337  dedicatedImage,
7338  createInfo,
7339  memTypeIndex,
7340  suballocType,
7341  pAllocation);
7342  // Succeeded on first try.
7343  if(res == VK_SUCCESS)
7344  {
7345  return res;
7346  }
7347  // Allocation from this memory type failed. Try other compatible memory types.
7348  else
7349  {
7350  for(;;)
7351  {
7352  // Remove old memTypeIndex from list of possibilities.
7353  memoryTypeBits &= ~(1u << memTypeIndex);
7354  // Find alternative memTypeIndex.
7355  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7356  if(res == VK_SUCCESS)
7357  {
7358  res = AllocateMemoryOfType(
7359  vkMemReq,
7360  requiresDedicatedAllocation || prefersDedicatedAllocation,
7361  dedicatedBuffer,
7362  dedicatedImage,
7363  createInfo,
7364  memTypeIndex,
7365  suballocType,
7366  pAllocation);
7367  // Allocation from this alternative memory type succeeded.
7368  if(res == VK_SUCCESS)
7369  {
7370  return res;
7371  }
7372  // else: Allocation from this memory type failed. Try next one - next loop iteration.
7373  }
7374  // No other matching memory type index could be found.
7375  else
7376  {
7377  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
7378  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7379  }
7380  }
7381  }
7382  }
7383  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
7384  else
7385  return res;
7386  }
7387 }
7388 
7389 void VmaAllocator_T::FreeMemory(const VmaAllocation allocation)
7390 {
7391  VMA_ASSERT(allocation);
7392 
7393  if(allocation->CanBecomeLost() == false ||
7394  allocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
7395  {
7396  switch(allocation->GetType())
7397  {
7398  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
7399  {
7400  VmaBlockVector* pBlockVector = VMA_NULL;
7401  VmaPool hPool = allocation->GetPool();
7402  if(hPool != VK_NULL_HANDLE)
7403  {
7404  pBlockVector = &hPool->m_BlockVector;
7405  }
7406  else
7407  {
7408  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
7409  pBlockVector = m_pBlockVectors[memTypeIndex];
7410  }
7411  pBlockVector->Free(allocation);
7412  }
7413  break;
7414  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
7415  FreeDedicatedMemory(allocation);
7416  break;
7417  default:
7418  VMA_ASSERT(0);
7419  }
7420  }
7421 
7422  allocation->SetUserData(this, VMA_NULL);
7423  vma_delete(this, allocation);
7424 }
7425 
7426 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
7427 {
7428  // Initialize.
7429  InitStatInfo(pStats->total);
7430  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
7431  InitStatInfo(pStats->memoryType[i]);
7432  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
7433  InitStatInfo(pStats->memoryHeap[i]);
7434 
7435  // Process default pools.
7436  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7437  {
7438  VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
7439  VMA_ASSERT(pBlockVector);
7440  pBlockVector->AddStats(pStats);
7441  }
7442 
7443  // Process custom pools.
7444  {
7445  VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7446  for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
7447  {
7448  m_Pools[poolIndex]->GetBlockVector().AddStats(pStats);
7449  }
7450  }
7451 
7452  // Process dedicated allocations.
7453  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7454  {
7455  const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7456  VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7457  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
7458  VMA_ASSERT(pDedicatedAllocVector);
7459  for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
7460  {
7461  VmaStatInfo allocationStatInfo;
7462  (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
7463  VmaAddStatInfo(pStats->total, allocationStatInfo);
7464  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
7465  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
7466  }
7467  }
7468 
7469  // Postprocess.
7470  VmaPostprocessCalcStatInfo(pStats->total);
7471  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
7472  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
7473  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
7474  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
7475 }
7476 
7477 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
7478 
7479 VkResult VmaAllocator_T::Defragment(
7480  VmaAllocation* pAllocations,
7481  size_t allocationCount,
7482  VkBool32* pAllocationsChanged,
7483  const VmaDefragmentationInfo* pDefragmentationInfo,
7484  VmaDefragmentationStats* pDefragmentationStats)
7485 {
7486  if(pAllocationsChanged != VMA_NULL)
7487  {
7488  memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged));
7489  }
7490  if(pDefragmentationStats != VMA_NULL)
7491  {
7492  memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats));
7493  }
7494 
7495  const uint32_t currentFrameIndex = m_CurrentFrameIndex.load();
7496 
7497  VmaMutexLock poolsLock(m_PoolsMutex, m_UseMutex);
7498 
7499  const size_t poolCount = m_Pools.size();
7500 
7501  // Dispatch pAllocations among defragmentators. Create them in BlockVectors when necessary.
7502  for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
7503  {
7504  VmaAllocation hAlloc = pAllocations[allocIndex];
7505  VMA_ASSERT(hAlloc);
7506  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
7507  // DedicatedAlloc cannot be defragmented.
7508  if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
7509  // Only HOST_VISIBLE memory types can be defragmented.
7510  ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) &&
7511  // Lost allocation cannot be defragmented.
7512  (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
7513  {
7514  VmaBlockVector* pAllocBlockVector = VMA_NULL;
7515 
7516  const VmaPool hAllocPool = hAlloc->GetPool();
7517  // This allocation belongs to custom pool.
7518  if(hAllocPool != VK_NULL_HANDLE)
7519  {
7520  pAllocBlockVector = &hAllocPool->GetBlockVector();
7521  }
7522  // This allocation belongs to general pool.
7523  else
7524  {
7525  pAllocBlockVector = m_pBlockVectors[memTypeIndex];
7526  }
7527 
7528  VmaDefragmentator* const pDefragmentator = pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex);
7529 
7530  VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
7531  &pAllocationsChanged[allocIndex] : VMA_NULL;
7532  pDefragmentator->AddAllocation(hAlloc, pChanged);
7533  }
7534  }
7535 
7536  VkResult result = VK_SUCCESS;
7537 
7538  // ======== Main processing.
7539 
7540  VkDeviceSize maxBytesToMove = SIZE_MAX;
7541  uint32_t maxAllocationsToMove = UINT32_MAX;
7542  if(pDefragmentationInfo != VMA_NULL)
7543  {
7544  maxBytesToMove = pDefragmentationInfo->maxBytesToMove;
7545  maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
7546  }
7547 
7548  // Process standard memory.
7549  for(uint32_t memTypeIndex = 0;
7550  (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS);
7551  ++memTypeIndex)
7552  {
7553  // Only HOST_VISIBLE memory types can be defragmented.
7554  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
7555  {
7556  result = m_pBlockVectors[memTypeIndex]->Defragment(
7557  pDefragmentationStats,
7558  maxBytesToMove,
7559  maxAllocationsToMove);
7560  }
7561  }
7562 
7563  // Process custom pools.
7564  for(size_t poolIndex = 0; (poolIndex < poolCount) && (result == VK_SUCCESS); ++poolIndex)
7565  {
7566  result = m_Pools[poolIndex]->GetBlockVector().Defragment(
7567  pDefragmentationStats,
7568  maxBytesToMove,
7569  maxAllocationsToMove);
7570  }
7571 
7572  // ======== Destroy defragmentators.
7573 
7574  // Process custom pools.
7575  for(size_t poolIndex = poolCount; poolIndex--; )
7576  {
7577  m_Pools[poolIndex]->GetBlockVector().DestroyDefragmentator();
7578  }
7579 
7580  // Process standard memory.
7581  for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
7582  {
7583  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
7584  {
7585  m_pBlockVectors[memTypeIndex]->DestroyDefragmentator();
7586  }
7587  }
7588 
7589  return result;
7590 }
7591 
7592 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
7593 {
7594  if(hAllocation->CanBecomeLost())
7595  {
7596  /*
7597  Warning: This is a carefully designed algorithm.
7598  Do not modify unless you really know what you're doing :)
7599  */
7600  uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
7601  uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
7602  for(;;)
7603  {
7604  if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7605  {
7606  pAllocationInfo->memoryType = UINT32_MAX;
7607  pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
7608  pAllocationInfo->offset = 0;
7609  pAllocationInfo->size = hAllocation->GetSize();
7610  pAllocationInfo->pMappedData = VMA_NULL;
7611  pAllocationInfo->pUserData = hAllocation->GetUserData();
7612  return;
7613  }
7614  else if(localLastUseFrameIndex == localCurrFrameIndex)
7615  {
7616  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
7617  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
7618  pAllocationInfo->offset = hAllocation->GetOffset();
7619  pAllocationInfo->size = hAllocation->GetSize();
7620  pAllocationInfo->pMappedData = VMA_NULL;
7621  pAllocationInfo->pUserData = hAllocation->GetUserData();
7622  return;
7623  }
7624  else // Last use time earlier than current time.
7625  {
7626  if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
7627  {
7628  localLastUseFrameIndex = localCurrFrameIndex;
7629  }
7630  }
7631  }
7632  }
7633  else
7634  {
7635  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
7636  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
7637  pAllocationInfo->offset = hAllocation->GetOffset();
7638  pAllocationInfo->size = hAllocation->GetSize();
7639  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
7640  pAllocationInfo->pUserData = hAllocation->GetUserData();
7641  }
7642 }
7643 
7644 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
7645 {
7646  VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u", pCreateInfo->memoryTypeIndex);
7647 
7648  VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
7649 
7650  if(newCreateInfo.maxBlockCount == 0)
7651  {
7652  newCreateInfo.maxBlockCount = SIZE_MAX;
7653  }
7654  if(newCreateInfo.blockSize == 0)
7655  {
7656  newCreateInfo.blockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
7657  }
7658 
7659  *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo);
7660 
7661  VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
7662  if(res != VK_SUCCESS)
7663  {
7664  vma_delete(this, *pPool);
7665  *pPool = VMA_NULL;
7666  return res;
7667  }
7668 
7669  // Add to m_Pools.
7670  {
7671  VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7672  VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
7673  }
7674 
7675  return VK_SUCCESS;
7676 }
7677 
7678 void VmaAllocator_T::DestroyPool(VmaPool pool)
7679 {
7680  // Remove from m_Pools.
7681  {
7682  VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7683  bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
7684  VMA_ASSERT(success && "Pool not found in Allocator.");
7685  }
7686 
7687  vma_delete(this, pool);
7688 }
7689 
7690 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
7691 {
7692  pool->m_BlockVector.GetPoolStats(pPoolStats);
7693 }
7694 
7695 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
7696 {
7697  m_CurrentFrameIndex.store(frameIndex);
7698 }
7699 
7700 void VmaAllocator_T::MakePoolAllocationsLost(
7701  VmaPool hPool,
7702  size_t* pLostAllocationCount)
7703 {
7704  hPool->m_BlockVector.MakePoolAllocationsLost(
7705  m_CurrentFrameIndex.load(),
7706  pLostAllocationCount);
7707 }
7708 
7709 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
7710 {
7711  *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
7712  (*pAllocation)->InitLost();
7713 }
7714 
7715 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
7716 {
7717  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
7718 
7719  VkResult res;
7720  if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
7721  {
7722  VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
7723  if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
7724  {
7725  res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
7726  if(res == VK_SUCCESS)
7727  {
7728  m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
7729  }
7730  }
7731  else
7732  {
7733  res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
7734  }
7735  }
7736  else
7737  {
7738  res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
7739  }
7740 
7741  if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
7742  {
7743  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
7744  }
7745 
7746  return res;
7747 }
7748 
7749 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
7750 {
7751  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
7752  {
7753  (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
7754  }
7755 
7756  (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
7757 
7758  const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
7759  if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
7760  {
7761  VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
7762  m_HeapSizeLimit[heapIndex] += size;
7763  }
7764 }
7765 
7766 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
7767 {
7768  if(hAllocation->CanBecomeLost())
7769  {
7770  return VK_ERROR_MEMORY_MAP_FAILED;
7771  }
7772 
7773  switch(hAllocation->GetType())
7774  {
7775  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
7776  {
7777  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
7778  char *pBytes = VMA_NULL;
7779  VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
7780  if(res == VK_SUCCESS)
7781  {
7782  *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
7783  hAllocation->BlockAllocMap();
7784  }
7785  return res;
7786  }
7787  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
7788  return hAllocation->DedicatedAllocMap(this, ppData);
7789  default:
7790  VMA_ASSERT(0);
7791  return VK_ERROR_MEMORY_MAP_FAILED;
7792  }
7793 }
7794 
7795 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
7796 {
7797  switch(hAllocation->GetType())
7798  {
7799  case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
7800  {
7801  VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
7802  hAllocation->BlockAllocUnmap();
7803  pBlock->Unmap(this, 1);
7804  }
7805  break;
7806  case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
7807  hAllocation->DedicatedAllocUnmap(this);
7808  break;
7809  default:
7810  VMA_ASSERT(0);
7811  }
7812 }
7813 
7814 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
7815 {
7816  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
7817 
7818  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
7819  {
7820  VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7821  AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
7822  VMA_ASSERT(pDedicatedAllocations);
7823  bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
7824  VMA_ASSERT(success);
7825  }
7826 
7827  VkDeviceMemory hMemory = allocation->GetMemory();
7828 
7829  if(allocation->GetMappedData() != VMA_NULL)
7830  {
7831  (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
7832  }
7833 
7834  FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
7835 
7836  VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
7837 }
7838 
7839 #if VMA_STATS_STRING_ENABLED
7840 
7841 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
7842 {
7843  bool dedicatedAllocationsStarted = false;
7844  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7845  {
7846  VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7847  AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
7848  VMA_ASSERT(pDedicatedAllocVector);
7849  if(pDedicatedAllocVector->empty() == false)
7850  {
7851  if(dedicatedAllocationsStarted == false)
7852  {
7853  dedicatedAllocationsStarted = true;
7854  json.WriteString("DedicatedAllocations");
7855  json.BeginObject();
7856  }
7857 
7858  json.BeginString("Type ");
7859  json.ContinueString(memTypeIndex);
7860  json.EndString();
7861 
7862  json.BeginArray();
7863 
7864  for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
7865  {
7866  const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
7867  json.BeginObject(true);
7868 
7869  json.WriteString("Type");
7870  json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]);
7871 
7872  json.WriteString("Size");
7873  json.WriteNumber(hAlloc->GetSize());
7874 
7875  const void* pUserData = hAlloc->GetUserData();
7876  if(pUserData != VMA_NULL)
7877  {
7878  json.WriteString("UserData");
7879  if(hAlloc->IsUserDataString())
7880  {
7881  json.WriteString((const char*)pUserData);
7882  }
7883  else
7884  {
7885  json.BeginString();
7886  json.ContinueString_Pointer(pUserData);
7887  json.EndString();
7888  }
7889  }
7890 
7891  json.EndObject();
7892  }
7893 
7894  json.EndArray();
7895  }
7896  }
7897  if(dedicatedAllocationsStarted)
7898  {
7899  json.EndObject();
7900  }
7901 
7902  {
7903  bool allocationsStarted = false;
7904  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7905  {
7906  if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
7907  {
7908  if(allocationsStarted == false)
7909  {
7910  allocationsStarted = true;
7911  json.WriteString("DefaultPools");
7912  json.BeginObject();
7913  }
7914 
7915  json.BeginString("Type ");
7916  json.ContinueString(memTypeIndex);
7917  json.EndString();
7918 
7919  m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
7920  }
7921  }
7922  if(allocationsStarted)
7923  {
7924  json.EndObject();
7925  }
7926  }
7927 
7928  {
7929  VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7930  const size_t poolCount = m_Pools.size();
7931  if(poolCount > 0)
7932  {
7933  json.WriteString("Pools");
7934  json.BeginArray();
7935  for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
7936  {
7937  m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
7938  }
7939  json.EndArray();
7940  }
7941  }
7942 }
7943 
7944 #endif // #if VMA_STATS_STRING_ENABLED
7945 
7946 static VkResult AllocateMemoryForImage(
7947  VmaAllocator allocator,
7948  VkImage image,
7949  const VmaAllocationCreateInfo* pAllocationCreateInfo,
7950  VmaSuballocationType suballocType,
7951  VmaAllocation* pAllocation)
7952 {
7953  VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pAllocationCreateInfo && pAllocation);
7954 
7955  VkMemoryRequirements vkMemReq = {};
7956  bool requiresDedicatedAllocation = false;
7957  bool prefersDedicatedAllocation = false;
7958  allocator->GetImageMemoryRequirements(image, vkMemReq,
7959  requiresDedicatedAllocation, prefersDedicatedAllocation);
7960 
7961  return allocator->AllocateMemory(
7962  vkMemReq,
7963  requiresDedicatedAllocation,
7964  prefersDedicatedAllocation,
7965  VK_NULL_HANDLE, // dedicatedBuffer
7966  image, // dedicatedImage
7967  *pAllocationCreateInfo,
7968  suballocType,
7969  pAllocation);
7970 }
7971 
7973 // Public interface
7974 
7975 VkResult vmaCreateAllocator(
7976  const VmaAllocatorCreateInfo* pCreateInfo,
7977  VmaAllocator* pAllocator)
7978 {
7979  VMA_ASSERT(pCreateInfo && pAllocator);
7980  VMA_DEBUG_LOG("vmaCreateAllocator");
7981  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
7982  return VK_SUCCESS;
7983 }
7984 
7985 void vmaDestroyAllocator(
7986  VmaAllocator allocator)
7987 {
7988  if(allocator != VK_NULL_HANDLE)
7989  {
7990  VMA_DEBUG_LOG("vmaDestroyAllocator");
7991  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
7992  vma_delete(&allocationCallbacks, allocator);
7993  }
7994 }
7995 
7996 void vmaGetPhysicalDeviceProperties(
7997  VmaAllocator allocator,
7998  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
7999 {
8000  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
8001  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
8002 }
8003 
8004 void vmaGetMemoryProperties(
8005  VmaAllocator allocator,
8006  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
8007 {
8008  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
8009  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
8010 }
8011 
8012 void vmaGetMemoryTypeProperties(
8013  VmaAllocator allocator,
8014  uint32_t memoryTypeIndex,
8015  VkMemoryPropertyFlags* pFlags)
8016 {
8017  VMA_ASSERT(allocator && pFlags);
8018  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
8019  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
8020 }
8021 
8022 void vmaSetCurrentFrameIndex(
8023  VmaAllocator allocator,
8024  uint32_t frameIndex)
8025 {
8026  VMA_ASSERT(allocator);
8027  VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
8028 
8029  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8030 
8031  allocator->SetCurrentFrameIndex(frameIndex);
8032 }
8033 
8034 void vmaCalculateStats(
8035  VmaAllocator allocator,
8036  VmaStats* pStats)
8037 {
8038  VMA_ASSERT(allocator && pStats);
8039  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8040  allocator->CalculateStats(pStats);
8041 }
8042 
8043 #if VMA_STATS_STRING_ENABLED
8044 
8045 void vmaBuildStatsString(
8046  VmaAllocator allocator,
8047  char** ppStatsString,
8048  VkBool32 detailedMap)
8049 {
8050  VMA_ASSERT(allocator && ppStatsString);
8051  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8052 
8053  VmaStringBuilder sb(allocator);
8054  {
8055  VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
8056  json.BeginObject();
8057 
8058  VmaStats stats;
8059  allocator->CalculateStats(&stats);
8060 
8061  json.WriteString("Total");
8062  VmaPrintStatInfo(json, stats.total);
8063 
8064  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
8065  {
8066  json.BeginString("Heap ");
8067  json.ContinueString(heapIndex);
8068  json.EndString();
8069  json.BeginObject();
8070 
8071  json.WriteString("Size");
8072  json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
8073 
8074  json.WriteString("Flags");
8075  json.BeginArray(true);
8076  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
8077  {
8078  json.WriteString("DEVICE_LOCAL");
8079  }
8080  json.EndArray();
8081 
8082  if(stats.memoryHeap[heapIndex].blockCount > 0)
8083  {
8084  json.WriteString("Stats");
8085  VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
8086  }
8087 
8088  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
8089  {
8090  if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
8091  {
8092  json.BeginString("Type ");
8093  json.ContinueString(typeIndex);
8094  json.EndString();
8095 
8096  json.BeginObject();
8097 
8098  json.WriteString("Flags");
8099  json.BeginArray(true);
8100  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
8101  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
8102  {
8103  json.WriteString("DEVICE_LOCAL");
8104  }
8105  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8106  {
8107  json.WriteString("HOST_VISIBLE");
8108  }
8109  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
8110  {
8111  json.WriteString("HOST_COHERENT");
8112  }
8113  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
8114  {
8115  json.WriteString("HOST_CACHED");
8116  }
8117  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
8118  {
8119  json.WriteString("LAZILY_ALLOCATED");
8120  }
8121  json.EndArray();
8122 
8123  if(stats.memoryType[typeIndex].blockCount > 0)
8124  {
8125  json.WriteString("Stats");
8126  VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
8127  }
8128 
8129  json.EndObject();
8130  }
8131  }
8132 
8133  json.EndObject();
8134  }
8135  if(detailedMap == VK_TRUE)
8136  {
8137  allocator->PrintDetailedMap(json);
8138  }
8139 
8140  json.EndObject();
8141  }
8142 
8143  const size_t len = sb.GetLength();
8144  char* const pChars = vma_new_array(allocator, char, len + 1);
8145  if(len > 0)
8146  {
8147  memcpy(pChars, sb.GetData(), len);
8148  }
8149  pChars[len] = '\0';
8150  *ppStatsString = pChars;
8151 }
8152 
8153 void vmaFreeStatsString(
8154  VmaAllocator allocator,
8155  char* pStatsString)
8156 {
8157  if(pStatsString != VMA_NULL)
8158  {
8159  VMA_ASSERT(allocator);
8160  size_t len = strlen(pStatsString);
8161  vma_delete_array(allocator, pStatsString, len + 1);
8162  }
8163 }
8164 
8165 #endif // #if VMA_STATS_STRING_ENABLED
8166 
8167 /*
8168 This function is not protected by any mutex because it just reads immutable data.
8169 */
8170 VkResult vmaFindMemoryTypeIndex(
8171  VmaAllocator allocator,
8172  uint32_t memoryTypeBits,
8173  const VmaAllocationCreateInfo* pAllocationCreateInfo,
8174  uint32_t* pMemoryTypeIndex)
8175 {
8176  VMA_ASSERT(allocator != VK_NULL_HANDLE);
8177  VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8178  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8179 
8180  if(pAllocationCreateInfo->memoryTypeBits != 0)
8181  {
8182  memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
8183  }
8184 
8185  uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
8186  uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
8187 
8188  // Convert usage to requiredFlags and preferredFlags.
8189  switch(pAllocationCreateInfo->usage)
8190  {
8191  case VMA_MEMORY_USAGE_UNKNOWN:
8192  break;
8193  case VMA_MEMORY_USAGE_GPU_ONLY:
8194  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8195  break;
8196  case VMA_MEMORY_USAGE_CPU_ONLY:
8197  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
8198  break;
8199  case VMA_MEMORY_USAGE_CPU_TO_GPU:
8200  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8201  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8202  break;
8203  case VMA_MEMORY_USAGE_GPU_TO_CPU:
8204  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8205  preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
8206  break;
8207  default:
8208  break;
8209  }
8210 
8211  *pMemoryTypeIndex = UINT32_MAX;
8212  uint32_t minCost = UINT32_MAX;
8213  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
8214  memTypeIndex < allocator->GetMemoryTypeCount();
8215  ++memTypeIndex, memTypeBit <<= 1)
8216  {
8217  // This memory type is acceptable according to memoryTypeBits bitmask.
8218  if((memTypeBit & memoryTypeBits) != 0)
8219  {
8220  const VkMemoryPropertyFlags currFlags =
8221  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
8222  // This memory type contains requiredFlags.
8223  if((requiredFlags & ~currFlags) == 0)
8224  {
8225  // Calculate cost as number of bits from preferredFlags not present in this memory type.
8226  uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
8227  // Remember memory type with lowest cost.
8228  if(currCost < minCost)
8229  {
8230  *pMemoryTypeIndex = memTypeIndex;
8231  if(currCost == 0)
8232  {
8233  return VK_SUCCESS;
8234  }
8235  minCost = currCost;
8236  }
8237  }
8238  }
8239  }
8240  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
8241 }
8242 
8243 VkResult vmaCreatePool(
8244  VmaAllocator allocator,
8245  const VmaPoolCreateInfo* pCreateInfo,
8246  VmaPool* pPool)
8247 {
8248  VMA_ASSERT(allocator && pCreateInfo && pPool);
8249 
8250  VMA_DEBUG_LOG("vmaCreatePool");
8251 
8252  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8253 
8254  return allocator->CreatePool(pCreateInfo, pPool);
8255 }
8256 
8257 void vmaDestroyPool(
8258  VmaAllocator allocator,
8259  VmaPool pool)
8260 {
8261  VMA_ASSERT(allocator);
8262 
8263  if(pool == VK_NULL_HANDLE)
8264  {
8265  return;
8266  }
8267 
8268  VMA_DEBUG_LOG("vmaDestroyPool");
8269 
8270  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8271 
8272  allocator->DestroyPool(pool);
8273 }
8274 
8275 void vmaGetPoolStats(
8276  VmaAllocator allocator,
8277  VmaPool pool,
8278  VmaPoolStats* pPoolStats)
8279 {
8280  VMA_ASSERT(allocator && pool && pPoolStats);
8281 
8282  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8283 
8284  allocator->GetPoolStats(pool, pPoolStats);
8285 }
8286 
8287 void vmaMakePoolAllocationsLost(
8288  VmaAllocator allocator,
8289  VmaPool pool,
8290  size_t* pLostAllocationCount)
8291 {
8292  VMA_ASSERT(allocator && pool);
8293 
8294  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8295 
8296  allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
8297 }
8298 
8299 VkResult vmaAllocateMemory(
8300  VmaAllocator allocator,
8301  const VkMemoryRequirements* pVkMemoryRequirements,
8302  const VmaAllocationCreateInfo* pCreateInfo,
8303  VmaAllocation* pAllocation,
8304  VmaAllocationInfo* pAllocationInfo)
8305 {
8306  VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
8307 
8308  VMA_DEBUG_LOG("vmaAllocateMemory");
8309 
8310  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8311 
8312  VkResult result = allocator->AllocateMemory(
8313  *pVkMemoryRequirements,
8314  false, // requiresDedicatedAllocation
8315  false, // prefersDedicatedAllocation
8316  VK_NULL_HANDLE, // dedicatedBuffer
8317  VK_NULL_HANDLE, // dedicatedImage
8318  *pCreateInfo,
8319  VMA_SUBALLOCATION_TYPE_UNKNOWN,
8320  pAllocation);
8321 
8322  if(pAllocationInfo && result == VK_SUCCESS)
8323  {
8324  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8325  }
8326 
8327  return result;
8328 }
8329 
8330 VkResult vmaAllocateMemoryForBuffer(
8331  VmaAllocator allocator,
8332  VkBuffer buffer,
8333  const VmaAllocationCreateInfo* pCreateInfo,
8334  VmaAllocation* pAllocation,
8335  VmaAllocationInfo* pAllocationInfo)
8336 {
8337  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
8338 
8339  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
8340 
8341  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8342 
8343  VkMemoryRequirements vkMemReq = {};
8344  bool requiresDedicatedAllocation = false;
8345  bool prefersDedicatedAllocation = false;
8346  allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
8347  requiresDedicatedAllocation,
8348  prefersDedicatedAllocation);
8349 
8350  VkResult result = allocator->AllocateMemory(
8351  vkMemReq,
8352  requiresDedicatedAllocation,
8353  prefersDedicatedAllocation,
8354  buffer, // dedicatedBuffer
8355  VK_NULL_HANDLE, // dedicatedImage
8356  *pCreateInfo,
8357  VMA_SUBALLOCATION_TYPE_BUFFER,
8358  pAllocation);
8359 
8360  if(pAllocationInfo && result == VK_SUCCESS)
8361  {
8362  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8363  }
8364 
8365  return result;
8366 }
8367 
8368 VkResult vmaAllocateMemoryForImage(
8369  VmaAllocator allocator,
8370  VkImage image,
8371  const VmaAllocationCreateInfo* pCreateInfo,
8372  VmaAllocation* pAllocation,
8373  VmaAllocationInfo* pAllocationInfo)
8374 {
8375  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
8376 
8377  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
8378 
8379  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8380 
8381  VkResult result = AllocateMemoryForImage(
8382  allocator,
8383  image,
8384  pCreateInfo,
8385  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
8386  pAllocation);
8387 
8388  if(pAllocationInfo && result == VK_SUCCESS)
8389  {
8390  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8391  }
8392 
8393  return result;
8394 }
8395 
8396 void vmaFreeMemory(
8397  VmaAllocator allocator,
8398  VmaAllocation allocation)
8399 {
8400  VMA_ASSERT(allocator && allocation);
8401 
8402  VMA_DEBUG_LOG("vmaFreeMemory");
8403 
8404  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8405 
8406  allocator->FreeMemory(allocation);
8407 }
8408 
8409 void vmaGetAllocationInfo(
8410  VmaAllocator allocator,
8411  VmaAllocation allocation,
8412  VmaAllocationInfo* pAllocationInfo)
8413 {
8414  VMA_ASSERT(allocator && allocation && pAllocationInfo);
8415 
8416  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8417 
8418  allocator->GetAllocationInfo(allocation, pAllocationInfo);
8419 }
8420 
8421 void vmaSetAllocationUserData(
8422  VmaAllocator allocator,
8423  VmaAllocation allocation,
8424  void* pUserData)
8425 {
8426  VMA_ASSERT(allocator && allocation);
8427 
8428  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8429 
8430  allocation->SetUserData(allocator, pUserData);
8431 }
8432 
8433 void vmaCreateLostAllocation(
8434  VmaAllocator allocator,
8435  VmaAllocation* pAllocation)
8436 {
8437  VMA_ASSERT(allocator && pAllocation);
8438 
8439  VMA_DEBUG_GLOBAL_MUTEX_LOCK;
8440 
8441  allocator->CreateLostAllocation(pAllocation);
8442 }
8443 
8444 VkResult vmaMapMemory(
8445  VmaAllocator allocator,
8446  VmaAllocation allocation,
8447  void** ppData)
8448 {
8449  VMA_ASSERT(allocator && allocation && ppData);
8450 
8451  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8452 
8453  return allocator->Map(allocation, ppData);
8454 }
8455 
8456 void vmaUnmapMemory(
8457  VmaAllocator allocator,
8458  VmaAllocation allocation)
8459 {
8460  VMA_ASSERT(allocator && allocation);
8461 
8462  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8463 
8464  allocator->Unmap(allocation);
8465 }
8466 
8467 VkResult vmaDefragment(
8468  VmaAllocator allocator,
8469  VmaAllocation* pAllocations,
8470  size_t allocationCount,
8471  VkBool32* pAllocationsChanged,
8472  const VmaDefragmentationInfo *pDefragmentationInfo,
8473  VmaDefragmentationStats* pDefragmentationStats)
8474 {
8475  VMA_ASSERT(allocator && pAllocations);
8476 
8477  VMA_DEBUG_LOG("vmaDefragment");
8478 
8479  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8480 
8481  return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats);
8482 }
8483 
8484 VkResult vmaCreateBuffer(
8485  VmaAllocator allocator,
8486  const VkBufferCreateInfo* pBufferCreateInfo,
8487  const VmaAllocationCreateInfo* pAllocationCreateInfo,
8488  VkBuffer* pBuffer,
8489  VmaAllocation* pAllocation,
8490  VmaAllocationInfo* pAllocationInfo)
8491 {
8492  VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
8493 
8494  VMA_DEBUG_LOG("vmaCreateBuffer");
8495 
8496  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8497 
8498  *pBuffer = VK_NULL_HANDLE;
8499  *pAllocation = VK_NULL_HANDLE;
8500 
8501  // 1. Create VkBuffer.
8502  VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
8503  allocator->m_hDevice,
8504  pBufferCreateInfo,
8505  allocator->GetAllocationCallbacks(),
8506  pBuffer);
8507  if(res >= 0)
8508  {
8509  // 2. vkGetBufferMemoryRequirements.
8510  VkMemoryRequirements vkMemReq = {};
8511  bool requiresDedicatedAllocation = false;
8512  bool prefersDedicatedAllocation = false;
8513  allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
8514  requiresDedicatedAllocation, prefersDedicatedAllocation);
8515 
8516  // Make sure alignment requirements for specific buffer usages reported
8517  // in Physical Device Properties are included in alignment reported by memory requirements.
8518  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
8519  {
8520  VMA_ASSERT(vkMemReq.alignment %
8521  allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
8522  }
8523  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
8524  {
8525  VMA_ASSERT(vkMemReq.alignment %
8526  allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
8527  }
8528  if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
8529  {
8530  VMA_ASSERT(vkMemReq.alignment %
8531  allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
8532  }
8533 
8534  // 3. Allocate memory using allocator.
8535  res = allocator->AllocateMemory(
8536  vkMemReq,
8537  requiresDedicatedAllocation,
8538  prefersDedicatedAllocation,
8539  *pBuffer, // dedicatedBuffer
8540  VK_NULL_HANDLE, // dedicatedImage
8541  *pAllocationCreateInfo,
8542  VMA_SUBALLOCATION_TYPE_BUFFER,
8543  pAllocation);
8544  if(res >= 0)
8545  {
8546  // 3. Bind buffer with memory.
8547  res = (*allocator->GetVulkanFunctions().vkBindBufferMemory)(
8548  allocator->m_hDevice,
8549  *pBuffer,
8550  (*pAllocation)->GetMemory(),
8551  (*pAllocation)->GetOffset());
8552  if(res >= 0)
8553  {
8554  // All steps succeeded.
8555  if(pAllocationInfo != VMA_NULL)
8556  {
8557  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8558  }
8559  return VK_SUCCESS;
8560  }
8561  allocator->FreeMemory(*pAllocation);
8562  *pAllocation = VK_NULL_HANDLE;
8563  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
8564  *pBuffer = VK_NULL_HANDLE;
8565  return res;
8566  }
8567  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
8568  *pBuffer = VK_NULL_HANDLE;
8569  return res;
8570  }
8571  return res;
8572 }
8573 
8574 void vmaDestroyBuffer(
8575  VmaAllocator allocator,
8576  VkBuffer buffer,
8577  VmaAllocation allocation)
8578 {
8579  if(buffer != VK_NULL_HANDLE)
8580  {
8581  VMA_ASSERT(allocator);
8582 
8583  VMA_DEBUG_LOG("vmaDestroyBuffer");
8584 
8585  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8586 
8587  (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
8588 
8589  allocator->FreeMemory(allocation);
8590  }
8591 }
8592 
8593 VkResult vmaCreateImage(
8594  VmaAllocator allocator,
8595  const VkImageCreateInfo* pImageCreateInfo,
8596  const VmaAllocationCreateInfo* pAllocationCreateInfo,
8597  VkImage* pImage,
8598  VmaAllocation* pAllocation,
8599  VmaAllocationInfo* pAllocationInfo)
8600 {
8601  VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
8602 
8603  VMA_DEBUG_LOG("vmaCreateImage");
8604 
8605  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8606 
8607  *pImage = VK_NULL_HANDLE;
8608  *pAllocation = VK_NULL_HANDLE;
8609 
8610  // 1. Create VkImage.
8611  VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
8612  allocator->m_hDevice,
8613  pImageCreateInfo,
8614  allocator->GetAllocationCallbacks(),
8615  pImage);
8616  if(res >= 0)
8617  {
8618  VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
8619  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
8620  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
8621 
8622  // 2. Allocate memory using allocator.
8623  res = AllocateMemoryForImage(allocator, *pImage, pAllocationCreateInfo, suballocType, pAllocation);
8624  if(res >= 0)
8625  {
8626  // 3. Bind image with memory.
8627  res = (*allocator->GetVulkanFunctions().vkBindImageMemory)(
8628  allocator->m_hDevice,
8629  *pImage,
8630  (*pAllocation)->GetMemory(),
8631  (*pAllocation)->GetOffset());
8632  if(res >= 0)
8633  {
8634  // All steps succeeded.
8635  if(pAllocationInfo != VMA_NULL)
8636  {
8637  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8638  }
8639  return VK_SUCCESS;
8640  }
8641  allocator->FreeMemory(*pAllocation);
8642  *pAllocation = VK_NULL_HANDLE;
8643  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
8644  *pImage = VK_NULL_HANDLE;
8645  return res;
8646  }
8647  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
8648  *pImage = VK_NULL_HANDLE;
8649  return res;
8650  }
8651  return res;
8652 }
8653 
8654 void vmaDestroyImage(
8655  VmaAllocator allocator,
8656  VkImage image,
8657  VmaAllocation allocation)
8658 {
8659  if(image != VK_NULL_HANDLE)
8660  {
8661  VMA_ASSERT(allocator);
8662 
8663  VMA_DEBUG_LOG("vmaDestroyImage");
8664 
8665  VMA_DEBUG_GLOBAL_MUTEX_LOCK
8666 
8667  (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
8668 
8669  allocator->FreeMemory(allocation);
8670  }
8671 }
8672 
8673 #endif // #ifdef VMA_IMPLEMENTATION
8674 #endif // ifdef USING_VULKAN
IMGUI_API void Value(const char *prefix, bool b)
Definition: imgui.cpp:10418