#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
#define AMD_VULKAN_MEMORY_ALLOCATOR_H
#ifdef __cplusplus
extern …
#endif
#endif
#if defined(__cplusplus) && defined(__INTELLISENSE__)
#define VMA_IMPLEMENTATION
#endif
#ifdef VMA_IMPLEMENTATION
#undef VMA_IMPLEMENTATION
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <utility>
#include <type_traits>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
#include <bit>
#endif
#if VMA_STATS_STRING_ENABLED
#include <cstdio>
#endif
#ifndef _VMA_CONFIGURATION
#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
#define VMA_STATIC_VULKAN_FUNCTIONS …
#endif
#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
#define VMA_DYNAMIC_VULKAN_FUNCTIONS …
#endif
#ifndef VMA_USE_STL_SHARED_MUTEX
#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L
#define VMA_USE_STL_SHARED_MUTEX …
#elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
#define VMA_USE_STL_SHARED_MUTEX …
#else
#define VMA_USE_STL_SHARED_MUTEX …
#endif
#endif
#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)
#include <cassert>
#include <algorithm>
#include <mutex>
#else
#include VMA_CONFIGURATION_USER_INCLUDES_H
#endif
#ifndef VMA_NULL
#define VMA_NULL …
#endif
#ifndef VMA_FALLTHROUGH
#if __has_cpp_attribute(clang::fallthrough)
#define VMA_FALLTHROUGH …
#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L
#define VMA_FALLTHROUGH …
#else
#define VMA_FALLTHROUGH
#endif
#endif
#ifndef VMA_ASSERT
#ifdef NDEBUG
#define VMA_ASSERT …
#else
#define VMA_ASSERT …
#endif
#endif
#ifndef VMA_HEAVY_ASSERT
#ifdef NDEBUG
#define VMA_HEAVY_ASSERT …
#else
#define VMA_HEAVY_ASSERT …
#endif
#endif
#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
#include <cstdlib>
void* vma_aligned_alloc(size_t alignment, size_t size)
{
if(alignment < sizeof(void*))
{
alignment = sizeof(void*);
}
return memalign(alignment, size);
}
#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
#include <cstdlib>
#if defined(__APPLE__)
#include <AvailabilityMacros.h>
#endif
void *vma_aligned_alloc(size_t alignment, size_t size)
{
if(alignment < sizeof(void*))
{
alignment = sizeof(void*);
}
void *pointer;
if(posix_memalign(&pointer, alignment, size) == 0)
return pointer;
return VMA_NULL;
}
#elif defined(_WIN32)
void* vma_aligned_alloc(size_t alignment, size_t size)
{
return _aligned_malloc(size, alignment);
}
#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L
void* vma_aligned_alloc(size_t alignment, size_t size)
{
return aligned_alloc(alignment, size);
}
#else
void* vma_aligned_alloc(size_t alignment, size_t size)
{
VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system.");
return VMA_NULL;
}
#endif
#if defined(_WIN32)
static void vma_aligned_free(void* ptr)
{
_aligned_free(ptr);
}
#else
static void vma_aligned_free(void* VMA_NULLABLE ptr)
{
free(ptr);
}
#endif
#ifndef VMA_ALIGN_OF
#define VMA_ALIGN_OF …
#endif
#ifndef VMA_SYSTEM_ALIGNED_MALLOC
#define VMA_SYSTEM_ALIGNED_MALLOC …
#endif
#ifndef VMA_SYSTEM_ALIGNED_FREE
#if defined(VMA_SYSTEM_FREE)
#define VMA_SYSTEM_ALIGNED_FREE …
#else
#define VMA_SYSTEM_ALIGNED_FREE …
#endif
#endif
#ifndef VMA_COUNT_BITS_SET
#define VMA_COUNT_BITS_SET …
#endif
#ifndef VMA_BITSCAN_LSB
#define VMA_BITSCAN_LSB …
#endif
#ifndef VMA_BITSCAN_MSB
#define VMA_BITSCAN_MSB …
#endif
#ifndef VMA_MIN
#define VMA_MIN …
#endif
#ifndef VMA_MAX
#define VMA_MAX …
#endif
#ifndef VMA_SWAP
#define VMA_SWAP …
#endif
#ifndef VMA_SORT
#define VMA_SORT …
#endif
#ifndef VMA_DEBUG_LOG_FORMAT
#define VMA_DEBUG_LOG_FORMAT …
#endif
#ifndef VMA_DEBUG_LOG
#define VMA_DEBUG_LOG …
#endif
#ifndef VMA_CLASS_NO_COPY
#define VMA_CLASS_NO_COPY …
#endif
#ifndef VMA_CLASS_NO_COPY_NO_MOVE
#define VMA_CLASS_NO_COPY_NO_MOVE …
#endif
#if VMA_STATS_STRING_ENABLED
static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
{
snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
}
static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
{
snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
}
static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
{
snprintf(outStr, strLen, "%p", ptr);
}
#endif
#ifndef VMA_MUTEX
class VmaMutex
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutex)
public:
VmaMutex() { }
void Lock() { m_Mutex.lock(); }
void Unlock() { m_Mutex.unlock(); }
bool TryLock() { return m_Mutex.try_lock(); }
private:
std::mutex m_Mutex;
};
#define VMA_MUTEX …
#endif
#ifndef VMA_RW_MUTEX
#if VMA_USE_STL_SHARED_MUTEX
#include <shared_mutex>
class VmaRWMutex
{
public:
void LockRead() { m_Mutex.lock_shared(); }
void UnlockRead() { m_Mutex.unlock_shared(); }
bool TryLockRead() { return m_Mutex.try_lock_shared(); }
void LockWrite() { m_Mutex.lock(); }
void UnlockWrite() { m_Mutex.unlock(); }
bool TryLockWrite() { return m_Mutex.try_lock(); }
private:
std::shared_mutex m_Mutex;
};
#define VMA_RW_MUTEX …
#elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
class VmaRWMutex
{
public:
VmaRWMutex() { InitializeSRWLock(&m_Lock); }
void LockRead() { AcquireSRWLockShared(&m_Lock); }
void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
private:
SRWLOCK m_Lock;
};
#define VMA_RW_MUTEX …
#else
class VmaRWMutex
{
public:
void LockRead() { m_Mutex.Lock(); }
void UnlockRead() { m_Mutex.Unlock(); }
bool TryLockRead() { return m_Mutex.TryLock(); }
void LockWrite() { m_Mutex.Lock(); }
void UnlockWrite() { m_Mutex.Unlock(); }
bool TryLockWrite() { return m_Mutex.TryLock(); }
private:
VMA_MUTEX m_Mutex;
};
#define VMA_RW_MUTEX …
#endif
#endif
#ifndef VMA_ATOMIC_UINT32
#include <atomic>
#define VMA_ATOMIC_UINT32 …
#endif
#ifndef VMA_ATOMIC_UINT64
#include <atomic>
#define VMA_ATOMIC_UINT64 …
#endif
#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
#define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY …
#endif
#ifndef VMA_MIN_ALIGNMENT
#ifdef VMA_DEBUG_ALIGNMENT
#define VMA_MIN_ALIGNMENT …
#else
#define VMA_MIN_ALIGNMENT …
#endif
#endif
#ifndef VMA_DEBUG_MARGIN
#define VMA_DEBUG_MARGIN …
#endif
#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
#define VMA_DEBUG_INITIALIZE_ALLOCATIONS …
#endif
#ifndef VMA_DEBUG_DETECT_CORRUPTION
#define VMA_DEBUG_DETECT_CORRUPTION …
#endif
#ifndef VMA_DEBUG_GLOBAL_MUTEX
#define VMA_DEBUG_GLOBAL_MUTEX …
#endif
#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY …
#endif
#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
#define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT …
#endif
#ifndef VMA_SMALL_HEAP_MAX_SIZE
#define VMA_SMALL_HEAP_MAX_SIZE …
#endif
#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
#define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE …
#endif
#ifndef VMA_MAPPING_HYSTERESIS_ENABLED
#define VMA_MAPPING_HYSTERESIS_ENABLED …
#endif
#define VMA_VALIDATE …
#endif
static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;
static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;
static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
static const uint32_t VMA_VENDOR_ID_AMD = 4098;
#define VK_ERROR_UNKNOWN_COPY …
#if VMA_STATS_STRING_ENABLED
static const char* VMA_SUBALLOCATION_TYPE_NAMES[] =
{
"FREE",
"UNKNOWN",
"BUFFER",
"IMAGE_UNKNOWN",
"IMAGE_LINEAR",
"IMAGE_OPTIMAL",
};
#endif
static VkAllocationCallbacks VmaEmptyAllocationCallbacks =
{ VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
#ifndef _VMA_ENUM_DECLARATIONS
enum VmaSuballocationType
{
VMA_SUBALLOCATION_TYPE_FREE = 0,
VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
VMA_SUBALLOCATION_TYPE_BUFFER = 2,
VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
};
enum VMA_CACHE_OPERATION
{
VMA_CACHE_FLUSH,
VMA_CACHE_INVALIDATE
};
enum class VmaAllocationRequestType
{
Normal,
TLSF,
UpperAddress,
EndOf1st,
EndOf2nd,
};
#endif
#ifndef _VMA_FORWARD_DECLARATIONS
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle)
struct VmaMutexLock;
struct VmaMutexLockRead;
struct VmaMutexLockWrite;
template<typename T>
struct AtomicTransactionalIncrement;
template<typename T>
struct VmaStlAllocator;
template<typename T, typename AllocatorT>
class VmaVector;
template<typename T, typename AllocatorT, size_t N>
class VmaSmallVector;
template<typename T>
class VmaPoolAllocator;
template<typename T>
struct VmaListItem;
template<typename T>
class VmaRawList;
template<typename T, typename AllocatorT>
class VmaList;
template<typename ItemTypeTraits>
class VmaIntrusiveLinkedList;
#if 0
template<typename T1, typename T2>
struct VmaPair;
template<typename FirstT, typename SecondT>
struct VmaPairFirstLess;
template<typename KeyT, typename ValueT>
class VmaMap;
#endif
#if VMA_STATS_STRING_ENABLED
class VmaStringBuilder;
class VmaJsonWriter;
#endif
class VmaDeviceMemoryBlock;
struct VmaDedicatedAllocationListItemTraits;
class VmaDedicatedAllocationList;
struct VmaSuballocation;
struct VmaSuballocationOffsetLess;
struct VmaSuballocationOffsetGreater;
struct VmaSuballocationItemSizeLess;
typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;
struct VmaAllocationRequest;
class VmaBlockMetadata;
class VmaBlockMetadata_Linear;
class VmaBlockMetadata_TLSF;
class VmaBlockVector;
struct VmaPoolListItemTraits;
struct VmaCurrentBudgetData;
class VmaAllocationObjectAllocator;
#endif
#ifndef _VMA_FUNCTIONS
static inline uint32_t VmaCountBitsSet(uint32_t v)
{
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
return std::popcount(v);
#else
uint32_t c = v - ((v >> 1) & 0x55555555);
c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
c = ((c >> 4) + c) & 0x0F0F0F0F;
c = ((c >> 8) + c) & 0x00FF00FF;
c = ((c >> 16) + c) & 0x0000FFFF;
return c;
#endif
}
static inline uint8_t VmaBitScanLSB(uint64_t mask)
{
#if defined(_MSC_VER) && defined(_WIN64)
unsigned long pos;
if (_BitScanForward64(&pos, mask))
return static_cast<uint8_t>(pos);
return UINT8_MAX;
#elif defined __GNUC__ || defined __clang__
return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;
#else
uint8_t pos = 0;
uint64_t bit = 1;
do
{
if (mask & bit)
return pos;
bit <<= 1;
} while (pos++ < 63);
return UINT8_MAX;
#endif
}
static inline uint8_t VmaBitScanLSB(uint32_t mask)
{
#ifdef _MSC_VER
unsigned long pos;
if (_BitScanForward(&pos, mask))
return static_cast<uint8_t>(pos);
return UINT8_MAX;
#elif defined __GNUC__ || defined __clang__
return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;
#else
uint8_t pos = 0;
uint32_t bit = 1;
do
{
if (mask & bit)
return pos;
bit <<= 1;
} while (pos++ < 31);
return UINT8_MAX;
#endif
}
static inline uint8_t VmaBitScanMSB(uint64_t mask)
{
#if defined(_MSC_VER) && defined(_WIN64)
unsigned long pos;
if (_BitScanReverse64(&pos, mask))
return static_cast<uint8_t>(pos);
#elif defined __GNUC__ || defined __clang__
if (mask)
return 63 - static_cast<uint8_t>(__builtin_clzll(mask));
#else
uint8_t pos = 63;
uint64_t bit = 1ULL << 63;
do
{
if (mask & bit)
return pos;
bit >>= 1;
} while (pos-- > 0);
#endif
return UINT8_MAX;
}
static inline uint8_t VmaBitScanMSB(uint32_t mask)
{
#ifdef _MSC_VER
unsigned long pos;
if (_BitScanReverse(&pos, mask))
return static_cast<uint8_t>(pos);
#elif defined __GNUC__ || defined __clang__
if (mask)
return 31 - static_cast<uint8_t>(__builtin_clz(mask));
#else
uint8_t pos = 31;
uint32_t bit = 1UL << 31;
do
{
if (mask & bit)
return pos;
bit >>= 1;
} while (pos-- > 0);
#endif
return UINT8_MAX;
}
template <typename T>
inline bool VmaIsPow2(T x)
{
return (x & (x - 1)) == 0;
}
template <typename T>
static inline T VmaAlignUp(T val, T alignment)
{
VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
return (val + alignment - 1) & ~(alignment - 1);
}
template <typename T>
static inline T VmaAlignDown(T val, T alignment)
{
VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
return val & ~(alignment - 1);
}
template <typename T>
static inline T VmaRoundDiv(T x, T y)
{
return (x + (y / (T)2)) / y;
}
template <typename T>
static inline T VmaDivideRoundingUp(T x, T y)
{
return (x + y - (T)1) / y;
}
static inline uint32_t VmaNextPow2(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
static inline uint64_t VmaNextPow2(uint64_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v++;
return v;
}
static inline uint32_t VmaPrevPow2(uint32_t v)
{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v = v ^ (v >> 1);
return v;
}
static inline uint64_t VmaPrevPow2(uint64_t v)
{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v = v ^ (v >> 1);
return v;
}
static inline bool VmaStrIsEmpty(const char* pStr)
{
return pStr == VMA_NULL || *pStr == '\0';
}
static inline bool VmaBlocksOnSamePage(
VkDeviceSize resourceAOffset,
VkDeviceSize resourceASize,
VkDeviceSize resourceBOffset,
VkDeviceSize pageSize)
{
VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
VkDeviceSize resourceBStart = resourceBOffset;
VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
return resourceAEndPage == resourceBStartPage;
}
static inline bool VmaIsBufferImageGranularityConflict(
VmaSuballocationType suballocType1,
VmaSuballocationType suballocType2)
{
if (suballocType1 > suballocType2)
{
VMA_SWAP(suballocType1, suballocType2);
}
switch (suballocType1)
{
case VMA_SUBALLOCATION_TYPE_FREE:
return false;
case VMA_SUBALLOCATION_TYPE_UNKNOWN:
return true;
case VMA_SUBALLOCATION_TYPE_BUFFER:
return
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
return
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
return
suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
return false;
default:
VMA_ASSERT(0);
return true;
}
}
static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
{
#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
uint32_t* pDst = (uint32_t*)((char*)pData + offset);
const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
for (size_t i = 0; i < numberCount; ++i, ++pDst)
{
*pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
}
#else
#endif
}
static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
{
#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
for (size_t i = 0; i < numberCount; ++i, ++pSrc)
{
if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
{
return false;
}
}
#endif
return true;
}
static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
{
memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE;
}
template <typename CmpLess, typename IterT, typename KeyT>
static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
{
size_t down = 0, up = size_t(end - beg);
while (down < up)
{
const size_t mid = down + (up - down) / 2;
if (cmp(*(beg + mid), key))
{
down = mid + 1;
}
else
{
up = mid;
}
}
return beg + down;
}
template<typename CmpLess, typename IterT, typename KeyT>
IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
{
IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
beg, end, value, cmp);
if (it == end ||
(!cmp(*it, value) && !cmp(value, *it)))
{
return it;
}
return end;
}
template<typename T>
static bool VmaValidatePointerArray(uint32_t count, const T* arr)
{
for (uint32_t i = 0; i < count; ++i)
{
const T iPtr = arr[i];
if (iPtr == VMA_NULL)
{
return false;
}
for (uint32_t j = i + 1; j < count; ++j)
{
if (iPtr == arr[j])
{
return false;
}
}
}
return true;
}
template<typename MainT, typename NewT>
static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
{
newStruct->pNext = mainStruct->pNext;
mainStruct->pNext = newStruct;
}
static bool FindMemoryPreferences(
bool isIntegratedGPU,
const VmaAllocationCreateInfo& allocCreateInfo,
VkFlags bufImgUsage,
VkMemoryPropertyFlags& outRequiredFlags,
VkMemoryPropertyFlags& outPreferredFlags,
VkMemoryPropertyFlags& outNotPreferredFlags)
{
outRequiredFlags = allocCreateInfo.requiredFlags;
outPreferredFlags = allocCreateInfo.preferredFlags;
outNotPreferredFlags = 0;
switch(allocCreateInfo.usage)
{
case VMA_MEMORY_USAGE_UNKNOWN:
break;
case VMA_MEMORY_USAGE_GPU_ONLY:
if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
break;
case VMA_MEMORY_USAGE_CPU_ONLY:
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
break;
case VMA_MEMORY_USAGE_CPU_TO_GPU:
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
break;
case VMA_MEMORY_USAGE_GPU_TO_CPU:
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
break;
case VMA_MEMORY_USAGE_CPU_COPY:
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
break;
case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
break;
case VMA_MEMORY_USAGE_AUTO:
case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:
case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:
{
if(bufImgUsage == UINT32_MAX)
{
VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known.");
return false;
}
const bool deviceAccess = (bufImgUsage & ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;
const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;
const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;
const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;
const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
if(hostAccessRandom)
{
if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
{
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
}
else
{
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
}
}
else if(hostAccessSequentialWrite)
{
outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
{
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
}
else
{
outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
if(deviceAccess)
{
if(preferHost)
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
else
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
else
{
if(preferDevice)
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
else
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
}
}
else
{
if(preferHost)
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
else
outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
break;
}
default:
VMA_ASSERT(0);
}
if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &
(VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
{
outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;
}
return true;
}
static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
{
void* result = VMA_NULL;
if ((pAllocationCallbacks != VMA_NULL) &&
(pAllocationCallbacks->pfnAllocation != VMA_NULL))
{
result = (*pAllocationCallbacks->pfnAllocation)(
pAllocationCallbacks->pUserData,
size,
alignment,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
}
else
{
result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
}
VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
return result;
}
static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
{
if ((pAllocationCallbacks != VMA_NULL) &&
(pAllocationCallbacks->pfnFree != VMA_NULL))
{
(*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
}
else
{
VMA_SYSTEM_ALIGNED_FREE(ptr);
}
}
template<typename T>
static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
{
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
}
template<typename T>
static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
{
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
}
#define vma_new …
#define vma_new_array …
template<typename T>
static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
{
ptr->~T();
VmaFree(pAllocationCallbacks, ptr);
}
template<typename T>
static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
{
if (ptr != VMA_NULL)
{
for (size_t i = count; i--; )
{
ptr[i].~T();
}
VmaFree(pAllocationCallbacks, ptr);
}
}
static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
{
if (srcStr != VMA_NULL)
{
const size_t len = strlen(srcStr);
char* const result = vma_new_array(allocs, char, len + 1);
memcpy(result, srcStr, len + 1);
return result;
}
return VMA_NULL;
}
#if VMA_STATS_STRING_ENABLED
static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
{
if (srcStr != VMA_NULL)
{
char* const result = vma_new_array(allocs, char, strLen + 1);
memcpy(result, srcStr, strLen);
result[strLen] = '\0';
return result;
}
return VMA_NULL;
}
#endif
static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
{
if (str != VMA_NULL)
{
const size_t len = strlen(str);
vma_delete_array(allocs, str, len + 1);
}
}
template<typename CmpLess, typename VectorT>
size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
{
const size_t indexToInsert = VmaBinaryFindFirstNotLess(
vector.data(),
vector.data() + vector.size(),
value,
CmpLess()) - vector.data();
VmaVectorInsert(vector, indexToInsert, value);
return indexToInsert;
}
template<typename CmpLess, typename VectorT>
bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
{
CmpLess comparator;
typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
vector.begin(),
vector.end(),
value,
comparator);
if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
{
size_t indexToRemove = it - vector.begin();
VmaVectorRemove(vector, indexToRemove);
return true;
}
return false;
}
#endif
#ifndef _VMA_STATISTICS_FUNCTIONS
static void VmaClearStatistics(VmaStatistics& outStats)
{
outStats.blockCount = 0;
outStats.allocationCount = 0;
outStats.blockBytes = 0;
outStats.allocationBytes = 0;
}
static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)
{
inoutStats.blockCount += src.blockCount;
inoutStats.allocationCount += src.allocationCount;
inoutStats.blockBytes += src.blockBytes;
inoutStats.allocationBytes += src.allocationBytes;
}
static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)
{
VmaClearStatistics(outStats.statistics);
outStats.unusedRangeCount = 0;
outStats.allocationSizeMin = VK_WHOLE_SIZE;
outStats.allocationSizeMax = 0;
outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;
outStats.unusedRangeSizeMax = 0;
}
static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
{
inoutStats.statistics.allocationCount++;
inoutStats.statistics.allocationBytes += size;
inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);
inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);
}
static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
{
inoutStats.unusedRangeCount++;
inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);
inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);
}
static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)
{
VmaAddStatistics(inoutStats.statistics, src.statistics);
inoutStats.unusedRangeCount += src.unusedRangeCount;
inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);
inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);
inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);
inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);
}
#endif
#ifndef _VMA_MUTEX_LOCK
struct VmaMutexLock
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLock)
public:
VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
m_pMutex(useMutex ? &mutex : VMA_NULL)
{
if (m_pMutex) { m_pMutex->Lock(); }
}
~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } }
private:
VMA_MUTEX* m_pMutex;
};
struct VmaMutexLockRead
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockRead)
public:
VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
m_pMutex(useMutex ? &mutex : VMA_NULL)
{
if (m_pMutex) { m_pMutex->LockRead(); }
}
~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }
private:
VMA_RW_MUTEX* m_pMutex;
};
struct VmaMutexLockWrite
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockWrite)
public:
VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)
: m_pMutex(useMutex ? &mutex : VMA_NULL)
{
if (m_pMutex) { m_pMutex->LockWrite(); }
}
~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }
private:
VMA_RW_MUTEX* m_pMutex;
};
#if VMA_DEBUG_GLOBAL_MUTEX
static VMA_MUTEX gDebugGlobalMutex;
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK …
#else
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK
#endif
#endif
#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
template<typename AtomicT>
struct AtomicTransactionalIncrement
{
public:
using T = decltype(AtomicT().load());
~AtomicTransactionalIncrement()
{
if(m_Atomic)
--(*m_Atomic);
}
void Commit() { m_Atomic = nullptr; }
T Increment(AtomicT* atomic)
{
m_Atomic = atomic;
return m_Atomic->fetch_add(1);
}
private:
AtomicT* m_Atomic = nullptr;
};
#endif
#ifndef _VMA_STL_ALLOCATOR
template<typename T>
struct VmaStlAllocator
{
const VkAllocationCallbacks* const m_pCallbacks;
typedef T value_type;
VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}
template<typename U>
VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}
VmaStlAllocator(const VmaStlAllocator&) = default;
VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;
T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
template<typename U>
bool operator==(const VmaStlAllocator<U>& rhs) const
{
return m_pCallbacks == rhs.m_pCallbacks;
}
template<typename U>
bool operator!=(const VmaStlAllocator<U>& rhs) const
{
return m_pCallbacks != rhs.m_pCallbacks;
}
};
#endif
#ifndef _VMA_VECTOR
template<typename T, typename AllocatorT>
class VmaVector
{
public:
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
VmaVector(const AllocatorT& allocator);
VmaVector(size_t count, const AllocatorT& allocator);
VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}
VmaVector(const VmaVector<T, AllocatorT>& src);
VmaVector& operator=(const VmaVector& rhs);
~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }
bool empty() const { return m_Count == 0; }
size_t size() const { return m_Count; }
T* data() { return m_pArray; }
T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
const T* data() const { return m_pArray; }
const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
iterator begin() { return m_pArray; }
iterator end() { return m_pArray + m_Count; }
const_iterator cbegin() const { return m_pArray; }
const_iterator cend() const { return m_pArray + m_Count; }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
void push_front(const T& src) { insert(0, src); }
void push_back(const T& src);
void reserve(size_t newCapacity, bool freeMemory = false);
void resize(size_t newCount);
void clear() { resize(0); }
void shrink_to_fit();
void insert(size_t index, const T& src);
void remove(size_t index);
T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
private:
AllocatorT m_Allocator;
T* m_pArray;
size_t m_Count;
size_t m_Capacity;
};
#ifndef _VMA_VECTOR_FUNCTIONS
template<typename T, typename AllocatorT>
VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)
: m_Allocator(allocator),
m_pArray(VMA_NULL),
m_Count(0),
m_Capacity(0) {}
template<typename T, typename AllocatorT>
VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)
: m_Allocator(allocator),
m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
m_Count(count),
m_Capacity(count) {}
template<typename T, typename AllocatorT>
VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)
: m_Allocator(src.m_Allocator),
m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
m_Count(src.m_Count),
m_Capacity(src.m_Count)
{
if (m_Count != 0)
{
memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
}
}
template<typename T, typename AllocatorT>
VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)
{
if (&rhs != this)
{
resize(rhs.m_Count);
if (m_Count != 0)
{
memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
}
}
return *this;
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::push_back(const T& src)
{
const size_t newIndex = size();
resize(newIndex + 1);
m_pArray[newIndex] = src;
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)
{
newCapacity = VMA_MAX(newCapacity, m_Count);
if ((newCapacity < m_Capacity) && !freeMemory)
{
newCapacity = m_Capacity;
}
if (newCapacity != m_Capacity)
{
T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
if (m_Count != 0)
{
memcpy(newArray, m_pArray, m_Count * sizeof(T));
}
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
m_Capacity = newCapacity;
m_pArray = newArray;
}
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::resize(size_t newCount)
{
size_t newCapacity = m_Capacity;
if (newCount > m_Capacity)
{
newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
}
if (newCapacity != m_Capacity)
{
T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
if (elementsToCopy != 0)
{
memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
}
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
m_Capacity = newCapacity;
m_pArray = newArray;
}
m_Count = newCount;
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::shrink_to_fit()
{
if (m_Capacity > m_Count)
{
T* newArray = VMA_NULL;
if (m_Count > 0)
{
newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
memcpy(newArray, m_pArray, m_Count * sizeof(T));
}
VmaFree(m_Allocator.m_pCallbacks, m_pArray);
m_Capacity = m_Count;
m_pArray = newArray;
}
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)
{
VMA_HEAVY_ASSERT(index <= m_Count);
const size_t oldCount = size();
resize(oldCount + 1);
if (index < oldCount)
{
memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
}
m_pArray[index] = src;
}
template<typename T, typename AllocatorT>
void VmaVector<T, AllocatorT>::remove(size_t index)
{
VMA_HEAVY_ASSERT(index < m_Count);
const size_t oldCount = size();
if (index < oldCount - 1)
{
memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
}
resize(oldCount - 1);
}
#endif
template<typename T, typename allocatorT>
static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
{
vec.insert(index, item);
}
template<typename T, typename allocatorT>
static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
{
vec.remove(index);
}
#endif
#ifndef _VMA_SMALL_VECTOR
template<typename T, typename AllocatorT, size_t N>
class VmaSmallVector
{
public:
typedef T value_type;
typedef T* iterator;
VmaSmallVector(const AllocatorT& allocator);
VmaSmallVector(size_t count, const AllocatorT& allocator);
template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
~VmaSmallVector() = default;
bool empty() const { return m_Count == 0; }
size_t size() const { return m_Count; }
T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
iterator begin() { return data(); }
iterator end() { return data() + m_Count; }
void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
void push_front(const T& src) { insert(0, src); }
void push_back(const T& src);
void resize(size_t newCount, bool freeMemory = false);
void clear(bool freeMemory = false);
void insert(size_t index, const T& src);
void remove(size_t index);
T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
private:
size_t m_Count;
T m_StaticArray[N];
VmaVector<T, AllocatorT> m_DynamicArray;
};
#ifndef _VMA_SMALL_VECTOR_FUNCTIONS
template<typename T, typename AllocatorT, size_t N>
VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)
: m_Count(0),
m_DynamicArray(allocator) {}
template<typename T, typename AllocatorT, size_t N>
VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)
: m_Count(count),
m_DynamicArray(count > N ? count : 0, allocator) {}
template<typename T, typename AllocatorT, size_t N>
void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)
{
const size_t newIndex = size();
resize(newIndex + 1);
data()[newIndex] = src;
}
template<typename T, typename AllocatorT, size_t N>
void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)
{
if (newCount > N && m_Count > N)
{
m_DynamicArray.resize(newCount);
if (freeMemory)
{
m_DynamicArray.shrink_to_fit();
}
}
else if (newCount > N && m_Count <= N)
{
m_DynamicArray.resize(newCount);
if (m_Count > 0)
{
memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
}
}
else if (newCount <= N && m_Count > N)
{
if (newCount > 0)
{
memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
}
m_DynamicArray.resize(0);
if (freeMemory)
{
m_DynamicArray.shrink_to_fit();
}
}
else
{
}
m_Count = newCount;
}
template<typename T, typename AllocatorT, size_t N>
void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)
{
m_DynamicArray.clear();
if (freeMemory)
{
m_DynamicArray.shrink_to_fit();
}
m_Count = 0;
}
template<typename T, typename AllocatorT, size_t N>
void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)
{
VMA_HEAVY_ASSERT(index <= m_Count);
const size_t oldCount = size();
resize(oldCount + 1);
T* const dataPtr = data();
if (index < oldCount)
{
memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
}
dataPtr[index] = src;
}
template<typename T, typename AllocatorT, size_t N>
void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)
{
VMA_HEAVY_ASSERT(index < m_Count);
const size_t oldCount = size();
if (index < oldCount - 1)
{
T* const dataPtr = data();
memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
}
resize(oldCount - 1);
}
#endif
#endif
#ifndef _VMA_POOL_ALLOCATOR
template<typename T>
class VmaPoolAllocator
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaPoolAllocator)
public:
VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
~VmaPoolAllocator();
template<typename... Types> T* Alloc(Types&&... args);
void Free(T* ptr);
private:
union Item
{
uint32_t NextFreeIndex;
alignas(T) char Value[sizeof(T)];
};
struct ItemBlock
{
Item* pItems;
uint32_t Capacity;
uint32_t FirstFreeIndex;
};
const VkAllocationCallbacks* m_pAllocationCallbacks;
const uint32_t m_FirstBlockCapacity;
VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;
ItemBlock& CreateNewBlock();
};
#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS
template<typename T>
VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)
: m_pAllocationCallbacks(pAllocationCallbacks),
m_FirstBlockCapacity(firstBlockCapacity),
m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
{
VMA_ASSERT(m_FirstBlockCapacity > 1);
}
template<typename T>
VmaPoolAllocator<T>::~VmaPoolAllocator()
{
for (size_t i = m_ItemBlocks.size(); i--;)
vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
m_ItemBlocks.clear();
}
template<typename T>
template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
{
for (size_t i = m_ItemBlocks.size(); i--; )
{
ItemBlock& block = m_ItemBlocks[i];
if (block.FirstFreeIndex != UINT32_MAX)
{
Item* const pItem = &block.pItems[block.FirstFreeIndex];
block.FirstFreeIndex = pItem->NextFreeIndex;
T* result = (T*)&pItem->Value;
new(result)T(std::forward<Types>(args)...);
return result;
}
}
ItemBlock& newBlock = CreateNewBlock();
Item* const pItem = &newBlock.pItems[0];
newBlock.FirstFreeIndex = pItem->NextFreeIndex;
T* result = (T*)&pItem->Value;
new(result) T(std::forward<Types>(args)...);
return result;
}
template<typename T>
void VmaPoolAllocator<T>::Free(T* ptr)
{
for (size_t i = m_ItemBlocks.size(); i--; )
{
ItemBlock& block = m_ItemBlocks[i];
Item* pItemPtr;
memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
{
ptr->~T();
const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
pItemPtr->NextFreeIndex = block.FirstFreeIndex;
block.FirstFreeIndex = index;
return;
}
}
VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
}
template<typename T>
typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
{
const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
const ItemBlock newBlock =
{
vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
newBlockCapacity,
0
};
m_ItemBlocks.push_back(newBlock);
for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)
newBlock.pItems[i].NextFreeIndex = i + 1;
newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
return m_ItemBlocks.back();
}
#endif
#endif
#ifndef _VMA_RAW_LIST
template<typename T>
struct VmaListItem
{
VmaListItem* pPrev;
VmaListItem* pNext;
T Value;
};
template<typename T>
class VmaRawList
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaRawList)
public:
typedef VmaListItem<T> ItemType;
VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
~VmaRawList() = default;
size_t GetCount() const { return m_Count; }
bool IsEmpty() const { return m_Count == 0; }
ItemType* Front() { return m_pFront; }
ItemType* Back() { return m_pBack; }
const ItemType* Front() const { return m_pFront; }
const ItemType* Back() const { return m_pBack; }
ItemType* PushFront();
ItemType* PushBack();
ItemType* PushFront(const T& value);
ItemType* PushBack(const T& value);
void PopFront();
void PopBack();
ItemType* InsertBefore(ItemType* pItem);
ItemType* InsertAfter(ItemType* pItem);
ItemType* InsertBefore(ItemType* pItem, const T& value);
ItemType* InsertAfter(ItemType* pItem, const T& value);
void Clear();
void Remove(ItemType* pItem);
private:
const VkAllocationCallbacks* const m_pAllocationCallbacks;
VmaPoolAllocator<ItemType> m_ItemAllocator;
ItemType* m_pFront;
ItemType* m_pBack;
size_t m_Count;
};
#ifndef _VMA_RAW_LIST_FUNCTIONS
template<typename T>
VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)
: m_pAllocationCallbacks(pAllocationCallbacks),
m_ItemAllocator(pAllocationCallbacks, 128),
m_pFront(VMA_NULL),
m_pBack(VMA_NULL),
m_Count(0) {}
template<typename T>
VmaListItem<T>* VmaRawList<T>::PushFront()
{
ItemType* const pNewItem = m_ItemAllocator.Alloc();
pNewItem->pPrev = VMA_NULL;
if (IsEmpty())
{
pNewItem->pNext = VMA_NULL;
m_pFront = pNewItem;
m_pBack = pNewItem;
m_Count = 1;
}
else
{
pNewItem->pNext = m_pFront;
m_pFront->pPrev = pNewItem;
m_pFront = pNewItem;
++m_Count;
}
return pNewItem;
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::PushBack()
{
ItemType* const pNewItem = m_ItemAllocator.Alloc();
pNewItem->pNext = VMA_NULL;
if(IsEmpty())
{
pNewItem->pPrev = VMA_NULL;
m_pFront = pNewItem;
m_pBack = pNewItem;
m_Count = 1;
}
else
{
pNewItem->pPrev = m_pBack;
m_pBack->pNext = pNewItem;
m_pBack = pNewItem;
++m_Count;
}
return pNewItem;
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
{
ItemType* const pNewItem = PushFront();
pNewItem->Value = value;
return pNewItem;
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
{
ItemType* const pNewItem = PushBack();
pNewItem->Value = value;
return pNewItem;
}
template<typename T>
void VmaRawList<T>::PopFront()
{
VMA_HEAVY_ASSERT(m_Count > 0);
ItemType* const pFrontItem = m_pFront;
ItemType* const pNextItem = pFrontItem->pNext;
if (pNextItem != VMA_NULL)
{
pNextItem->pPrev = VMA_NULL;
}
m_pFront = pNextItem;
m_ItemAllocator.Free(pFrontItem);
--m_Count;
}
template<typename T>
void VmaRawList<T>::PopBack()
{
VMA_HEAVY_ASSERT(m_Count > 0);
ItemType* const pBackItem = m_pBack;
ItemType* const pPrevItem = pBackItem->pPrev;
if(pPrevItem != VMA_NULL)
{
pPrevItem->pNext = VMA_NULL;
}
m_pBack = pPrevItem;
m_ItemAllocator.Free(pBackItem);
--m_Count;
}
template<typename T>
void VmaRawList<T>::Clear()
{
if (IsEmpty() == false)
{
ItemType* pItem = m_pBack;
while (pItem != VMA_NULL)
{
ItemType* const pPrevItem = pItem->pPrev;
m_ItemAllocator.Free(pItem);
pItem = pPrevItem;
}
m_pFront = VMA_NULL;
m_pBack = VMA_NULL;
m_Count = 0;
}
}
template<typename T>
void VmaRawList<T>::Remove(ItemType* pItem)
{
VMA_HEAVY_ASSERT(pItem != VMA_NULL);
VMA_HEAVY_ASSERT(m_Count > 0);
if(pItem->pPrev != VMA_NULL)
{
pItem->pPrev->pNext = pItem->pNext;
}
else
{
VMA_HEAVY_ASSERT(m_pFront == pItem);
m_pFront = pItem->pNext;
}
if(pItem->pNext != VMA_NULL)
{
pItem->pNext->pPrev = pItem->pPrev;
}
else
{
VMA_HEAVY_ASSERT(m_pBack == pItem);
m_pBack = pItem->pPrev;
}
m_ItemAllocator.Free(pItem);
--m_Count;
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
{
if(pItem != VMA_NULL)
{
ItemType* const prevItem = pItem->pPrev;
ItemType* const newItem = m_ItemAllocator.Alloc();
newItem->pPrev = prevItem;
newItem->pNext = pItem;
pItem->pPrev = newItem;
if(prevItem != VMA_NULL)
{
prevItem->pNext = newItem;
}
else
{
VMA_HEAVY_ASSERT(m_pFront == pItem);
m_pFront = newItem;
}
++m_Count;
return newItem;
}
else
return PushBack();
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
{
if(pItem != VMA_NULL)
{
ItemType* const nextItem = pItem->pNext;
ItemType* const newItem = m_ItemAllocator.Alloc();
newItem->pNext = nextItem;
newItem->pPrev = pItem;
pItem->pNext = newItem;
if(nextItem != VMA_NULL)
{
nextItem->pPrev = newItem;
}
else
{
VMA_HEAVY_ASSERT(m_pBack == pItem);
m_pBack = newItem;
}
++m_Count;
return newItem;
}
else
return PushFront();
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
{
ItemType* const newItem = InsertBefore(pItem);
newItem->Value = value;
return newItem;
}
template<typename T>
VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
{
ItemType* const newItem = InsertAfter(pItem);
newItem->Value = value;
return newItem;
}
#endif
#endif
#ifndef _VMA_LIST
template<typename T, typename AllocatorT>
class VmaList
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaList)
public:
class reverse_iterator;
class const_iterator;
class const_reverse_iterator;
class iterator
{
friend class const_iterator;
friend class VmaList<T, AllocatorT>;
public:
iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
iterator operator++(int) { iterator result = *this; ++*this; return result; }
iterator operator--(int) { iterator result = *this; --*this; return result; }
iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
iterator& operator--();
private:
VmaRawList<T>* m_pList;
VmaListItem<T>* m_pItem;
iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
};
class reverse_iterator
{
friend class const_reverse_iterator;
friend class VmaList<T, AllocatorT>;
public:
reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }
reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }
reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
reverse_iterator& operator--();
private:
VmaRawList<T>* m_pList;
VmaListItem<T>* m_pItem;
reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
};
class const_iterator
{
friend class VmaList<T, AllocatorT>;
public:
const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }
const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }
const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
const_iterator& operator--();
private:
const VmaRawList<T>* m_pList;
const VmaListItem<T>* m_pItem;
const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
};
class const_reverse_iterator
{
friend class VmaList<T, AllocatorT>;
public:
const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }
const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }
const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
const_reverse_iterator& operator--();
private:
const VmaRawList<T>* m_pList;
const VmaListItem<T>* m_pItem;
const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
};
VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}
bool empty() const { return m_RawList.IsEmpty(); }
size_t size() const { return m_RawList.GetCount(); }
iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
iterator end() { return iterator(&m_RawList, VMA_NULL); }
const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }
reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }
const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }
const_reverse_iterator rbegin() const { return crbegin(); }
const_reverse_iterator rend() const { return crend(); }
void push_back(const T& value) { m_RawList.PushBack(value); }
iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
void clear() { m_RawList.Clear(); }
void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
private:
VmaRawList<T> m_RawList;
};
#ifndef _VMA_LIST_FUNCTIONS
template<typename T, typename AllocatorT>
typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()
{
if (m_pItem != VMA_NULL)
{
m_pItem = m_pItem->pPrev;
}
else
{
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Back();
}
return *this;
}
template<typename T, typename AllocatorT>
typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()
{
if (m_pItem != VMA_NULL)
{
m_pItem = m_pItem->pNext;
}
else
{
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Front();
}
return *this;
}
template<typename T, typename AllocatorT>
typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()
{
if (m_pItem != VMA_NULL)
{
m_pItem = m_pItem->pPrev;
}
else
{
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Back();
}
return *this;
}
template<typename T, typename AllocatorT>
typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()
{
if (m_pItem != VMA_NULL)
{
m_pItem = m_pItem->pNext;
}
else
{
VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Back();
}
return *this;
}
#endif
#endif
#ifndef _VMA_INTRUSIVE_LINKED_LIST
template<typename ItemTypeTraits>
class VmaIntrusiveLinkedList
{
public:
typedef typename ItemTypeTraits::ItemType ItemType;
static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
VmaIntrusiveLinkedList() = default;
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src);
VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;
VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src);
VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;
~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }
size_t GetCount() const { return m_Count; }
bool IsEmpty() const { return m_Count == 0; }
ItemType* Front() { return m_Front; }
ItemType* Back() { return m_Back; }
const ItemType* Front() const { return m_Front; }
const ItemType* Back() const { return m_Back; }
void PushBack(ItemType* item);
void PushFront(ItemType* item);
ItemType* PopBack();
ItemType* PopFront();
void InsertBefore(ItemType* existingItem, ItemType* newItem);
void InsertAfter(ItemType* existingItem, ItemType* newItem);
void Remove(ItemType* item);
void RemoveAll();
private:
ItemType* m_Front = VMA_NULL;
ItemType* m_Back = VMA_NULL;
size_t m_Count = 0;
};
#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
template<typename ItemTypeTraits>
VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src)
: m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
{
src.m_Front = src.m_Back = VMA_NULL;
src.m_Count = 0;
}
template<typename ItemTypeTraits>
VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src)
{
if (&src != this)
{
VMA_HEAVY_ASSERT(IsEmpty());
m_Front = src.m_Front;
m_Back = src.m_Back;
m_Count = src.m_Count;
src.m_Front = src.m_Back = VMA_NULL;
src.m_Count = 0;
}
return *this;
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)
{
VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
if (IsEmpty())
{
m_Front = item;
m_Back = item;
m_Count = 1;
}
else
{
ItemTypeTraits::AccessPrev(item) = m_Back;
ItemTypeTraits::AccessNext(m_Back) = item;
m_Back = item;
++m_Count;
}
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)
{
VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
if (IsEmpty())
{
m_Front = item;
m_Back = item;
m_Count = 1;
}
else
{
ItemTypeTraits::AccessNext(item) = m_Front;
ItemTypeTraits::AccessPrev(m_Front) = item;
m_Front = item;
++m_Count;
}
}
template<typename ItemTypeTraits>
typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()
{
VMA_HEAVY_ASSERT(m_Count > 0);
ItemType* const backItem = m_Back;
ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
if (prevItem != VMA_NULL)
{
ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
}
m_Back = prevItem;
--m_Count;
ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
return backItem;
}
template<typename ItemTypeTraits>
typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()
{
VMA_HEAVY_ASSERT(m_Count > 0);
ItemType* const frontItem = m_Front;
ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
if (nextItem != VMA_NULL)
{
ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
}
m_Front = nextItem;
--m_Count;
ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
return frontItem;
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)
{
VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
if (existingItem != VMA_NULL)
{
ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
ItemTypeTraits::AccessPrev(newItem) = prevItem;
ItemTypeTraits::AccessNext(newItem) = existingItem;
ItemTypeTraits::AccessPrev(existingItem) = newItem;
if (prevItem != VMA_NULL)
{
ItemTypeTraits::AccessNext(prevItem) = newItem;
}
else
{
VMA_HEAVY_ASSERT(m_Front == existingItem);
m_Front = newItem;
}
++m_Count;
}
else
PushBack(newItem);
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)
{
VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
if (existingItem != VMA_NULL)
{
ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
ItemTypeTraits::AccessNext(newItem) = nextItem;
ItemTypeTraits::AccessPrev(newItem) = existingItem;
ItemTypeTraits::AccessNext(existingItem) = newItem;
if (nextItem != VMA_NULL)
{
ItemTypeTraits::AccessPrev(nextItem) = newItem;
}
else
{
VMA_HEAVY_ASSERT(m_Back == existingItem);
m_Back = newItem;
}
++m_Count;
}
else
return PushFront(newItem);
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)
{
VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
if (ItemTypeTraits::GetPrev(item) != VMA_NULL)
{
ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
}
else
{
VMA_HEAVY_ASSERT(m_Front == item);
m_Front = ItemTypeTraits::GetNext(item);
}
if (ItemTypeTraits::GetNext(item) != VMA_NULL)
{
ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
}
else
{
VMA_HEAVY_ASSERT(m_Back == item);
m_Back = ItemTypeTraits::GetPrev(item);
}
ItemTypeTraits::AccessPrev(item) = VMA_NULL;
ItemTypeTraits::AccessNext(item) = VMA_NULL;
--m_Count;
}
template<typename ItemTypeTraits>
void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()
{
if (!IsEmpty())
{
ItemType* item = m_Back;
while (item != VMA_NULL)
{
ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
ItemTypeTraits::AccessPrev(item) = VMA_NULL;
ItemTypeTraits::AccessNext(item) = VMA_NULL;
item = prevItem;
}
m_Front = VMA_NULL;
m_Back = VMA_NULL;
m_Count = 0;
}
}
#endif
#endif
#if 0
#ifndef _VMA_PAIR
template<typename T1, typename T2>
struct VmaPair
{
T1 first;
T2 second;
VmaPair() : first(), second() {}
VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {}
};
template<typename FirstT, typename SecondT>
struct VmaPairFirstLess
{
bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
{
return lhs.first < rhs.first;
}
bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
{
return lhs.first < rhsFirst;
}
};
#endif
#ifndef _VMA_MAP
template<typename KeyT, typename ValueT>
class VmaMap
{
public:
typedef VmaPair<KeyT, ValueT> PairType;
typedef PairType* iterator;
VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) {}
iterator begin() { return m_Vector.begin(); }
iterator end() { return m_Vector.end(); }
size_t size() { return m_Vector.size(); }
void insert(const PairType& pair);
iterator find(const KeyT& key);
void erase(iterator it);
private:
VmaVector< PairType, VmaStlAllocator<PairType>> m_Vector;
};
#ifndef _VMA_MAP_FUNCTIONS
template<typename KeyT, typename ValueT>
void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
{
const size_t indexToInsert = VmaBinaryFindFirstNotLess(
m_Vector.data(),
m_Vector.data() + m_Vector.size(),
pair,
VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
VmaVectorInsert(m_Vector, indexToInsert, pair);
}
template<typename KeyT, typename ValueT>
VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
{
PairType* it = VmaBinaryFindFirstNotLess(
m_Vector.data(),
m_Vector.data() + m_Vector.size(),
key,
VmaPairFirstLess<KeyT, ValueT>());
if ((it != m_Vector.end()) && (it->first == key))
{
return it;
}
else
{
return m_Vector.end();
}
}
template<typename KeyT, typename ValueT>
void VmaMap<KeyT, ValueT>::erase(iterator it)
{
VmaVectorRemove(m_Vector, it - m_Vector.begin());
}
#endif
#endif
#endif
#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED
class VmaStringBuilder
{
public:
VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}
~VmaStringBuilder() = default;
size_t GetLength() const { return m_Data.size(); }
const char* GetData() const { return m_Data.data(); }
void AddNewLine() { Add('\n'); }
void Add(char ch) { m_Data.push_back(ch); }
void Add(const char* pStr);
void AddNumber(uint32_t num);
void AddNumber(uint64_t num);
void AddPointer(const void* ptr);
private:
VmaVector<char, VmaStlAllocator<char>> m_Data;
};
#ifndef _VMA_STRING_BUILDER_FUNCTIONS
void VmaStringBuilder::Add(const char* pStr)
{
const size_t strLen = strlen(pStr);
if (strLen > 0)
{
const size_t oldCount = m_Data.size();
m_Data.resize(oldCount + strLen);
memcpy(m_Data.data() + oldCount, pStr, strLen);
}
}
void VmaStringBuilder::AddNumber(uint32_t num)
{
char buf[11];
buf[10] = '\0';
char* p = &buf[10];
do
{
*--p = '0' + (char)(num % 10);
num /= 10;
} while (num);
Add(p);
}
void VmaStringBuilder::AddNumber(uint64_t num)
{
char buf[21];
buf[20] = '\0';
char* p = &buf[20];
do
{
*--p = '0' + (char)(num % 10);
num /= 10;
} while (num);
Add(p);
}
void VmaStringBuilder::AddPointer(const void* ptr)
{
char buf[21];
VmaPtrToStr(buf, sizeof(buf), ptr);
Add(buf);
}
#endif
#endif
#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED
class VmaJsonWriter
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaJsonWriter)
public:
VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
~VmaJsonWriter();
void BeginObject(bool singleLine = false);
void EndObject();
void BeginArray(bool singleLine = false);
void EndArray();
void WriteString(const char* pStr);
void BeginString(const char* pStr = VMA_NULL);
void ContinueString(const char* pStr);
void ContinueString(uint32_t n);
void ContinueString(uint64_t n);
void ContinueString_Pointer(const void* ptr);
void EndString(const char* pStr = VMA_NULL);
void WriteNumber(uint32_t n);
void WriteNumber(uint64_t n);
void WriteBool(bool b);
void WriteNull();
private:
enum COLLECTION_TYPE
{
COLLECTION_TYPE_OBJECT,
COLLECTION_TYPE_ARRAY,
};
struct StackItem
{
COLLECTION_TYPE type;
uint32_t valueCount;
bool singleLineMode;
};
static const char* const INDENT;
VmaStringBuilder& m_SB;
VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
bool m_InsideString;
void BeginValue(bool isString);
void WriteIndent(bool oneLess = false);
};
const char* const VmaJsonWriter::INDENT = " ";
#ifndef _VMA_JSON_WRITER_FUNCTIONS
VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)
: m_SB(sb),
m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
m_InsideString(false) {}
VmaJsonWriter::~VmaJsonWriter()
{
VMA_ASSERT(!m_InsideString);
VMA_ASSERT(m_Stack.empty());
}
void VmaJsonWriter::BeginObject(bool singleLine)
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add('{');
StackItem item;
item.type = COLLECTION_TYPE_OBJECT;
item.valueCount = 0;
item.singleLineMode = singleLine;
m_Stack.push_back(item);
}
void VmaJsonWriter::EndObject()
{
VMA_ASSERT(!m_InsideString);
WriteIndent(true);
m_SB.Add('}');
VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
m_Stack.pop_back();
}
void VmaJsonWriter::BeginArray(bool singleLine)
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add('[');
StackItem item;
item.type = COLLECTION_TYPE_ARRAY;
item.valueCount = 0;
item.singleLineMode = singleLine;
m_Stack.push_back(item);
}
void VmaJsonWriter::EndArray()
{
VMA_ASSERT(!m_InsideString);
WriteIndent(true);
m_SB.Add(']');
VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
m_Stack.pop_back();
}
void VmaJsonWriter::WriteString(const char* pStr)
{
BeginString(pStr);
EndString();
}
void VmaJsonWriter::BeginString(const char* pStr)
{
VMA_ASSERT(!m_InsideString);
BeginValue(true);
m_SB.Add('"');
m_InsideString = true;
if (pStr != VMA_NULL && pStr[0] != '\0')
{
ContinueString(pStr);
}
}
void VmaJsonWriter::ContinueString(const char* pStr)
{
VMA_ASSERT(m_InsideString);
const size_t strLen = strlen(pStr);
for (size_t i = 0; i < strLen; ++i)
{
char ch = pStr[i];
if (ch == '\\')
{
m_SB.Add("\\\\");
}
else if (ch == '"')
{
m_SB.Add("\\\"");
}
else if (ch >= 32)
{
m_SB.Add(ch);
}
else switch (ch)
{
case '\b':
m_SB.Add("\\b");
break;
case '\f':
m_SB.Add("\\f");
break;
case '\n':
m_SB.Add("\\n");
break;
case '\r':
m_SB.Add("\\r");
break;
case '\t':
m_SB.Add("\\t");
break;
default:
VMA_ASSERT(0 && "Character not currently supported.");
}
}
}
void VmaJsonWriter::ContinueString(uint32_t n)
{
VMA_ASSERT(m_InsideString);
m_SB.AddNumber(n);
}
void VmaJsonWriter::ContinueString(uint64_t n)
{
VMA_ASSERT(m_InsideString);
m_SB.AddNumber(n);
}
void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
{
VMA_ASSERT(m_InsideString);
m_SB.AddPointer(ptr);
}
void VmaJsonWriter::EndString(const char* pStr)
{
VMA_ASSERT(m_InsideString);
if (pStr != VMA_NULL && pStr[0] != '\0')
{
ContinueString(pStr);
}
m_SB.Add('"');
m_InsideString = false;
}
void VmaJsonWriter::WriteNumber(uint32_t n)
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.AddNumber(n);
}
void VmaJsonWriter::WriteNumber(uint64_t n)
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.AddNumber(n);
}
void VmaJsonWriter::WriteBool(bool b)
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add(b ? "true" : "false");
}
void VmaJsonWriter::WriteNull()
{
VMA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add("null");
}
void VmaJsonWriter::BeginValue(bool isString)
{
if (!m_Stack.empty())
{
StackItem& currItem = m_Stack.back();
if (currItem.type == COLLECTION_TYPE_OBJECT &&
currItem.valueCount % 2 == 0)
{
VMA_ASSERT(isString);
}
if (currItem.type == COLLECTION_TYPE_OBJECT &&
currItem.valueCount % 2 != 0)
{
m_SB.Add(": ");
}
else if (currItem.valueCount > 0)
{
m_SB.Add(", ");
WriteIndent();
}
else
{
WriteIndent();
}
++currItem.valueCount;
}
}
void VmaJsonWriter::WriteIndent(bool oneLess)
{
if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
{
m_SB.AddNewLine();
size_t count = m_Stack.size();
if (count > 0 && oneLess)
{
--count;
}
for (size_t i = 0; i < count; ++i)
{
m_SB.Add(INDENT);
}
}
}
#endif
static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)
{
json.BeginObject();
json.WriteString("BlockCount");
json.WriteNumber(stat.statistics.blockCount);
json.WriteString("BlockBytes");
json.WriteNumber(stat.statistics.blockBytes);
json.WriteString("AllocationCount");
json.WriteNumber(stat.statistics.allocationCount);
json.WriteString("AllocationBytes");
json.WriteNumber(stat.statistics.allocationBytes);
json.WriteString("UnusedRangeCount");
json.WriteNumber(stat.unusedRangeCount);
if (stat.statistics.allocationCount > 1)
{
json.WriteString("AllocationSizeMin");
json.WriteNumber(stat.allocationSizeMin);
json.WriteString("AllocationSizeMax");
json.WriteNumber(stat.allocationSizeMax);
}
if (stat.unusedRangeCount > 1)
{
json.WriteString("UnusedRangeSizeMin");
json.WriteNumber(stat.unusedRangeSizeMin);
json.WriteString("UnusedRangeSizeMax");
json.WriteNumber(stat.unusedRangeSizeMax);
}
json.EndObject();
}
#endif
#ifndef _VMA_MAPPING_HYSTERESIS
class VmaMappingHysteresis
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaMappingHysteresis)
public:
VmaMappingHysteresis() = default;
uint32_t GetExtraMapping() const { return m_ExtraMapping; }
bool PostMap()
{
#if VMA_MAPPING_HYSTERESIS_ENABLED
if(m_ExtraMapping == 0)
{
++m_MajorCounter;
if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)
{
m_ExtraMapping = 1;
m_MajorCounter = 0;
m_MinorCounter = 0;
return true;
}
}
else
PostMinorCounter();
#endif
return false;
}
void PostUnmap()
{
#if VMA_MAPPING_HYSTERESIS_ENABLED
if(m_ExtraMapping == 0)
++m_MajorCounter;
else
PostMinorCounter();
#endif
}
void PostAlloc()
{
#if VMA_MAPPING_HYSTERESIS_ENABLED
if(m_ExtraMapping == 1)
++m_MajorCounter;
else
PostMinorCounter();
#endif
}
bool PostFree()
{
#if VMA_MAPPING_HYSTERESIS_ENABLED
if(m_ExtraMapping == 1)
{
++m_MajorCounter;
if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&
m_MajorCounter > m_MinorCounter + 1)
{
m_ExtraMapping = 0;
m_MajorCounter = 0;
m_MinorCounter = 0;
return true;
}
}
else
PostMinorCounter();
#endif
return false;
}
private:
static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;
uint32_t m_MinorCounter = 0;
uint32_t m_MajorCounter = 0;
uint32_t m_ExtraMapping = 0;
void PostMinorCounter()
{
if(m_MinorCounter < m_MajorCounter)
{
++m_MinorCounter;
}
else if(m_MajorCounter > 0)
{
--m_MajorCounter;
--m_MinorCounter;
}
}
};
#endif
#ifndef _VMA_DEVICE_MEMORY_BLOCK
class VmaDeviceMemoryBlock
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaDeviceMemoryBlock)
public:
VmaBlockMetadata* m_pMetadata;
VmaDeviceMemoryBlock(VmaAllocator hAllocator);
~VmaDeviceMemoryBlock();
void Init(
VmaAllocator hAllocator,
VmaPool hParentPool,
uint32_t newMemoryTypeIndex,
VkDeviceMemory newMemory,
VkDeviceSize newSize,
uint32_t id,
uint32_t algorithm,
VkDeviceSize bufferImageGranularity);
void Destroy(VmaAllocator allocator);
VmaPool GetParentPool() const { return m_hParentPool; }
VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
uint32_t GetId() const { return m_Id; }
void* GetMappedData() const { return m_pMappedData; }
uint32_t GetMapRefCount() const { return m_MapCount; }
void PostAlloc(VmaAllocator hAllocator);
void PostFree(VmaAllocator hAllocator);
bool Validate() const;
VkResult CheckCorruption(VmaAllocator hAllocator);
VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
void Unmap(VmaAllocator hAllocator, uint32_t count);
VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
VkResult BindBufferMemory(
const VmaAllocator hAllocator,
const VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkBuffer hBuffer,
const void* pNext);
VkResult BindImageMemory(
const VmaAllocator hAllocator,
const VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext);
private:
VmaPool m_hParentPool;
uint32_t m_MemoryTypeIndex;
uint32_t m_Id;
VkDeviceMemory m_hMemory;
VMA_MUTEX m_MapAndBindMutex;
VmaMappingHysteresis m_MappingHysteresis;
uint32_t m_MapCount;
void* m_pMappedData;
};
#endif
#ifndef _VMA_ALLOCATION_T
struct VmaAllocation_T
{
friend struct VmaDedicatedAllocationListItemTraits;
enum FLAGS
{
FLAG_PERSISTENT_MAP = 0x01,
FLAG_MAPPING_ALLOWED = 0x02,
};
public:
enum ALLOCATION_TYPE
{
ALLOCATION_TYPE_NONE,
ALLOCATION_TYPE_BLOCK,
ALLOCATION_TYPE_DEDICATED,
};
VmaAllocation_T(bool mappingAllowed);
~VmaAllocation_T();
void InitBlockAllocation(
VmaDeviceMemoryBlock* block,
VmaAllocHandle allocHandle,
VkDeviceSize alignment,
VkDeviceSize size,
uint32_t memoryTypeIndex,
VmaSuballocationType suballocationType,
bool mapped);
void InitDedicatedAllocation(
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
VmaSuballocationType suballocationType,
void* pMappedData,
VkDeviceSize size);
ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
VkDeviceSize GetAlignment() const { return m_Alignment; }
VkDeviceSize GetSize() const { return m_Size; }
void* GetUserData() const { return m_pUserData; }
const char* GetName() const { return m_pName; }
VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }
bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }
void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }
void SetName(VmaAllocator hAllocator, const char* pName);
void FreeName(VmaAllocator hAllocator);
uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);
VmaAllocHandle GetAllocHandle() const;
VkDeviceSize GetOffset() const;
VmaPool GetParentPool() const;
VkDeviceMemory GetMemory() const;
void* GetMappedData() const;
void BlockAllocMap();
void BlockAllocUnmap();
VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
void DedicatedAllocUnmap(VmaAllocator hAllocator);
#if VMA_STATS_STRING_ENABLED
uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
void InitBufferImageUsage(uint32_t bufferImageUsage);
void PrintParameters(class VmaJsonWriter& json) const;
#endif
private:
struct BlockAllocation
{
VmaDeviceMemoryBlock* m_Block;
VmaAllocHandle m_AllocHandle;
};
struct DedicatedAllocation
{
VmaPool m_hParentPool;
VkDeviceMemory m_hMemory;
void* m_pMappedData;
VmaAllocation_T* m_Prev;
VmaAllocation_T* m_Next;
};
union
{
BlockAllocation m_BlockAllocation;
DedicatedAllocation m_DedicatedAllocation;
};
VkDeviceSize m_Alignment;
VkDeviceSize m_Size;
void* m_pUserData;
char* m_pName;
uint32_t m_MemoryTypeIndex;
uint8_t m_Type;
uint8_t m_SuballocationType;
uint8_t m_MapCount;
uint8_t m_Flags;
#if VMA_STATS_STRING_ENABLED
uint32_t m_BufferImageUsage;
#endif
};
#endif
#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
struct VmaDedicatedAllocationListItemTraits
{
typedef VmaAllocation_T ItemType;
static ItemType* GetPrev(const ItemType* item)
{
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
return item->m_DedicatedAllocation.m_Prev;
}
static ItemType* GetNext(const ItemType* item)
{
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
return item->m_DedicatedAllocation.m_Next;
}
static ItemType*& AccessPrev(ItemType* item)
{
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
return item->m_DedicatedAllocation.m_Prev;
}
static ItemType*& AccessNext(ItemType* item)
{
VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
return item->m_DedicatedAllocation.m_Next;
}
};
#endif
#ifndef _VMA_DEDICATED_ALLOCATION_LIST
class VmaDedicatedAllocationList
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaDedicatedAllocationList)
public:
VmaDedicatedAllocationList() {}
~VmaDedicatedAllocationList();
void Init(bool useMutex) { m_UseMutex = useMutex; }
bool Validate();
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
void AddStatistics(VmaStatistics& inoutStats);
#if VMA_STATS_STRING_ENABLED
void BuildStatsString(VmaJsonWriter& json);
#endif
bool IsEmpty();
void Register(VmaAllocation alloc);
void Unregister(VmaAllocation alloc);
private:
typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
bool m_UseMutex = true;
VMA_RW_MUTEX m_Mutex;
DedicatedAllocationLinkedList m_AllocationList;
};
#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
VmaDedicatedAllocationList::~VmaDedicatedAllocationList()
{
VMA_HEAVY_ASSERT(Validate());
if (!m_AllocationList.IsEmpty())
{
VMA_ASSERT(false && "Unfreed dedicated allocations found!");
}
}
bool VmaDedicatedAllocationList::Validate()
{
const size_t declaredCount = m_AllocationList.GetCount();
size_t actualCount = 0;
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
for (VmaAllocation alloc = m_AllocationList.Front();
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
{
++actualCount;
}
VMA_VALIDATE(actualCount == declaredCount);
return true;
}
void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
{
for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
{
const VkDeviceSize size = item->GetSize();
inoutStats.statistics.blockCount++;
inoutStats.statistics.blockBytes += size;
VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());
}
}
void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)
{
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();
inoutStats.blockCount += allocCount;
inoutStats.allocationCount += allocCount;
for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
{
const VkDeviceSize size = item->GetSize();
inoutStats.blockBytes += size;
inoutStats.allocationBytes += size;
}
}
#if VMA_STATS_STRING_ENABLED
void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
{
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
json.BeginArray();
for (VmaAllocation alloc = m_AllocationList.Front();
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
{
json.BeginObject(true);
alloc->PrintParameters(json);
json.EndObject();
}
json.EndArray();
}
#endif
bool VmaDedicatedAllocationList::IsEmpty()
{
VmaMutexLockRead lock(m_Mutex, m_UseMutex);
return m_AllocationList.IsEmpty();
}
void VmaDedicatedAllocationList::Register(VmaAllocation alloc)
{
VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
m_AllocationList.PushBack(alloc);
}
void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)
{
VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
m_AllocationList.Remove(alloc);
}
#endif
#endif
#ifndef _VMA_SUBALLOCATION
struct VmaSuballocation
{
VkDeviceSize offset;
VkDeviceSize size;
void* userData;
VmaSuballocationType type;
};
struct VmaSuballocationOffsetLess
{
bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
{
return lhs.offset < rhs.offset;
}
};
struct VmaSuballocationOffsetGreater
{
bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
{
return lhs.offset > rhs.offset;
}
};
struct VmaSuballocationItemSizeLess
{
bool operator()(const VmaSuballocationList::iterator lhs,
const VmaSuballocationList::iterator rhs) const
{
return lhs->size < rhs->size;
}
bool operator()(const VmaSuballocationList::iterator lhs,
VkDeviceSize rhsSize) const
{
return lhs->size < rhsSize;
}
};
#endif
#ifndef _VMA_ALLOCATION_REQUEST
struct VmaAllocationRequest
{
VmaAllocHandle allocHandle;
VkDeviceSize size;
VmaSuballocationList::iterator item;
void* customData;
uint64_t algorithmData;
VmaAllocationRequestType type;
};
#endif
#ifndef _VMA_BLOCK_METADATA
class VmaBlockMetadata
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata)
public:
VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual);
virtual ~VmaBlockMetadata() = default;
virtual void Init(VkDeviceSize size) { m_Size = size; }
bool IsVirtual() const { return m_IsVirtual; }
VkDeviceSize GetSize() const { return m_Size; }
virtual bool Validate() const = 0;
virtual size_t GetAllocationCount() const = 0;
virtual size_t GetFreeRegionsCount() const = 0;
virtual VkDeviceSize GetSumFreeSize() const = 0;
virtual bool IsEmpty() const = 0;
virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;
virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;
virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;
virtual VmaAllocHandle GetAllocationListBegin() const = 0;
virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;
virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;
virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;
virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;
#if VMA_STATS_STRING_ENABLED
virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
#endif
virtual bool CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) = 0;
virtual VkResult CheckCorruption(const void* pBlockData) = 0;
virtual void Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData) = 0;
virtual void Free(VmaAllocHandle allocHandle) = 0;
virtual void Clear() = 0;
virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;
virtual void DebugLogAllAllocations() const = 0;
protected:
const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
VkDeviceSize GetDebugMargin() const { return VkDeviceSize(IsVirtual() ? 0 : VMA_DEBUG_MARGIN); }
void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap_Begin(class VmaJsonWriter& json,
VkDeviceSize unusedBytes,
size_t allocationCount,
size_t unusedRangeCount) const;
void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
VkDeviceSize offset, VkDeviceSize size, void* userData) const;
void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
VkDeviceSize offset,
VkDeviceSize size) const;
void PrintDetailedMap_End(class VmaJsonWriter& json) const;
#endif
private:
VkDeviceSize m_Size;
const VkAllocationCallbacks* m_pAllocationCallbacks;
const VkDeviceSize m_BufferImageGranularity;
const bool m_IsVirtual;
};
#ifndef _VMA_BLOCK_METADATA_FUNCTIONS
VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual)
: m_Size(0),
m_pAllocationCallbacks(pAllocationCallbacks),
m_BufferImageGranularity(bufferImageGranularity),
m_IsVirtual(isVirtual) {}
void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const
{
if (IsVirtual())
{
VMA_DEBUG_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData);
}
else
{
VMA_ASSERT(userData != VMA_NULL);
VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);
userData = allocation->GetUserData();
const char* name = allocation->GetName();
#if VMA_STATS_STRING_ENABLED
VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u",
offset, size, userData, name ? name : "vma_empty",
VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],
allocation->GetBufferImageUsage());
#else
VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u",
offset, size, userData, name ? name : "vma_empty",
(uint32_t)allocation->GetSuballocationType());
#endif
}
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
{
json.WriteString("TotalBytes");
json.WriteNumber(GetSize());
json.WriteString("UnusedBytes");
json.WriteNumber(unusedBytes);
json.WriteString("Allocations");
json.WriteNumber((uint64_t)allocationCount);
json.WriteString("UnusedRanges");
json.WriteNumber((uint64_t)unusedRangeCount);
json.WriteString("Suballocations");
json.BeginArray();
}
void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
VkDeviceSize offset, VkDeviceSize size, void* userData) const
{
json.BeginObject(true);
json.WriteString("Offset");
json.WriteNumber(offset);
if (IsVirtual())
{
json.WriteString("Size");
json.WriteNumber(size);
if (userData)
{
json.WriteString("CustomData");
json.BeginString();
json.ContinueString_Pointer(userData);
json.EndString();
}
}
else
{
((VmaAllocation)userData)->PrintParameters(json);
}
json.EndObject();
}
void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
VkDeviceSize offset, VkDeviceSize size) const
{
json.BeginObject(true);
json.WriteString("Offset");
json.WriteNumber(offset);
json.WriteString("Type");
json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
json.WriteString("Size");
json.WriteNumber(size);
json.EndObject();
}
void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
{
json.EndArray();
}
#endif
#endif
#endif
#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
class VmaBlockBufferImageGranularity final
{
public:
struct ValidationContext
{
const VkAllocationCallbacks* allocCallbacks;
uint16_t* pageAllocs;
};
VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);
~VmaBlockBufferImageGranularity();
bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }
void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);
void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);
void RoundupAllocRequest(VmaSuballocationType allocType,
VkDeviceSize& inOutAllocSize,
VkDeviceSize& inOutAllocAlignment) const;
bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
VkDeviceSize allocSize,
VkDeviceSize blockOffset,
VkDeviceSize blockSize,
VmaSuballocationType allocType) const;
void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);
void FreePages(VkDeviceSize offset, VkDeviceSize size);
void Clear();
ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,
bool isVirutal) const;
bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;
bool FinishValidation(ValidationContext& ctx) const;
private:
static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;
struct RegionInfo
{
uint8_t allocType;
uint16_t allocCount;
};
VkDeviceSize m_BufferImageGranularity;
uint32_t m_RegionCount;
RegionInfo* m_RegionInfo;
uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }
uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }
uint32_t OffsetToPageIndex(VkDeviceSize offset) const;
void AllocPage(RegionInfo& page, uint8_t allocType);
};
#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)
: m_BufferImageGranularity(bufferImageGranularity),
m_RegionCount(0),
m_RegionInfo(VMA_NULL) {}
VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()
{
VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");
}
void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)
{
if (IsEnabled())
{
m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));
m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);
memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
}
}
void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)
{
if (m_RegionInfo)
{
vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);
m_RegionInfo = VMA_NULL;
}
}
void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,
VkDeviceSize& inOutAllocSize,
VkDeviceSize& inOutAllocAlignment) const
{
if (m_BufferImageGranularity > 1 &&
m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)
{
if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
{
inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);
inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);
}
}
}
bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
VkDeviceSize allocSize,
VkDeviceSize blockOffset,
VkDeviceSize blockSize,
VmaSuballocationType allocType) const
{
if (IsEnabled())
{
uint32_t startPage = GetStartPage(inOutAllocOffset);
if (m_RegionInfo[startPage].allocCount > 0 &&
VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))
{
inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);
if (blockSize < allocSize + inOutAllocOffset - blockOffset)
return true;
++startPage;
}
uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);
if (endPage != startPage &&
m_RegionInfo[endPage].allocCount > 0 &&
VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))
{
return true;
}
}
return false;
}
void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)
{
if (IsEnabled())
{
uint32_t startPage = GetStartPage(offset);
AllocPage(m_RegionInfo[startPage], allocType);
uint32_t endPage = GetEndPage(offset, size);
if (startPage != endPage)
AllocPage(m_RegionInfo[endPage], allocType);
}
}
void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)
{
if (IsEnabled())
{
uint32_t startPage = GetStartPage(offset);
--m_RegionInfo[startPage].allocCount;
if (m_RegionInfo[startPage].allocCount == 0)
m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
uint32_t endPage = GetEndPage(offset, size);
if (startPage != endPage)
{
--m_RegionInfo[endPage].allocCount;
if (m_RegionInfo[endPage].allocCount == 0)
m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
}
}
}
void VmaBlockBufferImageGranularity::Clear()
{
if (m_RegionInfo)
memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
}
VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(
const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const
{
ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };
if (!isVirutal && IsEnabled())
{
ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);
memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));
}
return ctx;
}
bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,
VkDeviceSize offset, VkDeviceSize size) const
{
if (IsEnabled())
{
uint32_t start = GetStartPage(offset);
++ctx.pageAllocs[start];
VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);
uint32_t end = GetEndPage(offset, size);
if (start != end)
{
++ctx.pageAllocs[end];
VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);
}
}
return true;
}
bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const
{
if (IsEnabled())
{
VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");
for (uint32_t page = 0; page < m_RegionCount; ++page)
{
VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);
}
vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);
ctx.pageAllocs = VMA_NULL;
}
return true;
}
uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const
{
return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));
}
void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)
{
if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))
page.allocType = allocType;
++page.allocCount;
}
#endif
#endif
#if 0
#ifndef _VMA_BLOCK_METADATA_GENERIC
class VmaBlockMetadata_Generic : public VmaBlockMetadata
{
friend class VmaDefragmentationAlgorithm_Generic;
friend class VmaDefragmentationAlgorithm_Fast;
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Generic)
public:
VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual);
virtual ~VmaBlockMetadata_Generic() = default;
size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }
VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); }
void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); }
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }
void Init(VkDeviceSize size) override;
bool Validate() const override;
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
#endif
bool CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) override;
VkResult CheckCorruption(const void* pBlockData) override;
void Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData) override;
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
VmaAllocHandle GetAllocationListBegin() const override;
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
void Clear() override;
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
void DebugLogAllAllocations() const override;
private:
uint32_t m_FreeCount;
VkDeviceSize m_SumFreeSize;
VmaSuballocationList m_Suballocations;
VmaVector<VmaSuballocationList::iterator, VmaStlAllocator<VmaSuballocationList::iterator>> m_FreeSuballocationsBySize;
VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); }
VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const;
bool ValidateFreeSuballocationList() const;
bool CheckAllocation(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
VmaSuballocationList::const_iterator suballocItem,
VmaAllocHandle* pAllocHandle) const;
void MergeFreeWithNext(VmaSuballocationList::iterator item);
VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
};
#ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS
VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual)
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
m_FreeCount(0),
m_SumFreeSize(0),
m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks)) {}
void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
{
VmaBlockMetadata::Init(size);
m_FreeCount = 1;
m_SumFreeSize = size;
VmaSuballocation suballoc = {};
suballoc.offset = 0;
suballoc.size = size;
suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
m_Suballocations.push_back(suballoc);
m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
}
bool VmaBlockMetadata_Generic::Validate() const
{
VMA_VALIDATE(!m_Suballocations.empty());
VkDeviceSize calculatedOffset = 0;
uint32_t calculatedFreeCount = 0;
VkDeviceSize calculatedSumFreeSize = 0;
size_t freeSuballocationsToRegister = 0;
bool prevFree = false;
const VkDeviceSize debugMargin = GetDebugMargin();
for (const auto& subAlloc : m_Suballocations)
{
VMA_VALIDATE(subAlloc.offset == calculatedOffset);
const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_VALIDATE(!prevFree || !currFree);
VmaAllocation alloc = (VmaAllocation)subAlloc.userData;
if (!IsVirtual())
{
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
}
if (currFree)
{
calculatedSumFreeSize += subAlloc.size;
++calculatedFreeCount;
++freeSuballocationsToRegister;
VMA_VALIDATE(subAlloc.size >= debugMargin);
}
else
{
if (!IsVirtual())
{
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1);
VMA_VALIDATE(alloc->GetSize() == subAlloc.size);
}
VMA_VALIDATE(debugMargin == 0 || prevFree);
}
calculatedOffset += subAlloc.size;
prevFree = currFree;
}
VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
VkDeviceSize lastSize = 0;
for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
{
VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_VALIDATE(suballocItem->size >= lastSize);
lastSize = suballocItem->size;
}
VMA_VALIDATE(ValidateFreeSuballocationList());
VMA_VALIDATE(calculatedOffset == GetSize());
VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
return true;
}
void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
{
const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
inoutStats.statistics.blockCount++;
inoutStats.statistics.blockBytes += GetSize();
for (const auto& suballoc : m_Suballocations)
{
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
else
VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);
}
}
void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const
{
inoutStats.blockCount++;
inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount;
inoutStats.blockBytes += GetSize();
inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
{
PrintDetailedMap_Begin(json,
m_SumFreeSize,
m_Suballocations.size() - (size_t)m_FreeCount,
m_FreeCount,
mapRefCount);
for (const auto& suballoc : m_Suballocations)
{
if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
{
PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
}
else
{
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
}
}
PrintDetailedMap_End(json);
}
#endif
bool VmaBlockMetadata_Generic::CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
VMA_ASSERT(allocSize > 0);
VMA_ASSERT(!upperAddress);
VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT(pAllocationRequest != VMA_NULL);
VMA_HEAVY_ASSERT(Validate());
allocSize = AlignAllocationSize(allocSize);
pAllocationRequest->type = VmaAllocationRequestType::Normal;
pAllocationRequest->size = allocSize;
const VkDeviceSize debugMargin = GetDebugMargin();
if (m_SumFreeSize < allocSize + debugMargin)
{
return false;
}
const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
if (freeSuballocCount > 0)
{
if (strategy == 0 ||
strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
{
VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
m_FreeSuballocationsBySize.data(),
m_FreeSuballocationsBySize.data() + freeSuballocCount,
allocSize + debugMargin,
VmaSuballocationItemSizeLess());
size_t index = it - m_FreeSuballocationsBySize.data();
for (; index < freeSuballocCount; ++index)
{
if (CheckAllocation(
allocSize,
allocAlignment,
allocType,
m_FreeSuballocationsBySize[index],
&pAllocationRequest->allocHandle))
{
pAllocationRequest->item = m_FreeSuballocationsBySize[index];
return true;
}
}
}
else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
{
for (VmaSuballocationList::iterator it = m_Suballocations.begin();
it != m_Suballocations.end();
++it)
{
if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
allocSize,
allocAlignment,
allocType,
it,
&pAllocationRequest->allocHandle))
{
pAllocationRequest->item = it;
return true;
}
}
}
else
{
VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ));
for (size_t index = freeSuballocCount; index--; )
{
if (CheckAllocation(
allocSize,
allocAlignment,
allocType,
m_FreeSuballocationsBySize[index],
&pAllocationRequest->allocHandle))
{
pAllocationRequest->item = m_FreeSuballocationsBySize[index];
return true;
}
}
}
}
return false;
}
VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
{
for (auto& suballoc : m_Suballocations)
{
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
{
if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
{
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
return VK_ERROR_UNKNOWN_COPY;
}
}
}
return VK_SUCCESS;
}
void VmaBlockMetadata_Generic::Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData)
{
VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
VMA_ASSERT(request.item != m_Suballocations.end());
VmaSuballocation& suballoc = *request.item;
VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset);
const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1;
VMA_ASSERT(suballoc.size >= paddingBegin + request.size);
const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;
UnregisterFreeSuballocation(request.item);
suballoc.offset = (VkDeviceSize)request.allocHandle - 1;
suballoc.size = request.size;
suballoc.type = type;
suballoc.userData = userData;
if (paddingEnd)
{
VmaSuballocation paddingSuballoc = {};
paddingSuballoc.offset = suballoc.offset + suballoc.size;
paddingSuballoc.size = paddingEnd;
paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
VmaSuballocationList::iterator next = request.item;
++next;
const VmaSuballocationList::iterator paddingEndItem =
m_Suballocations.insert(next, paddingSuballoc);
RegisterFreeSuballocation(paddingEndItem);
}
if (paddingBegin)
{
VmaSuballocation paddingSuballoc = {};
paddingSuballoc.offset = suballoc.offset - paddingBegin;
paddingSuballoc.size = paddingBegin;
paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
const VmaSuballocationList::iterator paddingBeginItem =
m_Suballocations.insert(request.item, paddingSuballoc);
RegisterFreeSuballocation(paddingBeginItem);
}
m_FreeCount = m_FreeCount - 1;
if (paddingBegin > 0)
{
++m_FreeCount;
}
if (paddingEnd > 0)
{
++m_FreeCount;
}
m_SumFreeSize -= request.size;
}
void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
{
outInfo.offset = (VkDeviceSize)allocHandle - 1;
const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset);
outInfo.size = suballoc.size;
outInfo.pUserData = suballoc.userData;
}
void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const
{
return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData;
}
VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const
{
if (IsEmpty())
return VK_NULL_HANDLE;
for (const auto& suballoc : m_Suballocations)
{
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
return (VmaAllocHandle)(suballoc.offset + 1);
}
VMA_ASSERT(false && "Should contain at least 1 allocation!");
return VK_NULL_HANDLE;
}
VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const
{
VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1);
for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it)
{
if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
return (VmaAllocHandle)(it->offset + 1);
}
return VK_NULL_HANDLE;
}
void VmaBlockMetadata_Generic::Clear()
{
const VkDeviceSize size = GetSize();
VMA_ASSERT(IsVirtual());
m_FreeCount = 1;
m_SumFreeSize = size;
m_Suballocations.clear();
m_FreeSuballocationsBySize.clear();
VmaSuballocation suballoc = {};
suballoc.offset = 0;
suballoc.size = size;
suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
m_Suballocations.push_back(suballoc);
m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
}
void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
{
VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1);
suballoc.userData = userData;
}
void VmaBlockMetadata_Generic::DebugLogAllAllocations() const
{
for (const auto& suballoc : m_Suballocations)
{
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData);
}
}
VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const
{
VMA_HEAVY_ASSERT(!m_Suballocations.empty());
const VkDeviceSize last = m_Suballocations.rbegin()->offset;
if (last == offset)
return m_Suballocations.rbegin().drop_const();
const VkDeviceSize first = m_Suballocations.begin()->offset;
if (first == offset)
return m_Suballocations.begin().drop_const();
const size_t suballocCount = m_Suballocations.size();
const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount;
auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator
{
for (auto suballocItem = begin;
suballocItem != end;
++suballocItem)
{
if (suballocItem->offset == offset)
return suballocItem.drop_const();
}
VMA_ASSERT(false && "Not found!");
return m_Suballocations.end().drop_const();
};
if (offset - first > suballocCount * step / 2)
{
return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend());
}
return findSuballocation(m_Suballocations.begin(), m_Suballocations.end());
}
bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
{
VkDeviceSize lastSize = 0;
for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
{
const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_VALIDATE(it->size >= lastSize);
lastSize = it->size;
}
return true;
}
bool VmaBlockMetadata_Generic::CheckAllocation(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
VmaSuballocationList::const_iterator suballocItem,
VmaAllocHandle* pAllocHandle) const
{
VMA_ASSERT(allocSize > 0);
VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT(suballocItem != m_Suballocations.cend());
VMA_ASSERT(pAllocHandle != VMA_NULL);
const VkDeviceSize debugMargin = GetDebugMargin();
const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
const VmaSuballocation& suballoc = *suballocItem;
VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
if (suballoc.size < allocSize)
{
return false;
}
VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());
if (debugMargin > 0)
{
offset += debugMargin;
}
offset = VmaAlignUp(offset, allocAlignment);
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
{
bool bufferImageGranularityConflict = false;
VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
while (prevSuballocItem != m_Suballocations.cbegin())
{
--prevSuballocItem;
const VmaSuballocation& prevSuballoc = *prevSuballocItem;
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
{
bufferImageGranularityConflict = true;
break;
}
}
else
break;
}
if (bufferImageGranularityConflict)
{
offset = VmaAlignUp(offset, bufferImageGranularity);
}
}
const VkDeviceSize paddingBegin = offset - suballoc.offset;
if (paddingBegin + allocSize + debugMargin > suballoc.size)
{
return false;
}
if (allocSize % bufferImageGranularity || offset % bufferImageGranularity)
{
VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
++nextSuballocItem;
while (nextSuballocItem != m_Suballocations.cend())
{
const VmaSuballocation& nextSuballoc = *nextSuballocItem;
if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
{
return false;
}
}
else
{
break;
}
++nextSuballocItem;
}
}
*pAllocHandle = (VmaAllocHandle)(offset + 1);
return true;
}
void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
{
VMA_ASSERT(item != m_Suballocations.end());
VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
VmaSuballocationList::iterator nextItem = item;
++nextItem;
VMA_ASSERT(nextItem != m_Suballocations.end());
VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
item->size += nextItem->size;
--m_FreeCount;
m_Suballocations.erase(nextItem);
}
VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
{
VmaSuballocation& suballoc = *suballocItem;
suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
suballoc.userData = VMA_NULL;
++m_FreeCount;
m_SumFreeSize += suballoc.size;
bool mergeWithNext = false;
bool mergeWithPrev = false;
VmaSuballocationList::iterator nextItem = suballocItem;
++nextItem;
if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
{
mergeWithNext = true;
}
VmaSuballocationList::iterator prevItem = suballocItem;
if (suballocItem != m_Suballocations.begin())
{
--prevItem;
if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
{
mergeWithPrev = true;
}
}
if (mergeWithNext)
{
UnregisterFreeSuballocation(nextItem);
MergeFreeWithNext(suballocItem);
}
if (mergeWithPrev)
{
UnregisterFreeSuballocation(prevItem);
MergeFreeWithNext(prevItem);
RegisterFreeSuballocation(prevItem);
return prevItem;
}
else
{
RegisterFreeSuballocation(suballocItem);
return suballocItem;
}
}
void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
{
VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT(item->size > 0);
VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
if (m_FreeSuballocationsBySize.empty())
{
m_FreeSuballocationsBySize.push_back(item);
}
else
{
VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
}
}
void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
{
VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT(item->size > 0);
VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
m_FreeSuballocationsBySize.data(),
m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
item,
VmaSuballocationItemSizeLess());
for (size_t index = it - m_FreeSuballocationsBySize.data();
index < m_FreeSuballocationsBySize.size();
++index)
{
if (m_FreeSuballocationsBySize[index] == item)
{
VmaVectorRemove(m_FreeSuballocationsBySize, index);
return;
}
VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
}
VMA_ASSERT(0 && "Not found.");
}
#endif
#endif
#endif
#ifndef _VMA_BLOCK_METADATA_LINEAR
class VmaBlockMetadata_Linear : public VmaBlockMetadata
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Linear)
public:
VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual);
virtual ~VmaBlockMetadata_Linear() = default;
VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
bool IsEmpty() const override { return GetAllocationCount() == 0; }
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }
void Init(VkDeviceSize size) override;
bool Validate() const override;
size_t GetAllocationCount() const override;
size_t GetFreeRegionsCount() const override;
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json) const override;
#endif
bool CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) override;
VkResult CheckCorruption(const void* pBlockData) override;
void Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData) override;
void Free(VmaAllocHandle allocHandle) override;
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
VmaAllocHandle GetAllocationListBegin() const override;
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
void Clear() override;
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
void DebugLogAllAllocations() const override;
private:
typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;
enum SECOND_VECTOR_MODE
{
SECOND_VECTOR_EMPTY,
SECOND_VECTOR_RING_BUFFER,
SECOND_VECTOR_DOUBLE_STACK,
};
VkDeviceSize m_SumFreeSize;
SuballocationVectorType m_Suballocations0, m_Suballocations1;
uint32_t m_1stVectorIndex;
SECOND_VECTOR_MODE m_2ndVectorMode;
size_t m_1stNullItemsBeginCount;
size_t m_1stNullItemsMiddleCount;
size_t m_2ndNullItemsCount;
SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;
bool ShouldCompact1st() const;
void CleanupAfterFree();
bool CreateAllocationRequest_LowerAddress(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest);
bool CreateAllocationRequest_UpperAddress(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest);
};
#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual)
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
m_SumFreeSize(0),
m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
m_1stVectorIndex(0),
m_2ndVectorMode(SECOND_VECTOR_EMPTY),
m_1stNullItemsBeginCount(0),
m_1stNullItemsMiddleCount(0),
m_2ndNullItemsCount(0) {}
void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
{
VmaBlockMetadata::Init(size);
m_SumFreeSize = size;
}
bool VmaBlockMetadata_Linear::Validate() const
{
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
VMA_VALIDATE(!suballocations1st.empty() ||
suballocations2nd.empty() ||
m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
if (!suballocations1st.empty())
{
VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
}
if (!suballocations2nd.empty())
{
VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
}
VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
VkDeviceSize sumUsedSize = 0;
const size_t suballoc1stCount = suballocations1st.size();
const VkDeviceSize debugMargin = GetDebugMargin();
VkDeviceSize offset = 0;
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
const size_t suballoc2ndCount = suballocations2nd.size();
size_t nullItem2ndCount = 0;
for (size_t i = 0; i < suballoc2ndCount; ++i)
{
const VmaSuballocation& suballoc = suballocations2nd[i];
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
if (!IsVirtual())
{
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
}
VMA_VALIDATE(suballoc.offset >= offset);
if (!currFree)
{
if (!IsVirtual())
{
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
}
sumUsedSize += suballoc.size;
}
else
{
++nullItem2ndCount;
}
offset = suballoc.offset + suballoc.size + debugMargin;
}
VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
}
for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
{
const VmaSuballocation& suballoc = suballocations1st[i];
VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
suballoc.userData == VMA_NULL);
}
size_t nullItem1stCount = m_1stNullItemsBeginCount;
for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
{
const VmaSuballocation& suballoc = suballocations1st[i];
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
if (!IsVirtual())
{
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
}
VMA_VALIDATE(suballoc.offset >= offset);
VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
if (!currFree)
{
if (!IsVirtual())
{
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
}
sumUsedSize += suballoc.size;
}
else
{
++nullItem1stCount;
}
offset = suballoc.offset + suballoc.size + debugMargin;
}
VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
const size_t suballoc2ndCount = suballocations2nd.size();
size_t nullItem2ndCount = 0;
for (size_t i = suballoc2ndCount; i--; )
{
const VmaSuballocation& suballoc = suballocations2nd[i];
const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
if (!IsVirtual())
{
VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
}
VMA_VALIDATE(suballoc.offset >= offset);
if (!currFree)
{
if (!IsVirtual())
{
VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
VMA_VALIDATE(alloc->GetSize() == suballoc.size);
}
sumUsedSize += suballoc.size;
}
else
{
++nullItem2ndCount;
}
offset = suballoc.offset + suballoc.size + debugMargin;
}
VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
}
VMA_VALIDATE(offset <= GetSize());
VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
return true;
}
size_t VmaBlockMetadata_Linear::GetAllocationCount() const
{
return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
AccessSuballocations2nd().size() - m_2ndNullItemsCount;
}
size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const
{
VMA_ASSERT(0);
return SIZE_MAX;
}
void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
{
const VkDeviceSize size = GetSize();
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
const size_t suballoc1stCount = suballocations1st.size();
const size_t suballoc2ndCount = suballocations2nd.size();
inoutStats.statistics.blockCount++;
inoutStats.statistics.blockBytes += size;
VkDeviceSize lastOffset = 0;
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
size_t nextAlloc2ndIndex = 0;
while (lastOffset < freeSpace2ndTo1stEnd)
{
while (nextAlloc2ndIndex < suballoc2ndCount &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
++nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex < suballoc2ndCount)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc2ndIndex;
}
else
{
if (lastOffset < freeSpace2ndTo1stEnd)
{
const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
lastOffset = freeSpace2ndTo1stEnd;
}
}
}
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
const VkDeviceSize freeSpace1stTo2ndEnd =
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
while (lastOffset < freeSpace1stTo2ndEnd)
{
while (nextAlloc1stIndex < suballoc1stCount &&
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
{
++nextAlloc1stIndex;
}
if (nextAlloc1stIndex < suballoc1stCount)
{
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc1stIndex;
}
else
{
if (lastOffset < freeSpace1stTo2ndEnd)
{
const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
lastOffset = freeSpace1stTo2ndEnd;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
while (lastOffset < size)
{
while (nextAlloc2ndIndex != SIZE_MAX &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
--nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex != SIZE_MAX)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
lastOffset = suballoc.offset + suballoc.size;
--nextAlloc2ndIndex;
}
else
{
if (lastOffset < size)
{
const VkDeviceSize unusedRangeSize = size - lastOffset;
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
}
lastOffset = size;
}
}
}
}
void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const
{
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
const VkDeviceSize size = GetSize();
const size_t suballoc1stCount = suballocations1st.size();
const size_t suballoc2ndCount = suballocations2nd.size();
inoutStats.blockCount++;
inoutStats.blockBytes += size;
inoutStats.allocationBytes += size - m_SumFreeSize;
VkDeviceSize lastOffset = 0;
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
while (lastOffset < freeSpace2ndTo1stEnd)
{
while (nextAlloc2ndIndex < suballoc2ndCount &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
++nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex < suballoc2ndCount)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
++inoutStats.allocationCount;
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc2ndIndex;
}
else
{
lastOffset = freeSpace2ndTo1stEnd;
}
}
}
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
const VkDeviceSize freeSpace1stTo2ndEnd =
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
while (lastOffset < freeSpace1stTo2ndEnd)
{
while (nextAlloc1stIndex < suballoc1stCount &&
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
{
++nextAlloc1stIndex;
}
if (nextAlloc1stIndex < suballoc1stCount)
{
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
++inoutStats.allocationCount;
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc1stIndex;
}
else
{
lastOffset = freeSpace1stTo2ndEnd;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
while (lastOffset < size)
{
while (nextAlloc2ndIndex != SIZE_MAX &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
--nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex != SIZE_MAX)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
++inoutStats.allocationCount;
lastOffset = suballoc.offset + suballoc.size;
--nextAlloc2ndIndex;
}
else
{
lastOffset = size;
}
}
}
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
{
const VkDeviceSize size = GetSize();
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
const size_t suballoc1stCount = suballocations1st.size();
const size_t suballoc2ndCount = suballocations2nd.size();
size_t unusedRangeCount = 0;
VkDeviceSize usedBytes = 0;
VkDeviceSize lastOffset = 0;
size_t alloc2ndCount = 0;
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
size_t nextAlloc2ndIndex = 0;
while (lastOffset < freeSpace2ndTo1stEnd)
{
while (nextAlloc2ndIndex < suballoc2ndCount &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
++nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex < suballoc2ndCount)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
++unusedRangeCount;
}
++alloc2ndCount;
usedBytes += suballoc.size;
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc2ndIndex;
}
else
{
if (lastOffset < freeSpace2ndTo1stEnd)
{
++unusedRangeCount;
}
lastOffset = freeSpace2ndTo1stEnd;
}
}
}
size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
size_t alloc1stCount = 0;
const VkDeviceSize freeSpace1stTo2ndEnd =
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
while (lastOffset < freeSpace1stTo2ndEnd)
{
while (nextAlloc1stIndex < suballoc1stCount &&
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
{
++nextAlloc1stIndex;
}
if (nextAlloc1stIndex < suballoc1stCount)
{
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
if (lastOffset < suballoc.offset)
{
++unusedRangeCount;
}
++alloc1stCount;
usedBytes += suballoc.size;
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc1stIndex;
}
else
{
if (lastOffset < size)
{
++unusedRangeCount;
}
lastOffset = freeSpace1stTo2ndEnd;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
while (lastOffset < size)
{
while (nextAlloc2ndIndex != SIZE_MAX &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
--nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex != SIZE_MAX)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
++unusedRangeCount;
}
++alloc2ndCount;
usedBytes += suballoc.size;
lastOffset = suballoc.offset + suballoc.size;
--nextAlloc2ndIndex;
}
else
{
if (lastOffset < size)
{
++unusedRangeCount;
}
lastOffset = size;
}
}
}
const VkDeviceSize unusedBytes = size - usedBytes;
PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
lastOffset = 0;
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
size_t nextAlloc2ndIndex = 0;
while (lastOffset < freeSpace2ndTo1stEnd)
{
while (nextAlloc2ndIndex < suballoc2ndCount &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
++nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex < suballoc2ndCount)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc2ndIndex;
}
else
{
if (lastOffset < freeSpace2ndTo1stEnd)
{
const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
lastOffset = freeSpace2ndTo1stEnd;
}
}
}
nextAlloc1stIndex = m_1stNullItemsBeginCount;
while (lastOffset < freeSpace1stTo2ndEnd)
{
while (nextAlloc1stIndex < suballoc1stCount &&
suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
{
++nextAlloc1stIndex;
}
if (nextAlloc1stIndex < suballoc1stCount)
{
const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
lastOffset = suballoc.offset + suballoc.size;
++nextAlloc1stIndex;
}
else
{
if (lastOffset < freeSpace1stTo2ndEnd)
{
const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
lastOffset = freeSpace1stTo2ndEnd;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
while (lastOffset < size)
{
while (nextAlloc2ndIndex != SIZE_MAX &&
suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
{
--nextAlloc2ndIndex;
}
if (nextAlloc2ndIndex != SIZE_MAX)
{
const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
if (lastOffset < suballoc.offset)
{
const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
lastOffset = suballoc.offset + suballoc.size;
--nextAlloc2ndIndex;
}
else
{
if (lastOffset < size)
{
const VkDeviceSize unusedRangeSize = size - lastOffset;
PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
}
lastOffset = size;
}
}
}
PrintDetailedMap_End(json);
}
#endif
bool VmaBlockMetadata_Linear::CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
VMA_ASSERT(allocSize > 0);
VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
VMA_ASSERT(pAllocationRequest != VMA_NULL);
VMA_HEAVY_ASSERT(Validate());
pAllocationRequest->size = allocSize;
return upperAddress ?
CreateAllocationRequest_UpperAddress(
allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :
CreateAllocationRequest_LowerAddress(
allocSize, allocAlignment, allocType, strategy, pAllocationRequest);
}
VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
{
VMA_ASSERT(!IsVirtual());
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
{
const VmaSuballocation& suballoc = suballocations1st[i];
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
{
if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
{
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
return VK_ERROR_UNKNOWN_COPY;
}
}
}
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
{
const VmaSuballocation& suballoc = suballocations2nd[i];
if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
{
if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
{
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
return VK_ERROR_UNKNOWN_COPY;
}
}
}
return VK_SUCCESS;
}
void VmaBlockMetadata_Linear::Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData)
{
const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
const VmaSuballocation newSuballoc = { offset, request.size, userData, type };
switch (request.type)
{
case VmaAllocationRequestType::UpperAddress:
{
VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
"CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
suballocations2nd.push_back(newSuballoc);
m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
}
break;
case VmaAllocationRequestType::EndOf1st:
{
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
VMA_ASSERT(suballocations1st.empty() ||
offset >= suballocations1st.back().offset + suballocations1st.back().size);
VMA_ASSERT(offset + request.size <= GetSize());
suballocations1st.push_back(newSuballoc);
}
break;
case VmaAllocationRequestType::EndOf2nd:
{
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
VMA_ASSERT(!suballocations1st.empty() &&
offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
switch (m_2ndVectorMode)
{
case SECOND_VECTOR_EMPTY:
VMA_ASSERT(suballocations2nd.empty());
m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
break;
case SECOND_VECTOR_RING_BUFFER:
VMA_ASSERT(!suballocations2nd.empty());
break;
case SECOND_VECTOR_DOUBLE_STACK:
VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
break;
default:
VMA_ASSERT(0);
}
suballocations2nd.push_back(newSuballoc);
}
break;
default:
VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
}
m_SumFreeSize -= newSuballoc.size;
}
void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)
{
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;
if (!suballocations1st.empty())
{
VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
if (firstSuballoc.offset == offset)
{
firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
firstSuballoc.userData = VMA_NULL;
m_SumFreeSize += firstSuballoc.size;
++m_1stNullItemsBeginCount;
CleanupAfterFree();
return;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
VmaSuballocation& lastSuballoc = suballocations2nd.back();
if (lastSuballoc.offset == offset)
{
m_SumFreeSize += lastSuballoc.size;
suballocations2nd.pop_back();
CleanupAfterFree();
return;
}
}
else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
{
VmaSuballocation& lastSuballoc = suballocations1st.back();
if (lastSuballoc.offset == offset)
{
m_SumFreeSize += lastSuballoc.size;
suballocations1st.pop_back();
CleanupAfterFree();
return;
}
}
VmaSuballocation refSuballoc;
refSuballoc.offset = offset;
{
const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
suballocations1st.begin() + m_1stNullItemsBeginCount,
suballocations1st.end(),
refSuballoc,
VmaSuballocationOffsetLess());
if (it != suballocations1st.end())
{
it->type = VMA_SUBALLOCATION_TYPE_FREE;
it->userData = VMA_NULL;
++m_1stNullItemsMiddleCount;
m_SumFreeSize += it->size;
CleanupAfterFree();
return;
}
}
if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
{
const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
if (it != suballocations2nd.end())
{
it->type = VMA_SUBALLOCATION_TYPE_FREE;
it->userData = VMA_NULL;
++m_2ndNullItemsCount;
m_SumFreeSize += it->size;
CleanupAfterFree();
return;
}
}
VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
}
void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
{
outInfo.offset = (VkDeviceSize)allocHandle - 1;
VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);
outInfo.size = suballoc.size;
outInfo.pUserData = suballoc.userData;
}
void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const
{
return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;
}
VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const
{
VMA_ASSERT(0);
return VK_NULL_HANDLE;
}
VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const
{
VMA_ASSERT(0);
return VK_NULL_HANDLE;
}
VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const
{
VMA_ASSERT(0);
return 0;
}
void VmaBlockMetadata_Linear::Clear()
{
m_SumFreeSize = GetSize();
m_Suballocations0.clear();
m_Suballocations1.clear();
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
m_1stNullItemsBeginCount = 0;
m_1stNullItemsMiddleCount = 0;
m_2ndNullItemsCount = 0;
}
void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
{
VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);
suballoc.userData = userData;
}
void VmaBlockMetadata_Linear::DebugLogAllAllocations() const
{
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)
if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
DebugLogAllocation(it->offset, it->size, it->userData);
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)
if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
DebugLogAllocation(it->offset, it->size, it->userData);
}
VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const
{
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
VmaSuballocation refSuballoc;
refSuballoc.offset = offset;
{
SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(
suballocations1st.begin() + m_1stNullItemsBeginCount,
suballocations1st.end(),
refSuballoc,
VmaSuballocationOffsetLess());
if (it != suballocations1st.end())
{
return const_cast<VmaSuballocation&>(*it);
}
}
if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
{
SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
if (it != suballocations2nd.end())
{
return const_cast<VmaSuballocation&>(*it);
}
}
VMA_ASSERT(0 && "Allocation not found in linear allocator!");
return const_cast<VmaSuballocation&>(suballocations1st.back());
}
bool VmaBlockMetadata_Linear::ShouldCompact1st() const
{
const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
const size_t suballocCount = AccessSuballocations1st().size();
return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
}
void VmaBlockMetadata_Linear::CleanupAfterFree()
{
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
if (IsEmpty())
{
suballocations1st.clear();
suballocations2nd.clear();
m_1stNullItemsBeginCount = 0;
m_1stNullItemsMiddleCount = 0;
m_2ndNullItemsCount = 0;
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
}
else
{
const size_t suballoc1stCount = suballocations1st.size();
const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
while (m_1stNullItemsBeginCount < suballoc1stCount &&
suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
{
++m_1stNullItemsBeginCount;
--m_1stNullItemsMiddleCount;
}
while (m_1stNullItemsMiddleCount > 0 &&
suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
{
--m_1stNullItemsMiddleCount;
suballocations1st.pop_back();
}
while (m_2ndNullItemsCount > 0 &&
suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
{
--m_2ndNullItemsCount;
suballocations2nd.pop_back();
}
while (m_2ndNullItemsCount > 0 &&
suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
{
--m_2ndNullItemsCount;
VmaVectorRemove(suballocations2nd, 0);
}
if (ShouldCompact1st())
{
const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
size_t srcIndex = m_1stNullItemsBeginCount;
for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
{
while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
{
++srcIndex;
}
if (dstIndex != srcIndex)
{
suballocations1st[dstIndex] = suballocations1st[srcIndex];
}
++srcIndex;
}
suballocations1st.resize(nonNullItemCount);
m_1stNullItemsBeginCount = 0;
m_1stNullItemsMiddleCount = 0;
}
if (suballocations2nd.empty())
{
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
}
if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
{
suballocations1st.clear();
m_1stNullItemsBeginCount = 0;
if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
m_2ndVectorMode = SECOND_VECTOR_EMPTY;
m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
{
++m_1stNullItemsBeginCount;
--m_1stNullItemsMiddleCount;
}
m_2ndNullItemsCount = 0;
m_1stVectorIndex ^= 1;
}
}
}
VMA_HEAVY_ASSERT(Validate());
}
bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
const VkDeviceSize blockSize = GetSize();
const VkDeviceSize debugMargin = GetDebugMargin();
const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
VkDeviceSize resultBaseOffset = 0;
if (!suballocations1st.empty())
{
const VmaSuballocation& lastSuballoc = suballocations1st.back();
resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
}
VkDeviceSize resultOffset = resultBaseOffset;
resultOffset = VmaAlignUp(resultOffset, allocAlignment);
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
{
bool bufferImageGranularityConflict = false;
for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
{
const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
{
bufferImageGranularityConflict = true;
break;
}
}
else
break;
}
if (bufferImageGranularityConflict)
{
resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
}
}
const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
suballocations2nd.back().offset : blockSize;
if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)
{
if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
{
for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
{
const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
{
return false;
}
}
else
{
break;
}
}
}
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
return true;
}
}
if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
VMA_ASSERT(!suballocations1st.empty());
VkDeviceSize resultBaseOffset = 0;
if (!suballocations2nd.empty())
{
const VmaSuballocation& lastSuballoc = suballocations2nd.back();
resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
}
VkDeviceSize resultOffset = resultBaseOffset;
resultOffset = VmaAlignUp(resultOffset, allocAlignment);
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
{
bool bufferImageGranularityConflict = false;
for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
{
const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
{
bufferImageGranularityConflict = true;
break;
}
}
else
break;
}
if (bufferImageGranularityConflict)
{
resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
}
}
size_t index1st = m_1stNullItemsBeginCount;
if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
(index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
{
if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
{
for (size_t nextSuballocIndex = index1st;
nextSuballocIndex < suballocations1st.size();
nextSuballocIndex++)
{
const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
{
return false;
}
}
else
{
break;
}
}
}
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
return true;
}
}
return false;
}
bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
const VkDeviceSize blockSize = GetSize();
const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
SuballocationVectorType& suballocations1st = AccessSuballocations1st();
SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
{
VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
return false;
}
if (allocSize > blockSize)
{
return false;
}
VkDeviceSize resultBaseOffset = blockSize - allocSize;
if (!suballocations2nd.empty())
{
const VmaSuballocation& lastSuballoc = suballocations2nd.back();
resultBaseOffset = lastSuballoc.offset - allocSize;
if (allocSize > lastSuballoc.offset)
{
return false;
}
}
VkDeviceSize resultOffset = resultBaseOffset;
const VkDeviceSize debugMargin = GetDebugMargin();
if (debugMargin > 0)
{
if (resultOffset < debugMargin)
{
return false;
}
resultOffset -= debugMargin;
}
resultOffset = VmaAlignDown(resultOffset, allocAlignment);
if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
{
bool bufferImageGranularityConflict = false;
for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
{
const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
{
bufferImageGranularityConflict = true;
break;
}
}
else
break;
}
if (bufferImageGranularityConflict)
{
resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
}
}
const VkDeviceSize endOf1st = !suballocations1st.empty() ?
suballocations1st.back().offset + suballocations1st.back().size :
0;
if (endOf1st + debugMargin <= resultOffset)
{
if (bufferImageGranularity > 1)
{
for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
{
const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
{
if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
{
return false;
}
}
else
{
break;
}
}
}
pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
return true;
}
return false;
}
#endif
#endif
#if 0
#ifndef _VMA_BLOCK_METADATA_BUDDY
class VmaBlockMetadata_Buddy : public VmaBlockMetadata
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Buddy)
public:
VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual);
virtual ~VmaBlockMetadata_Buddy();
size_t GetAllocationCount() const override { return m_AllocationCount; }
VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); }
bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; }
VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; }
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }
void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); }
void Init(VkDeviceSize size) override;
bool Validate() const override;
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
#endif
bool CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) override;
void Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData) override;
void Free(VmaAllocHandle allocHandle) override;
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
VmaAllocHandle GetAllocationListBegin() const override;
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
void Clear() override;
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
private:
static const size_t MAX_LEVELS = 48;
struct ValidationContext
{
size_t calculatedAllocationCount = 0;
size_t calculatedFreeCount = 0;
VkDeviceSize calculatedSumFreeSize = 0;
};
struct Node
{
VkDeviceSize offset;
enum TYPE
{
TYPE_FREE,
TYPE_ALLOCATION,
TYPE_SPLIT,
TYPE_COUNT
} type;
Node* parent;
Node* buddy;
union
{
struct
{
Node* prev;
Node* next;
} free;
struct
{
void* userData;
} allocation;
struct
{
Node* leftChild;
} split;
};
};
VkDeviceSize m_UsableSize;
uint32_t m_LevelCount;
VmaPoolAllocator<Node> m_NodeAllocator;
Node* m_Root;
struct
{
Node* front;
Node* back;
} m_FreeList[MAX_LEVELS];
size_t m_AllocationCount;
size_t m_FreeCount;
VkDeviceSize m_SumFreeSize;
VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
{
if (!IsVirtual())
{
size = VmaAlignUp(size, (VkDeviceSize)16);
}
return VmaNextPow2(size);
}
Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const;
void DeleteNodeChildren(Node* node);
bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const;
void AddToFreeListFront(uint32_t level, Node* node);
void RemoveFromFreeList(uint32_t level, Node* node);
void DebugLogAllAllocationNode(Node* node, uint32_t level) const;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
#endif
};
#ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS
VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual)
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
m_NodeAllocator(pAllocationCallbacks, 32),
m_Root(VMA_NULL),
m_AllocationCount(0),
m_FreeCount(1),
m_SumFreeSize(0)
{
memset(m_FreeList, 0, sizeof(m_FreeList));
}
VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
{
DeleteNodeChildren(m_Root);
m_NodeAllocator.Free(m_Root);
}
void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
{
VmaBlockMetadata::Init(size);
m_UsableSize = VmaPrevPow2(size);
m_SumFreeSize = m_UsableSize;
const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;
m_LevelCount = 1;
while (m_LevelCount < MAX_LEVELS &&
LevelToNodeSize(m_LevelCount) >= minNodeSize)
{
++m_LevelCount;
}
Node* rootNode = m_NodeAllocator.Alloc();
rootNode->offset = 0;
rootNode->type = Node::TYPE_FREE;
rootNode->parent = VMA_NULL;
rootNode->buddy = VMA_NULL;
m_Root = rootNode;
AddToFreeListFront(0, rootNode);
}
bool VmaBlockMetadata_Buddy::Validate() const
{
ValidationContext ctx;
if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
{
VMA_VALIDATE(false && "ValidateNode failed.");
}
VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
for (uint32_t level = 0; level < m_LevelCount; ++level)
{
VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
m_FreeList[level].front->free.prev == VMA_NULL);
for (Node* node = m_FreeList[level].front;
node != VMA_NULL;
node = node->free.next)
{
VMA_VALIDATE(node->type == Node::TYPE_FREE);
if (node->free.next == VMA_NULL)
{
VMA_VALIDATE(m_FreeList[level].back == node);
}
else
{
VMA_VALIDATE(node->free.next->free.prev == node);
}
}
}
for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
{
VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
}
return true;
}
void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
{
inoutStats.statistics.blockCount++;
inoutStats.statistics.blockBytes += GetSize();
AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0));
const VkDeviceSize unusableSize = GetUnusableSize();
if (unusableSize > 0)
VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize);
}
void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const
{
inoutStats.blockCount++;
inoutStats.allocationCount += (uint32_t)m_AllocationCount;
inoutStats.blockBytes += GetSize();
inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
{
VmaDetailedStatistics stats;
VmaClearDetailedStatistics(stats);
AddDetailedStatistics(stats);
PrintDetailedMap_Begin(
json,
stats.statistics.blockBytes - stats.statistics.allocationBytes,
stats.statistics.allocationCount,
stats.unusedRangeCount,
mapRefCount);
PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
const VkDeviceSize unusableSize = GetUnusableSize();
if (unusableSize > 0)
{
PrintDetailedMap_UnusedRange(json,
m_UsableSize,
unusableSize);
}
PrintDetailedMap_End(json);
}
#endif
bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
allocSize = AlignAllocationSize(allocSize);
if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
{
allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity());
allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity());
}
if (allocSize > m_UsableSize)
{
return false;
}
const uint32_t targetLevel = AllocSizeToLevel(allocSize);
for (uint32_t level = targetLevel; level--; )
{
for (Node* freeNode = m_FreeList[level].front;
freeNode != VMA_NULL;
freeNode = freeNode->free.next)
{
if (freeNode->offset % allocAlignment == 0)
{
pAllocationRequest->type = VmaAllocationRequestType::Normal;
pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1);
pAllocationRequest->size = allocSize;
pAllocationRequest->customData = (void*)(uintptr_t)level;
return true;
}
}
}
return false;
}
void VmaBlockMetadata_Buddy::Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData)
{
VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
const uint32_t targetLevel = AllocSizeToLevel(request.size);
uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
Node* currNode = m_FreeList[currLevel].front;
VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
while (currNode->offset != offset)
{
currNode = currNode->free.next;
VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
}
while (currLevel < targetLevel)
{
RemoveFromFreeList(currLevel, currNode);
const uint32_t childrenLevel = currLevel + 1;
Node* leftChild = m_NodeAllocator.Alloc();
Node* rightChild = m_NodeAllocator.Alloc();
leftChild->offset = currNode->offset;
leftChild->type = Node::TYPE_FREE;
leftChild->parent = currNode;
leftChild->buddy = rightChild;
rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
rightChild->type = Node::TYPE_FREE;
rightChild->parent = currNode;
rightChild->buddy = leftChild;
currNode->type = Node::TYPE_SPLIT;
currNode->split.leftChild = leftChild;
AddToFreeListFront(childrenLevel, rightChild);
AddToFreeListFront(childrenLevel, leftChild);
++m_FreeCount;
++currLevel;
currNode = m_FreeList[currLevel].front;
}
VMA_ASSERT(currLevel == targetLevel &&
currNode != VMA_NULL &&
currNode->type == Node::TYPE_FREE);
RemoveFromFreeList(currLevel, currNode);
currNode->type = Node::TYPE_ALLOCATION;
currNode->allocation.userData = userData;
++m_AllocationCount;
--m_FreeCount;
m_SumFreeSize -= request.size;
}
void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
{
uint32_t level = 0;
outInfo.offset = (VkDeviceSize)allocHandle - 1;
const Node* const node = FindAllocationNode(outInfo.offset, level);
outInfo.size = LevelToNodeSize(level);
outInfo.pUserData = node->allocation.userData;
}
void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const
{
uint32_t level = 0;
const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
return node->allocation.userData;
}
VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const
{
return VK_NULL_HANDLE;
}
VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const
{
return VK_NULL_HANDLE;
}
void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)
{
if (node->type == Node::TYPE_SPLIT)
{
DeleteNodeChildren(node->split.leftChild->buddy);
DeleteNodeChildren(node->split.leftChild);
const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();
m_NodeAllocator.Free(node->split.leftChild->buddy);
m_NodeAllocator.Free(node->split.leftChild);
}
}
void VmaBlockMetadata_Buddy::Clear()
{
DeleteNodeChildren(m_Root);
m_Root->type = Node::TYPE_FREE;
m_AllocationCount = 0;
m_FreeCount = 1;
m_SumFreeSize = m_UsableSize;
}
void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
{
uint32_t level = 0;
Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
node->allocation.userData = userData;
}
VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const
{
Node* node = m_Root;
VkDeviceSize nodeOffset = 0;
outLevel = 0;
VkDeviceSize levelNodeSize = LevelToNodeSize(0);
while (node->type == Node::TYPE_SPLIT)
{
const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;
if (offset < nodeOffset + nextLevelNodeSize)
{
node = node->split.leftChild;
}
else
{
node = node->split.leftChild->buddy;
nodeOffset += nextLevelNodeSize;
}
++outLevel;
levelNodeSize = nextLevelNodeSize;
}
VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
return node;
}
bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
{
VMA_VALIDATE(level < m_LevelCount);
VMA_VALIDATE(curr->parent == parent);
VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
switch (curr->type)
{
case Node::TYPE_FREE:
ctx.calculatedSumFreeSize += levelNodeSize;
++ctx.calculatedFreeCount;
break;
case Node::TYPE_ALLOCATION:
++ctx.calculatedAllocationCount;
if (!IsVirtual())
{
VMA_VALIDATE(curr->allocation.userData != VMA_NULL);
}
break;
case Node::TYPE_SPLIT:
{
const uint32_t childrenLevel = level + 1;
const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;
const Node* const leftChild = curr->split.leftChild;
VMA_VALIDATE(leftChild != VMA_NULL);
VMA_VALIDATE(leftChild->offset == curr->offset);
if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
{
VMA_VALIDATE(false && "ValidateNode for left child failed.");
}
const Node* const rightChild = leftChild->buddy;
VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
{
VMA_VALIDATE(false && "ValidateNode for right child failed.");
}
}
break;
default:
return false;
}
return true;
}
uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
{
uint32_t level = 0;
VkDeviceSize currLevelNodeSize = m_UsableSize;
VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
{
++level;
currLevelNodeSize >>= 1;
nextLevelNodeSize >>= 1;
}
return level;
}
void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle)
{
uint32_t level = 0;
Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
++m_FreeCount;
--m_AllocationCount;
m_SumFreeSize += LevelToNodeSize(level);
node->type = Node::TYPE_FREE;
while (level > 0 && node->buddy->type == Node::TYPE_FREE)
{
RemoveFromFreeList(level, node->buddy);
Node* const parent = node->parent;
m_NodeAllocator.Free(node->buddy);
m_NodeAllocator.Free(node);
parent->type = Node::TYPE_FREE;
node = parent;
--level;
--m_FreeCount;
}
AddToFreeListFront(level, node);
}
void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const
{
switch (node->type)
{
case Node::TYPE_FREE:
VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize);
break;
case Node::TYPE_ALLOCATION:
VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize);
break;
case Node::TYPE_SPLIT:
{
const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
const Node* const leftChild = node->split.leftChild;
AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize);
const Node* const rightChild = leftChild->buddy;
AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize);
}
break;
default:
VMA_ASSERT(0);
}
}
void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
{
VMA_ASSERT(node->type == Node::TYPE_FREE);
Node* const frontNode = m_FreeList[level].front;
if (frontNode == VMA_NULL)
{
VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
node->free.prev = node->free.next = VMA_NULL;
m_FreeList[level].front = m_FreeList[level].back = node;
}
else
{
VMA_ASSERT(frontNode->free.prev == VMA_NULL);
node->free.prev = VMA_NULL;
node->free.next = frontNode;
frontNode->free.prev = node;
m_FreeList[level].front = node;
}
}
void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
{
VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
if (node->free.prev == VMA_NULL)
{
VMA_ASSERT(m_FreeList[level].front == node);
m_FreeList[level].front = node->free.next;
}
else
{
Node* const prevFreeNode = node->free.prev;
VMA_ASSERT(prevFreeNode->free.next == node);
prevFreeNode->free.next = node->free.next;
}
if (node->free.next == VMA_NULL)
{
VMA_ASSERT(m_FreeList[level].back == node);
m_FreeList[level].back = node->free.prev;
}
else
{
Node* const nextFreeNode = node->free.next;
VMA_ASSERT(nextFreeNode->free.prev == node);
nextFreeNode->free.prev = node->free.prev;
}
}
void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const
{
switch (node->type)
{
case Node::TYPE_FREE:
break;
case Node::TYPE_ALLOCATION:
DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData);
break;
case Node::TYPE_SPLIT:
{
++level;
DebugLogAllAllocationNode(node->split.leftChild, level);
DebugLogAllAllocationNode(node->split.leftChild->buddy, level);
}
break;
default:
VMA_ASSERT(0);
}
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
{
switch (node->type)
{
case Node::TYPE_FREE:
PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
break;
case Node::TYPE_ALLOCATION:
PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);
break;
case Node::TYPE_SPLIT:
{
const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
const Node* const leftChild = node->split.leftChild;
PrintDetailedMapNode(json, leftChild, childrenNodeSize);
const Node* const rightChild = leftChild->buddy;
PrintDetailedMapNode(json, rightChild, childrenNodeSize);
}
break;
default:
VMA_ASSERT(0);
}
}
#endif
#endif
#endif
#endif
#ifndef _VMA_BLOCK_METADATA_TLSF
class VmaBlockMetadata_TLSF : public VmaBlockMetadata
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_TLSF)
public:
VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual);
virtual ~VmaBlockMetadata_TLSF();
size_t GetAllocationCount() const override { return m_AllocCount; }
size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }
VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }
bool IsEmpty() const override { return m_NullBlock->offset == 0; }
VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }
void Init(VkDeviceSize size) override;
bool Validate() const override;
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json) const override;
#endif
bool CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) override;
VkResult CheckCorruption(const void* pBlockData) override;
void Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData) override;
void Free(VmaAllocHandle allocHandle) override;
void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
VmaAllocHandle GetAllocationListBegin() const override;
VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
void Clear() override;
void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
void DebugLogAllAllocations() const override;
private:
static const uint8_t SECOND_LEVEL_INDEX = 5;
static const uint16_t SMALL_BUFFER_SIZE = 256;
static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;
static const uint8_t MEMORY_CLASS_SHIFT = 7;
static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;
class Block
{
public:
VkDeviceSize offset;
VkDeviceSize size;
Block* prevPhysical;
Block* nextPhysical;
void MarkFree() { prevFree = VMA_NULL; }
void MarkTaken() { prevFree = this; }
bool IsFree() const { return prevFree != this; }
void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }
Block*& PrevFree() { return prevFree; }
Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }
private:
Block* prevFree;
union
{
Block* nextFree;
void* userData;
};
};
size_t m_AllocCount;
size_t m_BlocksFreeCount;
VkDeviceSize m_BlocksFreeSize;
uint32_t m_IsFreeBitmap;
uint8_t m_MemoryClasses;
uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];
uint32_t m_ListsCount;
Block** m_FreeList;
VmaPoolAllocator<Block> m_BlockAllocator;
Block* m_NullBlock;
VmaBlockBufferImageGranularity m_GranularityHandler;
uint8_t SizeToMemoryClass(VkDeviceSize size) const;
uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;
uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;
uint32_t GetListIndex(VkDeviceSize size) const;
void RemoveFreeBlock(Block* block);
void InsertFreeBlock(Block* block);
void MergeBlock(Block* block, Block* prev);
Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;
bool CheckBlock(
Block& block,
uint32_t listIndex,
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
VmaAllocationRequest* pAllocationRequest);
};
#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
VkDeviceSize bufferImageGranularity, bool isVirtual)
: VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
m_AllocCount(0),
m_BlocksFreeCount(0),
m_BlocksFreeSize(0),
m_IsFreeBitmap(0),
m_MemoryClasses(0),
m_ListsCount(0),
m_FreeList(VMA_NULL),
m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),
m_NullBlock(VMA_NULL),
m_GranularityHandler(bufferImageGranularity) {}
VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()
{
if (m_FreeList)
vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);
m_GranularityHandler.Destroy(GetAllocationCallbacks());
}
void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)
{
VmaBlockMetadata::Init(size);
if (!IsVirtual())
m_GranularityHandler.Init(GetAllocationCallbacks(), size);
m_NullBlock = m_BlockAllocator.Alloc();
m_NullBlock->size = size;
m_NullBlock->offset = 0;
m_NullBlock->prevPhysical = VMA_NULL;
m_NullBlock->nextPhysical = VMA_NULL;
m_NullBlock->MarkFree();
m_NullBlock->NextFree() = VMA_NULL;
m_NullBlock->PrevFree() = VMA_NULL;
uint8_t memoryClass = SizeToMemoryClass(size);
uint16_t sli = SizeToSecondIndex(size, memoryClass);
m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;
if (IsVirtual())
m_ListsCount += 1UL << SECOND_LEVEL_INDEX;
else
m_ListsCount += 4;
m_MemoryClasses = memoryClass + uint8_t(2);
memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));
m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);
memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
}
bool VmaBlockMetadata_TLSF::Validate() const
{
VMA_VALIDATE(GetSumFreeSize() <= GetSize());
VkDeviceSize calculatedSize = m_NullBlock->size;
VkDeviceSize calculatedFreeSize = m_NullBlock->size;
size_t allocCount = 0;
size_t freeCount = 0;
for (uint32_t list = 0; list < m_ListsCount; ++list)
{
Block* block = m_FreeList[list];
if (block != VMA_NULL)
{
VMA_VALIDATE(block->IsFree());
VMA_VALIDATE(block->PrevFree() == VMA_NULL);
while (block->NextFree())
{
VMA_VALIDATE(block->NextFree()->IsFree());
VMA_VALIDATE(block->NextFree()->PrevFree() == block);
block = block->NextFree();
}
}
}
VkDeviceSize nextOffset = m_NullBlock->offset;
auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());
VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);
if (m_NullBlock->prevPhysical)
{
VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);
}
for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)
{
VMA_VALIDATE(prev->offset + prev->size == nextOffset);
nextOffset = prev->offset;
calculatedSize += prev->size;
uint32_t listIndex = GetListIndex(prev->size);
if (prev->IsFree())
{
++freeCount;
Block* freeBlock = m_FreeList[listIndex];
VMA_VALIDATE(freeBlock != VMA_NULL);
bool found = false;
do
{
if (freeBlock == prev)
found = true;
freeBlock = freeBlock->NextFree();
} while (!found && freeBlock != VMA_NULL);
VMA_VALIDATE(found);
calculatedFreeSize += prev->size;
}
else
{
++allocCount;
Block* freeBlock = m_FreeList[listIndex];
while (freeBlock)
{
VMA_VALIDATE(freeBlock != prev);
freeBlock = freeBlock->NextFree();
}
if (!IsVirtual())
{
VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));
}
}
if (prev->prevPhysical)
{
VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);
}
}
if (!IsVirtual())
{
VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));
}
VMA_VALIDATE(nextOffset == 0);
VMA_VALIDATE(calculatedSize == GetSize());
VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());
VMA_VALIDATE(allocCount == m_AllocCount);
VMA_VALIDATE(freeCount == m_BlocksFreeCount);
return true;
}
void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
{
inoutStats.statistics.blockCount++;
inoutStats.statistics.blockBytes += GetSize();
if (m_NullBlock->size > 0)
VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
{
if (block->IsFree())
VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);
else
VmaAddDetailedStatisticsAllocation(inoutStats, block->size);
}
}
void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const
{
inoutStats.blockCount++;
inoutStats.allocationCount += (uint32_t)m_AllocCount;
inoutStats.blockBytes += GetSize();
inoutStats.allocationBytes += GetSize() - GetSumFreeSize();
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const
{
size_t blockCount = m_AllocCount + m_BlocksFreeCount;
VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);
size_t i = blockCount;
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
{
blockList[--i] = block;
}
VMA_ASSERT(i == 0);
VmaDetailedStatistics stats;
VmaClearDetailedStatistics(stats);
AddDetailedStatistics(stats);
PrintDetailedMap_Begin(json,
stats.statistics.blockBytes - stats.statistics.allocationBytes,
stats.statistics.allocationCount,
stats.unusedRangeCount);
for (; i < blockCount; ++i)
{
Block* block = blockList[i];
if (block->IsFree())
PrintDetailedMap_UnusedRange(json, block->offset, block->size);
else
PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());
}
if (m_NullBlock->size > 0)
PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);
PrintDetailedMap_End(json);
}
#endif
bool VmaBlockMetadata_TLSF::CreateAllocationRequest(
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
bool upperAddress,
VmaSuballocationType allocType,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest)
{
VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
if (!IsVirtual())
m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);
allocSize += GetDebugMargin();
if (allocSize > GetSumFreeSize())
return false;
if (m_BlocksFreeCount == 0)
return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);
VkDeviceSize sizeForNextList = allocSize;
VkDeviceSize smallSizeStep = VkDeviceSize(SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4));
if (allocSize > SMALL_BUFFER_SIZE)
{
sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));
}
else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)
sizeForNextList = SMALL_BUFFER_SIZE + 1;
else
sizeForNextList += smallSizeStep;
uint32_t nextListIndex = m_ListsCount;
uint32_t prevListIndex = m_ListsCount;
Block* nextListBlock = VMA_NULL;
Block* prevListBlock = VMA_NULL;
if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)
{
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
while (nextListBlock)
{
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
nextListBlock = nextListBlock->NextFree();
}
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
while (prevListBlock)
{
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
prevListBlock = prevListBlock->NextFree();
}
}
else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
{
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
while (prevListBlock)
{
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
prevListBlock = prevListBlock->NextFree();
}
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
while (nextListBlock)
{
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
nextListBlock = nextListBlock->NextFree();
}
}
else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )
{
VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);
size_t i = m_BlocksFreeCount;
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
{
if (block->IsFree() && block->size >= allocSize)
blockList[--i] = block;
}
for (; i < m_BlocksFreeCount; ++i)
{
Block& block = *blockList[i];
if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
}
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
return false;
}
else
{
nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
while (nextListBlock)
{
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
nextListBlock = nextListBlock->NextFree();
}
if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
prevListBlock = FindFreeBlock(allocSize, prevListIndex);
while (prevListBlock)
{
if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
prevListBlock = prevListBlock->NextFree();
}
}
while (++nextListIndex < m_ListsCount)
{
nextListBlock = m_FreeList[nextListIndex];
while (nextListBlock)
{
if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
return true;
nextListBlock = nextListBlock->NextFree();
}
}
return false;
}
VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)
{
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
{
if (!block->IsFree())
{
if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))
{
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
return VK_ERROR_UNKNOWN_COPY;
}
}
}
return VK_SUCCESS;
}
void VmaBlockMetadata_TLSF::Alloc(
const VmaAllocationRequest& request,
VmaSuballocationType type,
void* userData)
{
VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);
Block* currentBlock = (Block*)request.allocHandle;
VkDeviceSize offset = request.algorithmData;
VMA_ASSERT(currentBlock != VMA_NULL);
VMA_ASSERT(currentBlock->offset <= offset);
if (currentBlock != m_NullBlock)
RemoveFreeBlock(currentBlock);
VkDeviceSize debugMargin = GetDebugMargin();
VkDeviceSize misssingAlignment = offset - currentBlock->offset;
if (misssingAlignment)
{
Block* prevBlock = currentBlock->prevPhysical;
VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");
if (prevBlock->IsFree() && prevBlock->size != debugMargin)
{
uint32_t oldList = GetListIndex(prevBlock->size);
prevBlock->size += misssingAlignment;
if (oldList != GetListIndex(prevBlock->size))
{
prevBlock->size -= misssingAlignment;
RemoveFreeBlock(prevBlock);
prevBlock->size += misssingAlignment;
InsertFreeBlock(prevBlock);
}
else
m_BlocksFreeSize += misssingAlignment;
}
else
{
Block* newBlock = m_BlockAllocator.Alloc();
currentBlock->prevPhysical = newBlock;
prevBlock->nextPhysical = newBlock;
newBlock->prevPhysical = prevBlock;
newBlock->nextPhysical = currentBlock;
newBlock->size = misssingAlignment;
newBlock->offset = currentBlock->offset;
newBlock->MarkTaken();
InsertFreeBlock(newBlock);
}
currentBlock->size -= misssingAlignment;
currentBlock->offset += misssingAlignment;
}
VkDeviceSize size = request.size + debugMargin;
if (currentBlock->size == size)
{
if (currentBlock == m_NullBlock)
{
m_NullBlock = m_BlockAllocator.Alloc();
m_NullBlock->size = 0;
m_NullBlock->offset = currentBlock->offset + size;
m_NullBlock->prevPhysical = currentBlock;
m_NullBlock->nextPhysical = VMA_NULL;
m_NullBlock->MarkFree();
m_NullBlock->PrevFree() = VMA_NULL;
m_NullBlock->NextFree() = VMA_NULL;
currentBlock->nextPhysical = m_NullBlock;
currentBlock->MarkTaken();
}
}
else
{
VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");
Block* newBlock = m_BlockAllocator.Alloc();
newBlock->size = currentBlock->size - size;
newBlock->offset = currentBlock->offset + size;
newBlock->prevPhysical = currentBlock;
newBlock->nextPhysical = currentBlock->nextPhysical;
currentBlock->nextPhysical = newBlock;
currentBlock->size = size;
if (currentBlock == m_NullBlock)
{
m_NullBlock = newBlock;
m_NullBlock->MarkFree();
m_NullBlock->NextFree() = VMA_NULL;
m_NullBlock->PrevFree() = VMA_NULL;
currentBlock->MarkTaken();
}
else
{
newBlock->nextPhysical->prevPhysical = newBlock;
newBlock->MarkTaken();
InsertFreeBlock(newBlock);
}
}
currentBlock->UserData() = userData;
if (debugMargin > 0)
{
currentBlock->size -= debugMargin;
Block* newBlock = m_BlockAllocator.Alloc();
newBlock->size = debugMargin;
newBlock->offset = currentBlock->offset + currentBlock->size;
newBlock->prevPhysical = currentBlock;
newBlock->nextPhysical = currentBlock->nextPhysical;
newBlock->MarkTaken();
currentBlock->nextPhysical->prevPhysical = newBlock;
currentBlock->nextPhysical = newBlock;
InsertFreeBlock(newBlock);
}
if (!IsVirtual())
m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,
currentBlock->offset, currentBlock->size);
++m_AllocCount;
}
void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)
{
Block* block = (Block*)allocHandle;
Block* next = block->nextPhysical;
VMA_ASSERT(!block->IsFree() && "Block is already free!");
if (!IsVirtual())
m_GranularityHandler.FreePages(block->offset, block->size);
--m_AllocCount;
VkDeviceSize debugMargin = GetDebugMargin();
if (debugMargin > 0)
{
RemoveFreeBlock(next);
MergeBlock(next, block);
block = next;
next = next->nextPhysical;
}
Block* prev = block->prevPhysical;
if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)
{
RemoveFreeBlock(prev);
MergeBlock(block, prev);
}
if (!next->IsFree())
InsertFreeBlock(block);
else if (next == m_NullBlock)
MergeBlock(m_NullBlock, block);
else
{
RemoveFreeBlock(next);
MergeBlock(next, block);
InsertFreeBlock(next);
}
}
void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
{
Block* block = (Block*)allocHandle;
VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");
outInfo.offset = block->offset;
outInfo.size = block->size;
outInfo.pUserData = block->UserData();
}
void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const
{
Block* block = (Block*)allocHandle;
VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");
return block->UserData();
}
VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const
{
if (m_AllocCount == 0)
return VK_NULL_HANDLE;
for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)
{
if (!block->IsFree())
return (VmaAllocHandle)block;
}
VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");
return VK_NULL_HANDLE;
}
VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const
{
Block* startBlock = (Block*)prevAlloc;
VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");
for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)
{
if (!block->IsFree())
return (VmaAllocHandle)block;
}
return VK_NULL_HANDLE;
}
VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const
{
Block* block = (Block*)alloc;
VMA_ASSERT(!block->IsFree() && "Incorrect block!");
if (block->prevPhysical)
return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;
return 0;
}
void VmaBlockMetadata_TLSF::Clear()
{
m_AllocCount = 0;
m_BlocksFreeCount = 0;
m_BlocksFreeSize = 0;
m_IsFreeBitmap = 0;
m_NullBlock->offset = 0;
m_NullBlock->size = GetSize();
Block* block = m_NullBlock->prevPhysical;
m_NullBlock->prevPhysical = VMA_NULL;
while (block)
{
Block* prev = block->prevPhysical;
m_BlockAllocator.Free(block);
block = prev;
}
memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));
m_GranularityHandler.Clear();
}
void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
{
Block* block = (Block*)allocHandle;
VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");
block->UserData() = userData;
}
void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const
{
for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
if (!block->IsFree())
DebugLogAllocation(block->offset, block->size, block->UserData());
}
uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const
{
if (size > SMALL_BUFFER_SIZE)
return uint8_t(VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT);
return 0;
}
uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const
{
if (memoryClass == 0)
{
if (IsVirtual())
return static_cast<uint16_t>((size - 1) / 8);
else
return static_cast<uint16_t>((size - 1) / 64);
}
return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));
}
uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const
{
if (memoryClass == 0)
return secondIndex;
const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;
if (IsVirtual())
return index + (1 << SECOND_LEVEL_INDEX);
else
return index + 4;
}
uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const
{
uint8_t memoryClass = SizeToMemoryClass(size);
return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));
}
void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)
{
VMA_ASSERT(block != m_NullBlock);
VMA_ASSERT(block->IsFree());
if (block->NextFree() != VMA_NULL)
block->NextFree()->PrevFree() = block->PrevFree();
if (block->PrevFree() != VMA_NULL)
block->PrevFree()->NextFree() = block->NextFree();
else
{
uint8_t memClass = SizeToMemoryClass(block->size);
uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
uint32_t index = GetListIndex(memClass, secondIndex);
VMA_ASSERT(m_FreeList[index] == block);
m_FreeList[index] = block->NextFree();
if (block->NextFree() == VMA_NULL)
{
m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);
if (m_InnerIsFreeBitmap[memClass] == 0)
m_IsFreeBitmap &= ~(1UL << memClass);
}
}
block->MarkTaken();
block->UserData() = VMA_NULL;
--m_BlocksFreeCount;
m_BlocksFreeSize -= block->size;
}
void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)
{
VMA_ASSERT(block != m_NullBlock);
VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");
uint8_t memClass = SizeToMemoryClass(block->size);
uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
uint32_t index = GetListIndex(memClass, secondIndex);
VMA_ASSERT(index < m_ListsCount);
block->PrevFree() = VMA_NULL;
block->NextFree() = m_FreeList[index];
m_FreeList[index] = block;
if (block->NextFree() != VMA_NULL)
block->NextFree()->PrevFree() = block;
else
{
m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;
m_IsFreeBitmap |= 1UL << memClass;
}
++m_BlocksFreeCount;
m_BlocksFreeSize += block->size;
}
void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)
{
VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!");
VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");
block->offset = prev->offset;
block->size += prev->size;
block->prevPhysical = prev->prevPhysical;
if (block->prevPhysical)
block->prevPhysical->nextPhysical = block;
m_BlockAllocator.Free(prev);
}
VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const
{
uint8_t memoryClass = SizeToMemoryClass(size);
uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));
if (!innerFreeMap)
{
uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));
if (!freeMap)
return VMA_NULL;
memoryClass = VMA_BITSCAN_LSB(freeMap);
innerFreeMap = m_InnerIsFreeBitmap[memoryClass];
VMA_ASSERT(innerFreeMap != 0);
}
listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));
VMA_ASSERT(m_FreeList[listIndex]);
return m_FreeList[listIndex];
}
bool VmaBlockMetadata_TLSF::CheckBlock(
Block& block,
uint32_t listIndex,
VkDeviceSize allocSize,
VkDeviceSize allocAlignment,
VmaSuballocationType allocType,
VmaAllocationRequest* pAllocationRequest)
{
VMA_ASSERT(block.IsFree() && "Block is already taken!");
VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);
if (block.size < allocSize + alignedOffset - block.offset)
return false;
if (!IsVirtual() &&
m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))
return false;
pAllocationRequest->type = VmaAllocationRequestType::TLSF;
pAllocationRequest->allocHandle = (VmaAllocHandle)█
pAllocationRequest->size = allocSize - GetDebugMargin();
pAllocationRequest->customData = (void*)allocType;
pAllocationRequest->algorithmData = alignedOffset;
if (listIndex != m_ListsCount && block.PrevFree())
{
block.PrevFree()->NextFree() = block.NextFree();
if (block.NextFree())
block.NextFree()->PrevFree() = block.PrevFree();
block.PrevFree() = VMA_NULL;
block.NextFree() = m_FreeList[listIndex];
m_FreeList[listIndex] = █
if (block.NextFree())
block.NextFree()->PrevFree() = █
}
return true;
}
#endif
#endif
#ifndef _VMA_BLOCK_VECTOR
class VmaBlockVector
{
friend struct VmaDefragmentationContext_T;
VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockVector)
public:
VmaBlockVector(
VmaAllocator hAllocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceSize preferredBlockSize,
size_t minBlockCount,
size_t maxBlockCount,
VkDeviceSize bufferImageGranularity,
bool explicitBlockSize,
uint32_t algorithm,
float priority,
VkDeviceSize minAllocationAlignment,
void* pMemoryAllocateNext);
~VmaBlockVector();
VmaAllocator GetAllocator() const { return m_hAllocator; }
VmaPool GetParentPool() const { return m_hParentPool; }
bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
uint32_t GetAlgorithm() const { return m_Algorithm; }
bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }
float GetPriority() const { return m_Priority; }
const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }
size_t GetBlockCount() const { return m_Blocks.size(); }
VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
VMA_RW_MUTEX &GetMutex() { return m_Mutex; }
VkResult CreateMinBlocks();
void AddStatistics(VmaStatistics& inoutStats);
void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
bool IsEmpty();
bool IsCorruptionDetectionEnabled() const;
VkResult Allocate(
VkDeviceSize size,
VkDeviceSize alignment,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
size_t allocationCount,
VmaAllocation* pAllocations);
void Free(const VmaAllocation hAllocation);
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json);
#endif
VkResult CheckCorruption();
private:
const VmaAllocator m_hAllocator;
const VmaPool m_hParentPool;
const uint32_t m_MemoryTypeIndex;
const VkDeviceSize m_PreferredBlockSize;
const size_t m_MinBlockCount;
const size_t m_MaxBlockCount;
const VkDeviceSize m_BufferImageGranularity;
const bool m_ExplicitBlockSize;
const uint32_t m_Algorithm;
const float m_Priority;
const VkDeviceSize m_MinAllocationAlignment;
void* const m_pMemoryAllocateNext;
VMA_RW_MUTEX m_Mutex;
VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;
uint32_t m_NextBlockId;
bool m_IncrementalSort = true;
void SetIncrementalSort(bool val) { m_IncrementalSort = val; }
VkDeviceSize CalcMaxBlockSize() const;
void Remove(VmaDeviceMemoryBlock* pBlock);
void IncrementallySortBlocks();
void SortByFreeSize();
VkResult AllocatePage(
VkDeviceSize size,
VkDeviceSize alignment,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation);
VkResult AllocateFromBlock(
VmaDeviceMemoryBlock* pBlock,
VkDeviceSize size,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
uint32_t strategy,
VmaAllocation* pAllocation);
VkResult CommitAllocationRequest(
VmaAllocationRequest& allocRequest,
VmaDeviceMemoryBlock* pBlock,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation);
VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
bool HasEmptyBlock();
};
#endif
#ifndef _VMA_DEFRAGMENTATION_CONTEXT
struct VmaDefragmentationContext_T
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaDefragmentationContext_T)
public:
VmaDefragmentationContext_T(
VmaAllocator hAllocator,
const VmaDefragmentationInfo& info);
~VmaDefragmentationContext_T();
void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }
VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);
VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);
private:
static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;
enum class CounterStatus { Pass, Ignore, End };
struct FragmentedBlock
{
uint32_t data;
VmaDeviceMemoryBlock* block;
};
struct StateBalanced
{
VkDeviceSize avgFreeSize = 0;
VkDeviceSize avgAllocSize = UINT64_MAX;
};
struct StateExtensive
{
enum class Operation : uint8_t
{
FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,
MoveBuffers, MoveTextures, MoveAll,
Cleanup, Done
};
Operation operation = Operation::FindFreeBlockTexture;
size_t firstFreeBlock = SIZE_MAX;
};
struct MoveAllocationData
{
VkDeviceSize size;
VkDeviceSize alignment;
VmaSuballocationType type;
VmaAllocationCreateFlags flags;
VmaDefragmentationMove move = {};
};
const VkDeviceSize m_MaxPassBytes;
const uint32_t m_MaxPassAllocations;
const PFN_vmaCheckDefragmentationBreakFunction m_BreakCallback;
void* m_BreakCallbackUserData;
VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;
VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;
uint8_t m_IgnoredAllocs = 0;
uint32_t m_Algorithm;
uint32_t m_BlockVectorCount;
VmaBlockVector* m_PoolBlockVector;
VmaBlockVector** m_pBlockVectors;
size_t m_ImmovableBlockCount = 0;
VmaDefragmentationStats m_GlobalStats = { 0 };
VmaDefragmentationStats m_PassStats = { 0 };
void* m_AlgorithmState = VMA_NULL;
static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);
CounterStatus CheckCounters(VkDeviceSize bytes);
bool IncrementCounters(VkDeviceSize bytes);
bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);
bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);
bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);
bool ComputeDefragmentation_Fast(VmaBlockVector& vector);
bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);
bool ComputeDefragmentation_Full(VmaBlockVector& vector);
bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);
void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);
bool MoveDataToFreeBlocks(VmaSuballocationType currentType,
VmaBlockVector& vector, size_t firstFreeBlock,
bool& texturePresent, bool& bufferPresent, bool& otherPresent);
};
#endif
#ifndef _VMA_POOL_T
struct VmaPool_T
{
friend struct VmaPoolListItemTraits;
VMA_CLASS_NO_COPY_NO_MOVE(VmaPool_T)
public:
VmaBlockVector m_BlockVector;
VmaDedicatedAllocationList m_DedicatedAllocations;
VmaPool_T(
VmaAllocator hAllocator,
const VmaPoolCreateInfo& createInfo,
VkDeviceSize preferredBlockSize);
~VmaPool_T();
uint32_t GetId() const { return m_Id; }
void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
const char* GetName() const { return m_Name; }
void SetName(const char* pName);
#if VMA_STATS_STRING_ENABLED
#endif
private:
uint32_t m_Id;
char* m_Name;
VmaPool_T* m_PrevPool = VMA_NULL;
VmaPool_T* m_NextPool = VMA_NULL;
};
struct VmaPoolListItemTraits
{
typedef VmaPool_T ItemType;
static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
};
#endif
#ifndef _VMA_CURRENT_BUDGET_DATA
struct VmaCurrentBudgetData
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaCurrentBudgetData)
public:
VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];
VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];
VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
#if VMA_MEMORY_BUDGET
VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
VMA_RW_MUTEX m_BudgetMutex;
uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
#endif
VmaCurrentBudgetData();
void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
};
#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
VmaCurrentBudgetData::VmaCurrentBudgetData()
{
for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
{
m_BlockCount[heapIndex] = 0;
m_AllocationCount[heapIndex] = 0;
m_BlockBytes[heapIndex] = 0;
m_AllocationBytes[heapIndex] = 0;
#if VMA_MEMORY_BUDGET
m_VulkanUsage[heapIndex] = 0;
m_VulkanBudget[heapIndex] = 0;
m_BlockBytesAtBudgetFetch[heapIndex] = 0;
#endif
}
#if VMA_MEMORY_BUDGET
m_OperationsSinceBudgetFetch = 0;
#endif
}
void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
{
m_AllocationBytes[heapIndex] += allocationSize;
++m_AllocationCount[heapIndex];
#if VMA_MEMORY_BUDGET
++m_OperationsSinceBudgetFetch;
#endif
}
void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
{
VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
m_AllocationBytes[heapIndex] -= allocationSize;
VMA_ASSERT(m_AllocationCount[heapIndex] > 0);
--m_AllocationCount[heapIndex];
#if VMA_MEMORY_BUDGET
++m_OperationsSinceBudgetFetch;
#endif
}
#endif
#endif
#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR
class VmaAllocationObjectAllocator
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocationObjectAllocator)
public:
VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)
: m_Allocator(pAllocationCallbacks, 1024) {}
template<typename... Types> VmaAllocation Allocate(Types&&... args);
void Free(VmaAllocation hAlloc);
private:
VMA_MUTEX m_Mutex;
VmaPoolAllocator<VmaAllocation_T> m_Allocator;
};
template<typename... Types>
VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
{
VmaMutexLock mutexLock(m_Mutex);
return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
}
void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
{
VmaMutexLock mutexLock(m_Mutex);
m_Allocator.Free(hAlloc);
}
#endif
#ifndef _VMA_VIRTUAL_BLOCK_T
struct VmaVirtualBlock_T
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaVirtualBlock_T)
public:
const bool m_AllocationCallbacksSpecified;
const VkAllocationCallbacks m_AllocationCallbacks;
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
~VmaVirtualBlock_T();
VkResult Init() { return VK_SUCCESS; }
bool IsEmpty() const { return m_Metadata->IsEmpty(); }
void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }
void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }
void Clear() { m_Metadata->Clear(); }
const VkAllocationCallbacks* GetAllocationCallbacks() const;
void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);
VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
VkDeviceSize* outOffset);
void GetStatistics(VmaStatistics& outStats) const;
void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;
#if VMA_STATS_STRING_ENABLED
void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
#endif
private:
VmaBlockMetadata* m_Metadata;
};
#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)
: m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
{
const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
switch (algorithm)
{
case 0:
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
break;
case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);
break;
default:
VMA_ASSERT(0);
m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
}
m_Metadata->Init(createInfo.size);
}
VmaVirtualBlock_T::~VmaVirtualBlock_T()
{
if (!m_Metadata->IsEmpty())
m_Metadata->DebugLogAllAllocations();
VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
vma_delete(GetAllocationCallbacks(), m_Metadata);
}
const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const
{
return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
}
void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)
{
m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);
}
VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
VkDeviceSize* outOffset)
{
VmaAllocationRequest request = {};
if (m_Metadata->CreateAllocationRequest(
createInfo.size,
VMA_MAX(createInfo.alignment, (VkDeviceSize)1),
(createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
VMA_SUBALLOCATION_TYPE_UNKNOWN,
createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK,
&request))
{
m_Metadata->Alloc(request,
VMA_SUBALLOCATION_TYPE_UNKNOWN,
createInfo.pUserData);
outAllocation = (VmaVirtualAllocation)request.allocHandle;
if(outOffset)
*outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);
return VK_SUCCESS;
}
outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;
if (outOffset)
*outOffset = UINT64_MAX;
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const
{
VmaClearStatistics(outStats);
m_Metadata->AddStatistics(outStats);
}
void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const
{
VmaClearDetailedStatistics(outStats);
m_Metadata->AddDetailedStatistics(outStats);
}
#if VMA_STATS_STRING_ENABLED
void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
{
VmaJsonWriter json(GetAllocationCallbacks(), sb);
json.BeginObject();
VmaDetailedStatistics stats;
CalculateDetailedStatistics(stats);
json.WriteString("Stats");
VmaPrintDetailedStatistics(json, stats);
if (detailedMap)
{
json.WriteString("Details");
json.BeginObject();
m_Metadata->PrintDetailedMap(json);
json.EndObject();
}
json.EndObject();
}
#endif
#endif
#endif
struct VmaAllocator_T
{
VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocator_T)
public:
bool m_UseMutex;
uint32_t m_VulkanApiVersion;
bool m_UseKhrDedicatedAllocation;
bool m_UseKhrBindMemory2;
bool m_UseExtMemoryBudget;
bool m_UseAmdDeviceCoherentMemory;
bool m_UseKhrBufferDeviceAddress;
bool m_UseExtMemoryPriority;
VkDevice m_hDevice;
VkInstance m_hInstance;
bool m_AllocationCallbacksSpecified;
VkAllocationCallbacks m_AllocationCallbacks;
VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
VmaAllocationObjectAllocator m_AllocationObjectAllocator;
uint32_t m_HeapSizeLimitMask;
VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
VkPhysicalDeviceMemoryProperties m_MemProps;
VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
VmaCurrentBudgetData m_Budget;
VMA_ATOMIC_UINT32 m_DeviceMemoryCount;
VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
~VmaAllocator_T();
const VkAllocationCallbacks* GetAllocationCallbacks() const
{
return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
}
const VmaVulkanFunctions& GetVulkanFunctions() const
{
return m_VulkanFunctions;
}
VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
VkDeviceSize GetBufferImageGranularity() const
{
return VMA_MAX(
static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
m_PhysicalDeviceProperties.limits.bufferImageGranularity);
}
uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
{
VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
}
bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
{
return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
}
VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
{
return IsMemoryTypeNonCoherent(memTypeIndex) ?
VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
(VkDeviceSize)VMA_MIN_ALIGNMENT;
}
bool IsIntegratedGpu() const
{
return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
}
uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
void GetBufferMemoryRequirements(
VkBuffer hBuffer,
VkMemoryRequirements& memReq,
bool& requiresDedicatedAllocation,
bool& prefersDedicatedAllocation) const;
void GetImageMemoryRequirements(
VkImage hImage,
VkMemoryRequirements& memReq,
bool& requiresDedicatedAllocation,
bool& prefersDedicatedAllocation) const;
VkResult FindMemoryTypeIndex(
uint32_t memoryTypeBits,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
VkFlags bufImgUsage,
uint32_t* pMemoryTypeIndex) const;
VkResult AllocateMemory(
const VkMemoryRequirements& vkMemReq,
bool requiresDedicatedAllocation,
bool prefersDedicatedAllocation,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
size_t allocationCount,
VmaAllocation* pAllocations);
void FreeMemory(
size_t allocationCount,
const VmaAllocation* pAllocations);
void CalculateStatistics(VmaTotalStatistics* pStats);
void GetHeapBudgets(
VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json);
#endif
void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
void DestroyPool(VmaPool pool);
void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);
void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);
void SetCurrentFrameIndex(uint32_t frameIndex);
uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
VkResult CheckPoolCorruption(VmaPool hPool);
VkResult CheckCorruption(uint32_t memoryTypeBits);
VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
VkResult BindVulkanBuffer(
VkDeviceMemory memory,
VkDeviceSize memoryOffset,
VkBuffer buffer,
const void* pNext);
VkResult BindVulkanImage(
VkDeviceMemory memory,
VkDeviceSize memoryOffset,
VkImage image,
const void* pNext);
VkResult Map(VmaAllocation hAllocation, void** ppData);
void Unmap(VmaAllocation hAllocation);
VkResult BindBufferMemory(
VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkBuffer hBuffer,
const void* pNext);
VkResult BindImageMemory(
VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext);
VkResult FlushOrInvalidateAllocation(
VmaAllocation hAllocation,
VkDeviceSize offset, VkDeviceSize size,
VMA_CACHE_OPERATION op);
VkResult FlushOrInvalidateAllocations(
uint32_t allocationCount,
const VmaAllocation* allocations,
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
VMA_CACHE_OPERATION op);
void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
uint32_t GetGpuDefragmentationMemoryTypeBits();
#if VMA_EXTERNAL_MEMORY
VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
{
return m_TypeExternalMemoryHandleTypes[memTypeIndex];
}
#endif
private:
VkDeviceSize m_PreferredLargeHeapBlockSize;
VkPhysicalDevice m_PhysicalDevice;
VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits;
#if VMA_EXTERNAL_MEMORY
VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
#endif
VMA_RW_MUTEX m_PoolsMutex;
typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
PoolList m_Pools;
uint32_t m_NextPoolId;
VmaVulkanFunctions m_VulkanFunctions;
uint32_t m_GlobalMemoryTypeBits;
void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
void ImportVulkanFunctions_Static();
#endif
void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
void ImportVulkanFunctions_Dynamic();
#endif
void ValidateVulkanFunctions();
VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
VkResult AllocateMemoryOfType(
VmaPool pool,
VkDeviceSize size,
VkDeviceSize alignment,
bool dedicatedPreferred,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
const VmaAllocationCreateInfo& createInfo,
uint32_t memTypeIndex,
VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
VmaBlockVector& blockVector,
size_t allocationCount,
VmaAllocation* pAllocations);
VkResult AllocateDedicatedMemoryPage(
VmaPool pool,
VkDeviceSize size,
VmaSuballocationType suballocType,
uint32_t memTypeIndex,
const VkMemoryAllocateInfo& allocInfo,
bool map,
bool isUserDataString,
bool isMappingAllowed,
void* pUserData,
VmaAllocation* pAllocation);
VkResult AllocateDedicatedMemory(
VmaPool pool,
VkDeviceSize size,
VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
uint32_t memTypeIndex,
bool map,
bool isUserDataString,
bool isMappingAllowed,
bool canAliasMemory,
void* pUserData,
float priority,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
size_t allocationCount,
VmaAllocation* pAllocations,
const void* pNextChain = nullptr);
void FreeDedicatedMemory(const VmaAllocation allocation);
VkResult CalcMemTypeParams(
VmaAllocationCreateInfo& outCreateInfo,
uint32_t memTypeIndex,
VkDeviceSize size,
size_t allocationCount);
VkResult CalcAllocationParams(
VmaAllocationCreateInfo& outCreateInfo,
bool dedicatedRequired,
bool dedicatedPreferred);
uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
uint32_t CalculateGlobalMemoryTypeBits() const;
bool GetFlushOrInvalidateRange(
VmaAllocation allocation,
VkDeviceSize offset, VkDeviceSize size,
VkMappedMemoryRange& outRange) const;
#if VMA_MEMORY_BUDGET
void UpdateVulkanBudget();
#endif
};
#ifndef _VMA_MEMORY_FUNCTIONS
static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
{
return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
}
static void VmaFree(VmaAllocator hAllocator, void* ptr)
{
VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
}
template<typename T>
static T* VmaAllocate(VmaAllocator hAllocator)
{
return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
}
template<typename T>
static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
{
return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
}
template<typename T>
static void vma_delete(VmaAllocator hAllocator, T* ptr)
{
if(ptr != VMA_NULL)
{
ptr->~T();
VmaFree(hAllocator, ptr);
}
}
template<typename T>
static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
{
if(ptr != VMA_NULL)
{
for(size_t i = count; i--; )
ptr[i].~T();
VmaFree(hAllocator, ptr);
}
}
#endif
#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
: m_pMetadata(VMA_NULL),
m_MemoryTypeIndex(UINT32_MAX),
m_Id(0),
m_hMemory(VK_NULL_HANDLE),
m_MapCount(0),
m_pMappedData(VMA_NULL) {}
VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
{
VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
}
void VmaDeviceMemoryBlock::Init(
VmaAllocator hAllocator,
VmaPool hParentPool,
uint32_t newMemoryTypeIndex,
VkDeviceMemory newMemory,
VkDeviceSize newSize,
uint32_t id,
uint32_t algorithm,
VkDeviceSize bufferImageGranularity)
{
VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
m_hParentPool = hParentPool;
m_MemoryTypeIndex = newMemoryTypeIndex;
m_Id = id;
m_hMemory = newMemory;
switch (algorithm)
{
case 0:
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
bufferImageGranularity, false);
break;
case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
bufferImageGranularity, false);
break;
default:
VMA_ASSERT(0);
m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
bufferImageGranularity, false);
}
m_pMetadata->Init(newSize);
}
void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
{
if (!m_pMetadata->IsEmpty())
m_pMetadata->DebugLogAllAllocations();
VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
m_hMemory = VK_NULL_HANDLE;
vma_delete(allocator, m_pMetadata);
m_pMetadata = VMA_NULL;
}
void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator)
{
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
m_MappingHysteresis.PostAlloc();
}
void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)
{
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
if(m_MappingHysteresis.PostFree())
{
VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);
if (m_MapCount == 0)
{
m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
}
}
}
bool VmaDeviceMemoryBlock::Validate() const
{
VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
(m_pMetadata->GetSize() != 0));
return m_pMetadata->Validate();
}
VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
{
void* pData = nullptr;
VkResult res = Map(hAllocator, 1, &pData);
if (res != VK_SUCCESS)
{
return res;
}
res = m_pMetadata->CheckCorruption(pData);
Unmap(hAllocator, 1);
return res;
}
VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
{
if (count == 0)
{
return VK_SUCCESS;
}
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
m_MappingHysteresis.PostMap();
if (oldTotalMapCount != 0)
{
m_MapCount += count;
VMA_ASSERT(m_pMappedData != VMA_NULL);
if (ppData != VMA_NULL)
{
*ppData = m_pMappedData;
}
return VK_SUCCESS;
}
else
{
VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
hAllocator->m_hDevice,
m_hMemory,
0,
VK_WHOLE_SIZE,
0,
&m_pMappedData);
if (result == VK_SUCCESS)
{
if (ppData != VMA_NULL)
{
*ppData = m_pMappedData;
}
m_MapCount = count;
}
return result;
}
}
void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
{
if (count == 0)
{
return;
}
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
if (m_MapCount >= count)
{
m_MapCount -= count;
const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
if (totalMapCount == 0)
{
m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
}
m_MappingHysteresis.PostUnmap();
}
else
{
VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
}
}
VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
{
VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
void* pData;
VkResult res = Map(hAllocator, 1, &pData);
if (res != VK_SUCCESS)
{
return res;
}
VmaWriteMagicValue(pData, allocOffset + allocSize);
Unmap(hAllocator, 1);
return VK_SUCCESS;
}
VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
{
VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
void* pData;
VkResult res = Map(hAllocator, 1, &pData);
if (res != VK_SUCCESS)
{
return res;
}
if (!VmaValidateMagicValue(pData, allocOffset + allocSize))
{
VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
}
Unmap(hAllocator, 1);
return VK_SUCCESS;
}
VkResult VmaDeviceMemoryBlock::BindBufferMemory(
const VmaAllocator hAllocator,
const VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkBuffer hBuffer,
const void* pNext)
{
VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
hAllocation->GetBlock() == this);
VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
"Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
}
VkResult VmaDeviceMemoryBlock::BindImageMemory(
const VmaAllocator hAllocator,
const VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext)
{
VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
hAllocation->GetBlock() == this);
VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
"Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
}
#endif
#ifndef _VMA_ALLOCATION_T_FUNCTIONS
VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)
: m_Alignment{ 1 },
m_Size{ 0 },
m_pUserData{ VMA_NULL },
m_pName{ VMA_NULL },
m_MemoryTypeIndex{ 0 },
m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },
m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },
m_MapCount{ 0 },
m_Flags{ 0 }
{
if(mappingAllowed)
m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;
#if VMA_STATS_STRING_ENABLED
m_BufferImageUsage = 0;
#endif
}
VmaAllocation_T::~VmaAllocation_T()
{
VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction.");
VMA_ASSERT(m_pName == VMA_NULL);
}
void VmaAllocation_T::InitBlockAllocation(
VmaDeviceMemoryBlock* block,
VmaAllocHandle allocHandle,
VkDeviceSize alignment,
VkDeviceSize size,
uint32_t memoryTypeIndex,
VmaSuballocationType suballocationType,
bool mapped)
{
VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
VMA_ASSERT(block != VMA_NULL);
m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
m_Alignment = alignment;
m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex;
if(mapped)
{
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
}
m_SuballocationType = (uint8_t)suballocationType;
m_BlockAllocation.m_Block = block;
m_BlockAllocation.m_AllocHandle = allocHandle;
}
void VmaAllocation_T::InitDedicatedAllocation(
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceMemory hMemory,
VmaSuballocationType suballocationType,
void* pMappedData,
VkDeviceSize size)
{
VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
VMA_ASSERT(hMemory != VK_NULL_HANDLE);
m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
m_Alignment = 0;
m_Size = size;
m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType;
if(pMappedData != VMA_NULL)
{
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
}
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL;
m_DedicatedAllocation.m_Next = VMA_NULL;
}
void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
{
VMA_ASSERT(pName == VMA_NULL || pName != m_pName);
FreeName(hAllocator);
if (pName != VMA_NULL)
m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);
}
uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)
{
VMA_ASSERT(allocation != VMA_NULL);
VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);
if (m_MapCount != 0)
m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);
m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);
VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation);
m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);
#if VMA_STATS_STRING_ENABLED
VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage);
#endif
return m_MapCount;
}
VmaAllocHandle VmaAllocation_T::GetAllocHandle() const
{
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_AllocHandle;
case ALLOCATION_TYPE_DEDICATED:
return VK_NULL_HANDLE;
default:
VMA_ASSERT(0);
return VK_NULL_HANDLE;
}
}
VkDeviceSize VmaAllocation_T::GetOffset() const
{
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);
case ALLOCATION_TYPE_DEDICATED:
return 0;
default:
VMA_ASSERT(0);
return 0;
}
}
VmaPool VmaAllocation_T::GetParentPool() const
{
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->GetParentPool();
case ALLOCATION_TYPE_DEDICATED:
return m_DedicatedAllocation.m_hParentPool;
default:
VMA_ASSERT(0);
return VK_NULL_HANDLE;
}
}
VkDeviceMemory VmaAllocation_T::GetMemory() const
{
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
return m_BlockAllocation.m_Block->GetDeviceMemory();
case ALLOCATION_TYPE_DEDICATED:
return m_DedicatedAllocation.m_hMemory;
default:
VMA_ASSERT(0);
return VK_NULL_HANDLE;
}
}
void* VmaAllocation_T::GetMappedData() const
{
switch (m_Type)
{
case ALLOCATION_TYPE_BLOCK:
if (m_MapCount != 0 || IsPersistentMap())
{
void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
VMA_ASSERT(pBlockData != VMA_NULL);
return (char*)pBlockData + GetOffset();
}
else
{
return VMA_NULL;
}
break;
case ALLOCATION_TYPE_DEDICATED:
VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
return m_DedicatedAllocation.m_pMappedData;
default:
VMA_ASSERT(0);
return VMA_NULL;
}
}
void VmaAllocation_T::BlockAllocMap()
{
VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
if (m_MapCount < 0xFF)
{
++m_MapCount;
}
else
{
VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
}
}
void VmaAllocation_T::BlockAllocUnmap()
{
VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
if (m_MapCount > 0)
{
--m_MapCount;
}
else
{
VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
}
}
VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
{
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
if (m_MapCount != 0 || IsPersistentMap())
{
if (m_MapCount < 0xFF)
{
VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
*ppData = m_DedicatedAllocation.m_pMappedData;
++m_MapCount;
return VK_SUCCESS;
}
else
{
VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
return VK_ERROR_MEMORY_MAP_FAILED;
}
}
else
{
VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory,
0,
VK_WHOLE_SIZE,
0,
ppData);
if (result == VK_SUCCESS)
{
m_DedicatedAllocation.m_pMappedData = *ppData;
m_MapCount = 1;
}
return result;
}
}
void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
{
VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
if (m_MapCount > 0)
{
--m_MapCount;
if (m_MapCount == 0 && !IsPersistentMap())
{
m_DedicatedAllocation.m_pMappedData = VMA_NULL;
(*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
hAllocator->m_hDevice,
m_DedicatedAllocation.m_hMemory);
}
}
else
{
VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
}
}
#if VMA_STATS_STRING_ENABLED
void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage)
{
VMA_ASSERT(m_BufferImageUsage == 0);
m_BufferImageUsage = bufferImageUsage;
}
void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
{
json.WriteString("Type");
json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
json.WriteString("Size");
json.WriteNumber(m_Size);
json.WriteString("Usage");
json.WriteNumber(m_BufferImageUsage);
if (m_pUserData != VMA_NULL)
{
json.WriteString("CustomData");
json.BeginString();
json.ContinueString_Pointer(m_pUserData);
json.EndString();
}
if (m_pName != VMA_NULL)
{
json.WriteString("Name");
json.WriteString(m_pName);
}
}
#endif
void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
{
if(m_pName)
{
VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);
m_pName = VMA_NULL;
}
}
#endif
#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS
VmaBlockVector::VmaBlockVector(
VmaAllocator hAllocator,
VmaPool hParentPool,
uint32_t memoryTypeIndex,
VkDeviceSize preferredBlockSize,
size_t minBlockCount,
size_t maxBlockCount,
VkDeviceSize bufferImageGranularity,
bool explicitBlockSize,
uint32_t algorithm,
float priority,
VkDeviceSize minAllocationAlignment,
void* pMemoryAllocateNext)
: m_hAllocator(hAllocator),
m_hParentPool(hParentPool),
m_MemoryTypeIndex(memoryTypeIndex),
m_PreferredBlockSize(preferredBlockSize),
m_MinBlockCount(minBlockCount),
m_MaxBlockCount(maxBlockCount),
m_BufferImageGranularity(bufferImageGranularity),
m_ExplicitBlockSize(explicitBlockSize),
m_Algorithm(algorithm),
m_Priority(priority),
m_MinAllocationAlignment(minAllocationAlignment),
m_pMemoryAllocateNext(pMemoryAllocateNext),
m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
m_NextBlockId(0) {}
VmaBlockVector::~VmaBlockVector()
{
for (size_t i = m_Blocks.size(); i--; )
{
m_Blocks[i]->Destroy(m_hAllocator);
vma_delete(m_hAllocator, m_Blocks[i]);
}
}
VkResult VmaBlockVector::CreateMinBlocks()
{
for (size_t i = 0; i < m_MinBlockCount; ++i)
{
VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
if (res != VK_SUCCESS)
{
return res;
}
}
return VK_SUCCESS;
}
void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)
{
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
const size_t blockCount = m_Blocks.size();
for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
{
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
VMA_ASSERT(pBlock);
VMA_HEAVY_ASSERT(pBlock->Validate());
pBlock->m_pMetadata->AddStatistics(inoutStats);
}
}
void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
{
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
const size_t blockCount = m_Blocks.size();
for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
{
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
VMA_ASSERT(pBlock);
VMA_HEAVY_ASSERT(pBlock->Validate());
pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);
}
}
bool VmaBlockVector::IsEmpty()
{
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
return m_Blocks.empty();
}
bool VmaBlockVector::IsCorruptionDetectionEnabled() const
{
const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
(VMA_DEBUG_MARGIN > 0) &&
(m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
(m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
}
VkResult VmaBlockVector::Allocate(
VkDeviceSize size,
VkDeviceSize alignment,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
size_t allocationCount,
VmaAllocation* pAllocations)
{
size_t allocIndex;
VkResult res = VK_SUCCESS;
alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
if (IsCorruptionDetectionEnabled())
{
size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
}
{
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{
res = AllocatePage(
size,
alignment,
createInfo,
suballocType,
pAllocations + allocIndex);
if (res != VK_SUCCESS)
{
break;
}
}
}
if (res != VK_SUCCESS)
{
while (allocIndex--)
Free(pAllocations[allocIndex]);
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
}
return res;
}
VkResult VmaBlockVector::AllocatePage(
VkDeviceSize size,
VkDeviceSize alignment,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation)
{
const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
VkDeviceSize freeMemory;
{
const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
VmaBudget heapBudget = {};
m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
}
const bool canFallbackToDedicated = !HasExplicitBlockSize() &&
(createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;
const bool canCreateNewBlock =
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
(m_Blocks.size() < m_MaxBlockCount) &&
(freeMemory >= size || !canFallbackToDedicated);
uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
if (isUpperAddress &&
(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
{
return VK_ERROR_FEATURE_NOT_PRESENT;
}
if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
{
if (!m_Blocks.empty())
{
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
if (res == VK_SUCCESS)
{
VMA_DEBUG_LOG_FORMAT(" Returned from last block #%u", pCurrBlock->GetId());
IncrementallySortBlocks();
return VK_SUCCESS;
}
}
}
else
{
if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)
{
const bool isHostVisible =
(m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
if(isHostVisible)
{
const bool isMappingAllowed = (createInfo.flags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
for(size_t mappingI = 0; mappingI < 2; ++mappingI)
{
for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
{
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
VMA_ASSERT(pCurrBlock);
const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;
if((mappingI == 0) == (isMappingAllowed == isBlockMapped))
{
VkResult res = AllocateFromBlock(
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
if (res == VK_SUCCESS)
{
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId());
IncrementallySortBlocks();
return VK_SUCCESS;
}
}
}
}
}
else
{
for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
{
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(
pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
if (res == VK_SUCCESS)
{
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId());
IncrementallySortBlocks();
return VK_SUCCESS;
}
}
}
}
else
{
for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
{
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
if (res == VK_SUCCESS)
{
VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId());
IncrementallySortBlocks();
return VK_SUCCESS;
}
}
}
}
if (canCreateNewBlock)
{
VkDeviceSize newBlockSize = m_PreferredBlockSize;
uint32_t newBlockSizeShift = 0;
const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
if (!m_ExplicitBlockSize)
{
const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
{
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
{
newBlockSize = smallerNewBlockSize;
++newBlockSizeShift;
}
else
{
break;
}
}
}
size_t newBlockIndex = 0;
VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
if (!m_ExplicitBlockSize)
{
while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
{
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
if (smallerNewBlockSize >= size)
{
newBlockSize = smallerNewBlockSize;
++newBlockSizeShift;
res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
else
{
break;
}
}
}
if (res == VK_SUCCESS)
{
VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
res = AllocateFromBlock(
pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
if (res == VK_SUCCESS)
{
VMA_DEBUG_LOG_FORMAT(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
IncrementallySortBlocks();
return VK_SUCCESS;
}
else
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
}
}
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
void VmaBlockVector::Free(const VmaAllocation hAllocation)
{
VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
bool budgetExceeded = false;
{
const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
VmaBudget heapBudget = {};
m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
budgetExceeded = heapBudget.usage >= heapBudget.budget;
}
{
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
if (IsCorruptionDetectionEnabled())
{
VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
}
if (hAllocation->IsPersistentMap())
{
pBlock->Unmap(m_hAllocator, 1);
}
const bool hadEmptyBlockBeforeFree = HasEmptyBlock();
pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());
pBlock->PostFree(m_hAllocator);
VMA_HEAVY_ASSERT(pBlock->Validate());
VMA_DEBUG_LOG_FORMAT(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
if (pBlock->m_pMetadata->IsEmpty())
{
if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)
{
pBlockToDelete = pBlock;
Remove(pBlock);
}
}
else if (hadEmptyBlockBeforeFree && canDeleteBlock)
{
VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
if (pLastBlock->m_pMetadata->IsEmpty())
{
pBlockToDelete = pLastBlock;
m_Blocks.pop_back();
}
}
IncrementallySortBlocks();
}
if (pBlockToDelete != VMA_NULL)
{
VMA_DEBUG_LOG_FORMAT(" Deleted empty block #%u", pBlockToDelete->GetId());
pBlockToDelete->Destroy(m_hAllocator);
vma_delete(m_hAllocator, pBlockToDelete);
}
m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
}
VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
{
VkDeviceSize result = 0;
for (size_t i = m_Blocks.size(); i--; )
{
result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
if (result >= m_PreferredBlockSize)
{
break;
}
}
return result;
}
void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
{
for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
{
if (m_Blocks[blockIndex] == pBlock)
{
VmaVectorRemove(m_Blocks, blockIndex);
return;
}
}
VMA_ASSERT(0);
}
void VmaBlockVector::IncrementallySortBlocks()
{
if (!m_IncrementalSort)
return;
if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
{
for (size_t i = 1; i < m_Blocks.size(); ++i)
{
if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
{
VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
return;
}
}
}
}
void VmaBlockVector::SortByFreeSize()
{
VMA_SORT(m_Blocks.begin(), m_Blocks.end(),
[](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool
{
return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();
});
}
VkResult VmaBlockVector::AllocateFromBlock(
VmaDeviceMemoryBlock* pBlock,
VkDeviceSize size,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
uint32_t strategy,
VmaAllocation* pAllocation)
{
const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
VmaAllocationRequest currRequest = {};
if (pBlock->m_pMetadata->CreateAllocationRequest(
size,
alignment,
isUpperAddress,
suballocType,
strategy,
&currRequest))
{
return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);
}
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
VkResult VmaBlockVector::CommitAllocationRequest(
VmaAllocationRequest& allocRequest,
VmaDeviceMemoryBlock* pBlock,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation)
{
const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
const bool isMappingAllowed = (allocFlags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
pBlock->PostAlloc(m_hAllocator);
if (mapped)
{
VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
if (res != VK_SUCCESS)
{
return res;
}
}
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);
pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);
(*pAllocation)->InitBlockAllocation(
pBlock,
allocRequest.allocHandle,
alignment,
allocRequest.size,
m_MemoryTypeIndex,
suballocType,
mapped);
VMA_HEAVY_ASSERT(pBlock->Validate());
if (isUserDataString)
(*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);
else
(*pAllocation)->SetUserData(m_hAllocator, pUserData);
m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);
if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
}
if (IsCorruptionDetectionEnabled())
{
VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
}
return VK_SUCCESS;
}
VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
{
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
allocInfo.pNext = m_pMemoryAllocateNext;
allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
allocInfo.allocationSize = blockSize;
#if VMA_BUFFER_DEVICE_ADDRESS
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
if (m_hAllocator->m_UseKhrBufferDeviceAddress)
{
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
}
#endif
#if VMA_MEMORY_PRIORITY
VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
if (m_hAllocator->m_UseExtMemoryPriority)
{
VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f);
priorityInfo.priority = m_Priority;
VmaPnextChainPushFront(&allocInfo, &priorityInfo);
}
#endif
#if VMA_EXTERNAL_MEMORY
VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
if (exportMemoryAllocInfo.handleTypes != 0)
{
VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
}
#endif
VkDeviceMemory mem = VK_NULL_HANDLE;
VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
if (res < 0)
{
return res;
}
VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
pBlock->Init(
m_hAllocator,
m_hParentPool,
m_MemoryTypeIndex,
mem,
allocInfo.allocationSize,
m_NextBlockId++,
m_Algorithm,
m_BufferImageGranularity);
m_Blocks.push_back(pBlock);
if (pNewBlockIndex != VMA_NULL)
{
*pNewBlockIndex = m_Blocks.size() - 1;
}
return VK_SUCCESS;
}
bool VmaBlockVector::HasEmptyBlock()
{
for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)
{
VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
if (pBlock->m_pMetadata->IsEmpty())
{
return true;
}
}
return false;
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
{
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
json.BeginObject();
for (size_t i = 0; i < m_Blocks.size(); ++i)
{
json.BeginString();
json.ContinueString(m_Blocks[i]->GetId());
json.EndString();
json.BeginObject();
json.WriteString("MapRefCount");
json.WriteNumber(m_Blocks[i]->GetMapRefCount());
m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
json.EndObject();
}
json.EndObject();
}
#endif
VkResult VmaBlockVector::CheckCorruption()
{
if (!IsCorruptionDetectionEnabled())
{
return VK_ERROR_FEATURE_NOT_PRESENT;
}
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
{
VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
VMA_ASSERT(pBlock);
VkResult res = pBlock->CheckCorruption(m_hAllocator);
if (res != VK_SUCCESS)
{
return res;
}
}
return VK_SUCCESS;
}
#endif
#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
VmaDefragmentationContext_T::VmaDefragmentationContext_T(
VmaAllocator hAllocator,
const VmaDefragmentationInfo& info)
: m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),
m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),
m_BreakCallback(info.pfnBreakCallback),
m_BreakCallbackUserData(info.pBreakCallbackUserData),
m_MoveAllocator(hAllocator->GetAllocationCallbacks()),
m_Moves(m_MoveAllocator)
{
m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK;
if (info.pool != VMA_NULL)
{
m_BlockVectorCount = 1;
m_PoolBlockVector = &info.pool->m_BlockVector;
m_pBlockVectors = &m_PoolBlockVector;
m_PoolBlockVector->SetIncrementalSort(false);
m_PoolBlockVector->SortByFreeSize();
}
else
{
m_BlockVectorCount = hAllocator->GetMemoryTypeCount();
m_PoolBlockVector = VMA_NULL;
m_pBlockVectors = hAllocator->m_pBlockVectors;
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
{
VmaBlockVector* vector = m_pBlockVectors[i];
if (vector != VMA_NULL)
{
vector->SetIncrementalSort(false);
vector->SortByFreeSize();
}
}
}
switch (m_Algorithm)
{
case 0:
m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;
m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
break;
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
break;
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
if (hAllocator->GetBufferImageGranularity() > 1)
{
m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);
}
break;
}
}
VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
{
if (m_PoolBlockVector != VMA_NULL)
{
m_PoolBlockVector->SetIncrementalSort(true);
}
else
{
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
{
VmaBlockVector* vector = m_pBlockVectors[i];
if (vector != VMA_NULL)
vector->SetIncrementalSort(true);
}
}
if (m_AlgorithmState)
{
switch (m_Algorithm)
{
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);
break;
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);
break;
default:
VMA_ASSERT(0);
}
}
}
VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)
{
if (m_PoolBlockVector != VMA_NULL)
{
VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);
if (m_PoolBlockVector->GetBlockCount() > 1)
ComputeDefragmentation(*m_PoolBlockVector, 0);
else if (m_PoolBlockVector->GetBlockCount() == 1)
ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));
}
else
{
for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
{
if (m_pBlockVectors[i] != VMA_NULL)
{
VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);
if (m_pBlockVectors[i]->GetBlockCount() > 1)
{
if (ComputeDefragmentation(*m_pBlockVectors[i], i))
break;
}
else if (m_pBlockVectors[i]->GetBlockCount() == 1)
{
if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))
break;
}
}
}
}
moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());
if (moveInfo.moveCount > 0)
{
moveInfo.pMoves = m_Moves.data();
return VK_INCOMPLETE;
}
moveInfo.pMoves = VMA_NULL;
return VK_SUCCESS;
}
VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)
{
VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);
VkResult result = VK_SUCCESS;
VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);
VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);
VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);
VmaAllocator allocator = VMA_NULL;
for (uint32_t i = 0; i < moveInfo.moveCount; ++i)
{
VmaDefragmentationMove& move = moveInfo.pMoves[i];
size_t prevCount = 0, currentCount = 0;
VkDeviceSize freedBlockSize = 0;
uint32_t vectorIndex;
VmaBlockVector* vector;
if (m_PoolBlockVector != VMA_NULL)
{
vectorIndex = 0;
vector = m_PoolBlockVector;
}
else
{
vectorIndex = move.srcAllocation->GetMemoryTypeIndex();
vector = m_pBlockVectors[vectorIndex];
VMA_ASSERT(vector != VMA_NULL);
}
switch (move.operation)
{
case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:
{
uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);
if (mapCount > 0)
{
allocator = vector->m_hAllocator;
VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();
bool notPresent = true;
for (FragmentedBlock& block : mappedBlocks)
{
if (block.block == newMapBlock)
{
notPresent = false;
block.data += mapCount;
break;
}
}
if (notPresent)
mappedBlocks.push_back({ mapCount, newMapBlock });
}
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
prevCount = vector->GetBlockCount();
freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
}
vector->Free(move.dstTmpAllocation);
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
currentCount = vector->GetBlockCount();
}
result = VK_INCOMPLETE;
break;
}
case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:
{
m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
--m_PassStats.allocationsMoved;
vector->Free(move.dstTmpAllocation);
VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();
bool notPresent = true;
for (const FragmentedBlock& block : immovableBlocks)
{
if (block.block == newBlock)
{
notPresent = false;
break;
}
}
if (notPresent)
immovableBlocks.push_back({ vectorIndex, newBlock });
break;
}
case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:
{
m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
--m_PassStats.allocationsMoved;
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
prevCount = vector->GetBlockCount();
freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();
}
vector->Free(move.srcAllocation);
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
currentCount = vector->GetBlockCount();
}
freedBlockSize *= prevCount - currentCount;
VkDeviceSize dstBlockSize;
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
}
vector->Free(move.dstTmpAllocation);
{
VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());
currentCount = vector->GetBlockCount();
}
result = VK_INCOMPLETE;
break;
}
default:
VMA_ASSERT(0);
}
if (prevCount > currentCount)
{
size_t freedBlocks = prevCount - currentCount;
m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);
m_PassStats.bytesFreed += freedBlockSize;
}
if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT &&
m_AlgorithmState != VMA_NULL)
{
StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];
if (state.firstFreeBlock != SIZE_MAX)
{
const size_t diff = prevCount - currentCount;
if (state.firstFreeBlock >= diff)
{
state.firstFreeBlock -= diff;
if (state.firstFreeBlock != 0)
state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();
}
else
state.firstFreeBlock = 0;
}
}
}
moveInfo.moveCount = 0;
moveInfo.pMoves = VMA_NULL;
m_Moves.clear();
m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;
m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;
m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;
m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;
m_PassStats = { 0 };
if (immovableBlocks.size() > 0)
{
do
{
if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT)
{
if (m_AlgorithmState != VMA_NULL)
{
bool swapped = false;
for (const FragmentedBlock& block : immovableBlocks)
{
StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];
if (state.operation != StateExtensive::Operation::Cleanup)
{
VmaBlockVector* vector = m_pBlockVectors[block.data];
VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)
{
if (vector->GetBlock(i) == block.block)
{
VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);
if (state.firstFreeBlock != SIZE_MAX)
{
if (i + 1 < state.firstFreeBlock)
{
if (state.firstFreeBlock > 1)
VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
else
--state.firstFreeBlock;
}
}
swapped = true;
break;
}
}
}
}
if (swapped)
result = VK_INCOMPLETE;
break;
}
}
for (const FragmentedBlock& block : immovableBlocks)
{
VmaBlockVector* vector = m_pBlockVectors[block.data];
VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)
{
if (vector->GetBlock(i) == block.block)
{
VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);
break;
}
}
}
} while (false);
}
for (const FragmentedBlock& block : mappedBlocks)
{
VkResult res = block.block->Map(allocator, block.data, VMA_NULL);
VMA_ASSERT(res == VK_SUCCESS);
}
return result;
}
bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)
{
switch (m_Algorithm)
{
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:
return ComputeDefragmentation_Fast(vector);
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
return ComputeDefragmentation_Balanced(vector, index, true);
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:
return ComputeDefragmentation_Full(vector);
case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
return ComputeDefragmentation_Extensive(vector, index);
default:
VMA_ASSERT(0);
return ComputeDefragmentation_Balanced(vector, index, true);
}
}
VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(
VmaAllocHandle handle, VmaBlockMetadata* metadata)
{
MoveAllocationData moveData;
moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);
moveData.size = moveData.move.srcAllocation->GetSize();
moveData.alignment = moveData.move.srcAllocation->GetAlignment();
moveData.type = moveData.move.srcAllocation->GetSuballocationType();
moveData.flags = 0;
if (moveData.move.srcAllocation->IsPersistentMap())
moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
if (moveData.move.srcAllocation->IsMappingAllowed())
moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
return moveData;
}
VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)
{
if (m_BreakCallback && m_BreakCallback(m_BreakCallbackUserData))
return CounterStatus::End;
if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)
{
if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)
return CounterStatus::Ignore;
else
return CounterStatus::End;
}
else
m_IgnoredAllocs = 0;
return CounterStatus::Pass;
}
bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)
{
m_PassStats.bytesMoved += bytes;
if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)
{
VMA_ASSERT((m_PassStats.allocationsMoved == m_MaxPassAllocations ||
m_PassStats.bytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");
return true;
}
return false;
}
bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)
{
VmaBlockMetadata* metadata = block->m_pMetadata;
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = metadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, metadata);
if (moveData.move.srcAllocation->GetUserData() == this)
continue;
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
{
VmaAllocationRequest request = {};
if (metadata->CreateAllocationRequest(
moveData.size,
moveData.alignment,
false,
moveData.type,
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
&request))
{
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
{
if (vector.CommitAllocationRequest(
request,
block,
moveData.alignment,
moveData.flags,
this,
moveData.type,
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
{
m_Moves.push_back(moveData.move);
if (IncrementCounters(moveData.size))
return true;
}
}
}
}
}
return false;
}
bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)
{
for (; start < end; ++start)
{
VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);
if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)
{
if (vector.AllocateFromBlock(dstBlock,
data.size,
data.alignment,
data.flags,
this,
data.type,
0,
&data.move.dstTmpAllocation) == VK_SUCCESS)
{
m_Moves.push_back(data.move);
if (IncrementCounters(data.size))
return true;
break;
}
}
}
return false;
}
bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)
{
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
{
VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = metadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, metadata);
if (moveData.move.srcAllocation->GetUserData() == this)
continue;
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
if (AllocInOtherBlock(0, i, moveData, vector))
return true;
}
}
return false;
}
bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)
{
VMA_ASSERT(m_AlgorithmState != VMA_NULL);
StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];
if (update && vectorState.avgAllocSize == UINT64_MAX)
UpdateVectorStatistics(vector, vectorState);
const size_t startMoveCount = m_Moves.size();
VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
{
VmaDeviceMemoryBlock* block = vector.GetBlock(i);
VmaBlockMetadata* metadata = block->m_pMetadata;
VkDeviceSize prevFreeRegionSize = 0;
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = metadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, metadata);
if (moveData.move.srcAllocation->GetUserData() == this)
continue;
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
const size_t prevMoveCount = m_Moves.size();
if (AllocInOtherBlock(0, i, moveData, vector))
return true;
VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
{
if (prevFreeRegionSize >= minimalFreeRegion ||
nextFreeRegionSize >= minimalFreeRegion ||
moveData.size <= vectorState.avgFreeSize ||
moveData.size <= vectorState.avgAllocSize)
{
VmaAllocationRequest request = {};
if (metadata->CreateAllocationRequest(
moveData.size,
moveData.alignment,
false,
moveData.type,
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
&request))
{
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
{
if (vector.CommitAllocationRequest(
request,
block,
moveData.alignment,
moveData.flags,
this,
moveData.type,
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
{
m_Moves.push_back(moveData.move);
if (IncrementCounters(moveData.size))
return true;
}
}
}
}
}
prevFreeRegionSize = nextFreeRegionSize;
}
}
if (startMoveCount == m_Moves.size() && !update)
{
vectorState.avgAllocSize = UINT64_MAX;
return ComputeDefragmentation_Balanced(vector, index, false);
}
return false;
}
bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)
{
for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
{
VmaDeviceMemoryBlock* block = vector.GetBlock(i);
VmaBlockMetadata* metadata = block->m_pMetadata;
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = metadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, metadata);
if (moveData.move.srcAllocation->GetUserData() == this)
continue;
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
const size_t prevMoveCount = m_Moves.size();
if (AllocInOtherBlock(0, i, moveData, vector))
return true;
VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
{
VmaAllocationRequest request = {};
if (metadata->CreateAllocationRequest(
moveData.size,
moveData.alignment,
false,
moveData.type,
VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
&request))
{
if (metadata->GetAllocationOffset(request.allocHandle) < offset)
{
if (vector.CommitAllocationRequest(
request,
block,
moveData.alignment,
moveData.flags,
this,
moveData.type,
&moveData.move.dstTmpAllocation) == VK_SUCCESS)
{
m_Moves.push_back(moveData.move);
if (IncrementCounters(moveData.size))
return true;
}
}
}
}
}
}
return false;
}
bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)
{
if (vector.m_BufferImageGranularity == 1)
return ComputeDefragmentation_Full(vector);
VMA_ASSERT(m_AlgorithmState != VMA_NULL);
StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];
bool texturePresent = false, bufferPresent = false, otherPresent = false;
switch (vectorState.operation)
{
case StateExtensive::Operation::Done:
return false;
case StateExtensive::Operation::FindFreeBlockBuffer:
case StateExtensive::Operation::FindFreeBlockTexture:
case StateExtensive::Operation::FindFreeBlockAll:
{
if (vectorState.firstFreeBlock == 0)
{
vectorState.operation = StateExtensive::Operation::Cleanup;
return ComputeDefragmentation_Fast(vector);
}
size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;
VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;
const size_t prevMoveCount = m_Moves.size();
for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = freeMetadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, freeMetadata);
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
if (AllocInOtherBlock(0, last, moveData, vector))
{
if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)
vectorState.firstFreeBlock = last;
return true;
}
}
if (prevMoveCount == m_Moves.size())
{
if (last != 0)
{
for (size_t i = last - 1; i; --i)
{
if (ReallocWithinBlock(vector, vector.GetBlock(i)))
return true;
}
}
if (prevMoveCount == m_Moves.size())
{
return ComputeDefragmentation_Fast(vector);
}
}
else
{
switch (vectorState.operation)
{
case StateExtensive::Operation::FindFreeBlockBuffer:
vectorState.operation = StateExtensive::Operation::MoveBuffers;
break;
case StateExtensive::Operation::FindFreeBlockTexture:
vectorState.operation = StateExtensive::Operation::MoveTextures;
break;
case StateExtensive::Operation::FindFreeBlockAll:
vectorState.operation = StateExtensive::Operation::MoveAll;
break;
default:
VMA_ASSERT(0);
vectorState.operation = StateExtensive::Operation::MoveTextures;
}
vectorState.firstFreeBlock = last;
return ComputeDefragmentation_Extensive(vector, index);
}
break;
}
case StateExtensive::Operation::MoveTextures:
{
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
{
if (texturePresent)
{
vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;
return ComputeDefragmentation_Extensive(vector, index);
}
if (!bufferPresent && !otherPresent)
{
vectorState.operation = StateExtensive::Operation::Cleanup;
break;
}
vectorState.operation = StateExtensive::Operation::MoveBuffers;
bufferPresent = false;
otherPresent = false;
}
else
break;
VMA_FALLTHROUGH;
}
case StateExtensive::Operation::MoveBuffers:
{
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
{
if (bufferPresent)
{
vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
return ComputeDefragmentation_Extensive(vector, index);
}
if (!otherPresent)
{
vectorState.operation = StateExtensive::Operation::Cleanup;
break;
}
vectorState.operation = StateExtensive::Operation::MoveAll;
otherPresent = false;
}
else
break;
VMA_FALLTHROUGH;
}
case StateExtensive::Operation::MoveAll:
{
if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,
vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
{
if (otherPresent)
{
vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
return ComputeDefragmentation_Extensive(vector, index);
}
vectorState.operation = StateExtensive::Operation::Cleanup;
}
break;
}
case StateExtensive::Operation::Cleanup:
break;
}
if (vectorState.operation == StateExtensive::Operation::Cleanup)
{
const size_t prevMoveCount = m_Moves.size();
for (size_t i = 0; i < vector.GetBlockCount(); ++i)
{
if (ReallocWithinBlock(vector, vector.GetBlock(i)))
return true;
}
if (prevMoveCount == m_Moves.size())
vectorState.operation = StateExtensive::Operation::Done;
}
return false;
}
void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)
{
size_t allocCount = 0;
size_t freeCount = 0;
state.avgFreeSize = 0;
state.avgAllocSize = 0;
for (size_t i = 0; i < vector.GetBlockCount(); ++i)
{
VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
allocCount += metadata->GetAllocationCount();
freeCount += metadata->GetFreeRegionsCount();
state.avgFreeSize += metadata->GetSumFreeSize();
state.avgAllocSize += metadata->GetSize();
}
state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;
state.avgFreeSize /= freeCount;
}
bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,
VmaBlockVector& vector, size_t firstFreeBlock,
bool& texturePresent, bool& bufferPresent, bool& otherPresent)
{
const size_t prevMoveCount = m_Moves.size();
for (size_t i = firstFreeBlock ; i;)
{
VmaDeviceMemoryBlock* block = vector.GetBlock(--i);
VmaBlockMetadata* metadata = block->m_pMetadata;
for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
handle != VK_NULL_HANDLE;
handle = metadata->GetNextAllocation(handle))
{
MoveAllocationData moveData = GetMoveData(handle, metadata);
if (moveData.move.srcAllocation->GetUserData() == this)
continue;
switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
{
case CounterStatus::Ignore:
continue;
case CounterStatus::End:
return true;
case CounterStatus::Pass:
break;
default:
VMA_ASSERT(0);
}
if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))
{
if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))
return false;
}
if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))
texturePresent = true;
else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))
bufferPresent = true;
else
otherPresent = true;
}
}
return prevMoveCount == m_Moves.size();
}
#endif
#ifndef _VMA_POOL_T_FUNCTIONS
VmaPool_T::VmaPool_T(
VmaAllocator hAllocator,
const VmaPoolCreateInfo& createInfo,
VkDeviceSize preferredBlockSize)
: m_BlockVector(
hAllocator,
this,
createInfo.memoryTypeIndex,
createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
createInfo.minBlockCount,
createInfo.maxBlockCount,
(createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
createInfo.blockSize != 0,
createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK,
createInfo.priority,
VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
createInfo.pMemoryAllocateNext),
m_Id(0),
m_Name(VMA_NULL) {}
VmaPool_T::~VmaPool_T()
{
VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
}
void VmaPool_T::SetName(const char* pName)
{
const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
VmaFreeString(allocs, m_Name);
if (pName != VMA_NULL)
{
m_Name = VmaCreateStringCopy(allocs, pName);
}
else
{
m_Name = VMA_NULL;
}
}
#endif
#ifndef _VMA_ALLOCATOR_T_FUNCTIONS
VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
m_hDevice(pCreateInfo->device),
m_hInstance(pCreateInfo->instance),
m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
*pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
m_AllocationObjectAllocator(&m_AllocationCallbacks),
m_HeapSizeLimitMask(0),
m_DeviceMemoryCount(0),
m_PreferredLargeHeapBlockSize(0),
m_PhysicalDevice(pCreateInfo->physicalDevice),
m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
m_NextPoolId(0),
m_GlobalMemoryTypeBits(UINT32_MAX)
{
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
m_UseKhrDedicatedAllocation = false;
m_UseKhrBindMemory2 = false;
}
if(VMA_DEBUG_DETECT_CORRUPTION)
{
VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
}
VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
{
#if !(VMA_DEDICATED_ALLOCATION)
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
}
#endif
#if !(VMA_BIND_MEMORY2)
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
}
#endif
}
#if !(VMA_MEMORY_BUDGET)
if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
}
#endif
#if !(VMA_BUFFER_DEVICE_ADDRESS)
if(m_UseKhrBufferDeviceAddress)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
#if VMA_VULKAN_VERSION < 1003000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
{
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros.");
}
#endif
#if VMA_VULKAN_VERSION < 1002000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
{
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
}
#endif
#if VMA_VULKAN_VERSION < 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
}
#endif
#if !(VMA_MEMORY_PRIORITY)
if(m_UseExtMemoryPriority)
{
VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
}
#endif
memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
memset(&m_MemProps, 0, sizeof(m_MemProps));
memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
#if VMA_EXTERNAL_MEMORY
memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
#endif
if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
{
m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
}
ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
(*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
(*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
#if VMA_EXTERNAL_MEMORY
if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
{
memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
}
#endif
if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
{
for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
{
const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
if(limit != VK_WHOLE_SIZE)
{
m_HeapSizeLimitMask |= 1u << heapIndex;
if(limit < m_MemProps.memoryHeaps[heapIndex].size)
{
m_MemProps.memoryHeaps[heapIndex].size = limit;
}
}
}
}
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
{
const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
this,
VK_NULL_HANDLE,
memTypeIndex,
preferredBlockSize,
0,
SIZE_MAX,
GetBufferImageGranularity(),
false,
0,
0.5f,
GetMemoryTypeMinAlignment(memTypeIndex),
VMA_NULL);
}
}
}
VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
{
VkResult res = VK_SUCCESS;
#if VMA_MEMORY_BUDGET
if(m_UseExtMemoryBudget)
{
UpdateVulkanBudget();
}
#endif
return res;
}
VmaAllocator_T::~VmaAllocator_T()
{
VMA_ASSERT(m_Pools.IsEmpty());
for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
{
vma_delete(this, m_pBlockVectors[memTypeIndex]);
}
}
void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
{
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
ImportVulkanFunctions_Static();
#endif
if(pVulkanFunctions != VMA_NULL)
{
ImportVulkanFunctions_Custom(pVulkanFunctions);
}
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
ImportVulkanFunctions_Dynamic();
#endif
ValidateVulkanFunctions();
}
#if VMA_STATIC_VULKAN_FUNCTIONS == 1
void VmaAllocator_T::ImportVulkanFunctions_Static()
{
m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;
m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;
m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
#if VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
}
#endif
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
}
#endif
#if VMA_VULKAN_VERSION >= 1003000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
{
m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;
m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;
}
#endif
}
#endif
void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
{
VMA_ASSERT(pVulkanFunctions != VMA_NULL);
#define VMA_COPY_IF_NOT_NULL …
VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);
VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
VMA_COPY_IF_NOT_NULL(vkFreeMemory);
VMA_COPY_IF_NOT_NULL(vkMapMemory);
VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
VMA_COPY_IF_NOT_NULL(vkCreateImage);
VMA_COPY_IF_NOT_NULL(vkDestroyImage);
VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
#endif
#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
#endif
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
#endif
#if VMA_VULKAN_VERSION >= 1003000
VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
#endif
#undef VMA_COPY_IF_NOT_NULL
}
#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
{
VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&
"To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "
"VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "
"Other members can be null.");
#define VMA_FETCH_INSTANCE_FUNC …
#define VMA_FETCH_DEVICE_FUNC …
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
#if VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
}
#endif
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
}
else if(m_UseExtMemoryBudget)
{
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2KHR");
}
#endif
#if VMA_DEDICATED_ALLOCATION
if(m_UseKhrDedicatedAllocation)
{
VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
}
#endif
#if VMA_BIND_MEMORY2
if(m_UseKhrBindMemory2)
{
VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
}
#endif
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");
}
else if(m_UseExtMemoryBudget)
{
VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
}
#endif
#if VMA_VULKAN_VERSION >= 1003000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
{
VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");
VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");
}
#endif
#undef VMA_FETCH_DEVICE_FUNC
#undef VMA_FETCH_INSTANCE_FUNC
}
#endif
void VmaAllocator_T::ValidateVulkanFunctions()
{
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
{
VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
}
#endif
#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
{
VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
}
#endif
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
}
#endif
#if VMA_VULKAN_VERSION >= 1003000
if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
{
VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);
VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);
}
#endif
}
VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
{
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
}
VkResult VmaAllocator_T::AllocateMemoryOfType(
VmaPool pool,
VkDeviceSize size,
VkDeviceSize alignment,
bool dedicatedPreferred,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
const VmaAllocationCreateInfo& createInfo,
uint32_t memTypeIndex,
VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
VmaBlockVector& blockVector,
size_t allocationCount,
VmaAllocation* pAllocations)
{
VMA_ASSERT(pAllocations != VMA_NULL);
VMA_DEBUG_LOG_FORMAT(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
VmaAllocationCreateInfo finalCreateInfo = createInfo;
VkResult res = CalcMemTypeParams(
finalCreateInfo,
memTypeIndex,
size,
allocationCount);
if(res != VK_SUCCESS)
return res;
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
{
return AllocateDedicatedMemory(
pool,
size,
suballocType,
dedicatedAllocations,
memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
(finalCreateInfo.flags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedImage,
dedicatedBufferImageUsage,
allocationCount,
pAllocations,
blockVector.GetAllocationNextPtr());
}
else
{
const bool canAllocateDedicated =
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
(pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());
if(canAllocateDedicated)
{
if(size > blockVector.GetPreferredBlockSize() / 2)
{
dedicatedPreferred = true;
}
if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 &&
m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
{
dedicatedPreferred = false;
}
if(dedicatedPreferred)
{
res = AllocateDedicatedMemory(
pool,
size,
suballocType,
dedicatedAllocations,
memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
(finalCreateInfo.flags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedImage,
dedicatedBufferImageUsage,
allocationCount,
pAllocations,
blockVector.GetAllocationNextPtr());
if(res == VK_SUCCESS)
{
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
return VK_SUCCESS;
}
}
}
res = blockVector.Allocate(
size,
alignment,
finalCreateInfo,
suballocType,
allocationCount,
pAllocations);
if(res == VK_SUCCESS)
return VK_SUCCESS;
if(canAllocateDedicated && !dedicatedPreferred)
{
res = AllocateDedicatedMemory(
pool,
size,
suballocType,
dedicatedAllocations,
memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
(finalCreateInfo.flags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedImage,
dedicatedBufferImageUsage,
allocationCount,
pAllocations,
blockVector.GetAllocationNextPtr());
if(res == VK_SUCCESS)
{
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
return VK_SUCCESS;
}
}
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
return res;
}
}
VkResult VmaAllocator_T::AllocateDedicatedMemory(
VmaPool pool,
VkDeviceSize size,
VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
uint32_t memTypeIndex,
bool map,
bool isUserDataString,
bool isMappingAllowed,
bool canAliasMemory,
void* pUserData,
float priority,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
size_t allocationCount,
VmaAllocation* pAllocations,
const void* pNextChain)
{
VMA_ASSERT(allocationCount > 0 && pAllocations);
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
allocInfo.memoryTypeIndex = memTypeIndex;
allocInfo.allocationSize = size;
allocInfo.pNext = pNextChain;
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
if(!canAliasMemory)
{
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
if(dedicatedBuffer != VK_NULL_HANDLE)
{
VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
dedicatedAllocInfo.buffer = dedicatedBuffer;
VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
}
else if(dedicatedImage != VK_NULL_HANDLE)
{
dedicatedAllocInfo.image = dedicatedImage;
VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
}
}
}
#endif
#if VMA_BUFFER_DEVICE_ADDRESS
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
if(m_UseKhrBufferDeviceAddress)
{
bool canContainBufferWithDeviceAddress = true;
if(dedicatedBuffer != VK_NULL_HANDLE)
{
canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX ||
(dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
}
else if(dedicatedImage != VK_NULL_HANDLE)
{
canContainBufferWithDeviceAddress = false;
}
if(canContainBufferWithDeviceAddress)
{
allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
}
}
#endif
#if VMA_MEMORY_PRIORITY
VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
if(m_UseExtMemoryPriority)
{
VMA_ASSERT(priority >= 0.f && priority <= 1.f);
priorityInfo.priority = priority;
VmaPnextChainPushFront(&allocInfo, &priorityInfo);
}
#endif
#if VMA_EXTERNAL_MEMORY
VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
if(exportMemoryAllocInfo.handleTypes != 0)
{
VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
}
#endif
size_t allocIndex;
VkResult res = VK_SUCCESS;
for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{
res = AllocateDedicatedMemoryPage(
pool,
size,
suballocType,
memTypeIndex,
allocInfo,
map,
isUserDataString,
isMappingAllowed,
pUserData,
pAllocations + allocIndex);
if(res != VK_SUCCESS)
{
break;
}
}
if(res == VK_SUCCESS)
{
for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{
dedicatedAllocations.Register(pAllocations[allocIndex]);
}
VMA_DEBUG_LOG_FORMAT(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
}
else
{
while(allocIndex--)
{
VmaAllocation currAlloc = pAllocations[allocIndex];
VkDeviceMemory hMemory = currAlloc->GetMemory();
FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
m_AllocationObjectAllocator.Free(currAlloc);
}
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
}
return res;
}
VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
VmaPool pool,
VkDeviceSize size,
VmaSuballocationType suballocType,
uint32_t memTypeIndex,
const VkMemoryAllocateInfo& allocInfo,
bool map,
bool isUserDataString,
bool isMappingAllowed,
void* pUserData,
VmaAllocation* pAllocation)
{
VkDeviceMemory hMemory = VK_NULL_HANDLE;
VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
if(res < 0)
{
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
return res;
}
void* pMappedData = VMA_NULL;
if(map)
{
res = (*m_VulkanFunctions.vkMapMemory)(
m_hDevice,
hMemory,
0,
VK_WHOLE_SIZE,
0,
&pMappedData);
if(res < 0)
{
VMA_DEBUG_LOG(" vkMapMemory FAILED");
FreeVulkanMemory(memTypeIndex, size, hMemory);
return res;
}
}
*pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
(*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
if (isUserDataString)
(*pAllocation)->SetName(this, (const char*)pUserData);
else
(*pAllocation)->SetUserData(this, pUserData);
m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{
FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
}
return VK_SUCCESS;
}
void VmaAllocator_T::GetBufferMemoryRequirements(
VkBuffer hBuffer,
VkMemoryRequirements& memReq,
bool& requiresDedicatedAllocation,
bool& prefersDedicatedAllocation) const
{
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
memReqInfo.buffer = hBuffer;
VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
(*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
memReq = memReq2.memoryRequirements;
requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
}
else
#endif
{
(*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
requiresDedicatedAllocation = false;
prefersDedicatedAllocation = false;
}
}
void VmaAllocator_T::GetImageMemoryRequirements(
VkImage hImage,
VkMemoryRequirements& memReq,
bool& requiresDedicatedAllocation,
bool& prefersDedicatedAllocation) const
{
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
{
VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
memReqInfo.image = hImage;
VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
(*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
memReq = memReq2.memoryRequirements;
requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
}
else
#endif
{
(*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
requiresDedicatedAllocation = false;
prefersDedicatedAllocation = false;
}
}
VkResult VmaAllocator_T::FindMemoryTypeIndex(
uint32_t memoryTypeBits,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
VkFlags bufImgUsage,
uint32_t* pMemoryTypeIndex) const
{
memoryTypeBits &= GetGlobalMemoryTypeBits();
if(pAllocationCreateInfo->memoryTypeBits != 0)
{
memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
}
VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0;
if(!FindMemoryPreferences(
IsIntegratedGpu(),
*pAllocationCreateInfo,
bufImgUsage,
requiredFlags, preferredFlags, notPreferredFlags))
{
return VK_ERROR_FEATURE_NOT_PRESENT;
}
*pMemoryTypeIndex = UINT32_MAX;
uint32_t minCost = UINT32_MAX;
for(uint32_t memTypeIndex = 0, memTypeBit = 1;
memTypeIndex < GetMemoryTypeCount();
++memTypeIndex, memTypeBit <<= 1)
{
if((memTypeBit & memoryTypeBits) != 0)
{
const VkMemoryPropertyFlags currFlags =
m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
if((requiredFlags & ~currFlags) == 0)
{
uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) +
VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);
if(currCost < minCost)
{
*pMemoryTypeIndex = memTypeIndex;
if(currCost == 0)
{
return VK_SUCCESS;
}
minCost = currCost;
}
}
}
}
return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
}
VkResult VmaAllocator_T::CalcMemTypeParams(
VmaAllocationCreateInfo& inoutCreateInfo,
uint32_t memTypeIndex,
VkDeviceSize size,
size_t allocationCount)
{
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
(m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
}
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)
{
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
VmaBudget heapBudget = {};
GetHeapBudgets(&heapBudget, heapIndex, 1);
if(heapBudget.usage + size * allocationCount > heapBudget.budget)
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
}
return VK_SUCCESS;
}
VkResult VmaAllocator_T::CalcAllocationParams(
VmaAllocationCreateInfo& inoutCreateInfo,
bool dedicatedRequired,
bool dedicatedPreferred)
{
VMA_ASSERT((inoutCreateInfo.flags &
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=
(VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&
"Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");
VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||
(inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&
"Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
{
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)
{
VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&
"When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
}
}
if(dedicatedRequired ||
inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
{
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
}
if(inoutCreateInfo.pool != VK_NULL_HANDLE)
{
if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
{
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
return VK_ERROR_FEATURE_NOT_PRESENT;
}
inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
}
if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
{
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
return VK_ERROR_FEATURE_NOT_PRESENT;
}
if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
{
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
}
if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&
inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&
inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
{
if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)
{
inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
}
}
return VK_SUCCESS;
}
VkResult VmaAllocator_T::AllocateMemory(
const VkMemoryRequirements& vkMemReq,
bool requiresDedicatedAllocation,
bool prefersDedicatedAllocation,
VkBuffer dedicatedBuffer,
VkImage dedicatedImage,
VkFlags dedicatedBufferImageUsage,
const VmaAllocationCreateInfo& createInfo,
VmaSuballocationType suballocType,
size_t allocationCount,
VmaAllocation* pAllocations)
{
memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
if(vkMemReq.size == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
VmaAllocationCreateInfo createInfoFinal = createInfo;
VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);
if(res != VK_SUCCESS)
return res;
if(createInfoFinal.pool != VK_NULL_HANDLE)
{
VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
return AllocateMemoryOfType(
createInfoFinal.pool,
vkMemReq.size,
vkMemReq.alignment,
prefersDedicatedAllocation,
dedicatedBuffer,
dedicatedImage,
dedicatedBufferImageUsage,
createInfoFinal,
blockVector.GetMemoryTypeIndex(),
suballocType,
createInfoFinal.pool->m_DedicatedAllocations,
blockVector,
allocationCount,
pAllocations);
}
else
{
uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
uint32_t memTypeIndex = UINT32_MAX;
res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
if(res != VK_SUCCESS)
return res;
do
{
VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
res = AllocateMemoryOfType(
VK_NULL_HANDLE,
vkMemReq.size,
vkMemReq.alignment,
requiresDedicatedAllocation || prefersDedicatedAllocation,
dedicatedBuffer,
dedicatedImage,
dedicatedBufferImageUsage,
createInfoFinal,
memTypeIndex,
suballocType,
m_DedicatedAllocations[memTypeIndex],
*blockVector,
allocationCount,
pAllocations);
if(res == VK_SUCCESS)
return VK_SUCCESS;
memoryTypeBits &= ~(1u << memTypeIndex);
res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
} while(res == VK_SUCCESS);
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
}
void VmaAllocator_T::FreeMemory(
size_t allocationCount,
const VmaAllocation* pAllocations)
{
VMA_ASSERT(pAllocations);
for(size_t allocIndex = allocationCount; allocIndex--; )
{
VmaAllocation allocation = pAllocations[allocIndex];
if(allocation != VK_NULL_HANDLE)
{
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{
FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
}
allocation->FreeName(this);
switch(allocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
VmaBlockVector* pBlockVector = VMA_NULL;
VmaPool hPool = allocation->GetParentPool();
if(hPool != VK_NULL_HANDLE)
{
pBlockVector = &hPool->m_BlockVector;
}
else
{
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
pBlockVector = m_pBlockVectors[memTypeIndex];
VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
}
pBlockVector->Free(allocation);
}
break;
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
FreeDedicatedMemory(allocation);
break;
default:
VMA_ASSERT(0);
}
}
}
}
void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)
{
VmaClearDetailedStatistics(pStats->total);
for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
VmaClearDetailedStatistics(pStats->memoryType[i]);
for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
VmaClearDetailedStatistics(pStats->memoryHeap[i]);
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
if (pBlockVector != VMA_NULL)
pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
}
{
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
{
VmaBlockVector& blockVector = pool->m_BlockVector;
const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
}
}
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
}
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);
}
for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)
VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);
VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||
pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);
VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||
pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);
}
void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)
{
#if VMA_MEMORY_BUDGET
if(m_UseExtMemoryBudget)
{
if(m_Budget.m_OperationsSinceBudgetFetch < 30)
{
VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
{
const uint32_t heapIndex = firstHeap + i;
outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
{
outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] +
outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
}
else
{
outBudgets->usage = 0;
}
outBudgets->budget = VMA_MIN(
m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
}
}
else
{
UpdateVulkanBudget();
GetHeapBudgets(outBudgets, firstHeap, heapCount);
}
}
else
#endif
{
for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
{
const uint32_t heapIndex = firstHeap + i;
outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
outBudgets->usage = outBudgets->statistics.blockBytes;
outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10;
}
}
}
void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
{
pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
pAllocationInfo->deviceMemory = hAllocation->GetMemory();
pAllocationInfo->offset = hAllocation->GetOffset();
pAllocationInfo->size = hAllocation->GetSize();
pAllocationInfo->pMappedData = hAllocation->GetMappedData();
pAllocationInfo->pUserData = hAllocation->GetUserData();
pAllocationInfo->pName = hAllocation->GetName();
}
VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
{
VMA_DEBUG_LOG_FORMAT(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
if(pCreateInfo->pMemoryAllocateNext)
{
VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
}
if(newCreateInfo.maxBlockCount == 0)
{
newCreateInfo.maxBlockCount = SIZE_MAX;
}
if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
{
return VK_ERROR_FEATURE_NOT_PRESENT;
}
if(newCreateInfo.minAllocationAlignment > 0)
{
VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
}
const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
*pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
if(res != VK_SUCCESS)
{
vma_delete(this, *pPool);
*pPool = VMA_NULL;
return res;
}
{
VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
(*pPool)->SetId(m_NextPoolId++);
m_Pools.PushBack(*pPool);
}
return VK_SUCCESS;
}
void VmaAllocator_T::DestroyPool(VmaPool pool)
{
{
VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
m_Pools.Remove(pool);
}
vma_delete(this, pool);
}
void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)
{
VmaClearStatistics(*pPoolStats);
pool->m_BlockVector.AddStatistics(*pPoolStats);
pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);
}
void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)
{
VmaClearDetailedStatistics(*pPoolStats);
pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);
pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);
}
void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
{
m_CurrentFrameIndex.store(frameIndex);
#if VMA_MEMORY_BUDGET
if(m_UseExtMemoryBudget)
{
UpdateVulkanBudget();
}
#endif
}
VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
{
return hPool->m_BlockVector.CheckCorruption();
}
VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
{
VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
if(pBlockVector != VMA_NULL)
{
VkResult localRes = pBlockVector->CheckCorruption();
switch(localRes)
{
case VK_ERROR_FEATURE_NOT_PRESENT:
break;
case VK_SUCCESS:
finalRes = VK_SUCCESS;
break;
default:
return localRes;
}
}
}
{
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
{
if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
{
VkResult localRes = pool->m_BlockVector.CheckCorruption();
switch(localRes)
{
case VK_ERROR_FEATURE_NOT_PRESENT:
break;
case VK_SUCCESS:
finalRes = VK_SUCCESS;
break;
default:
return localRes;
}
}
}
}
return finalRes;
}
VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
{
AtomicTransactionalIncrement<VMA_ATOMIC_UINT32> deviceMemoryCountIncrement;
const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
{
return VK_ERROR_TOO_MANY_OBJECTS;
}
#endif
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
{
const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
for(;;)
{
const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
if(blockBytesAfterAllocation > heapSize)
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
{
break;
}
}
}
else
{
m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
}
++m_Budget.m_BlockCount[heapIndex];
VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
if(res == VK_SUCCESS)
{
#if VMA_MEMORY_BUDGET
++m_Budget.m_OperationsSinceBudgetFetch;
#endif
if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
{
(*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
}
deviceMemoryCountIncrement.Commit();
}
else
{
--m_Budget.m_BlockCount[heapIndex];
m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
}
return res;
}
void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
{
if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
{
(*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
}
(*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
--m_Budget.m_BlockCount[heapIndex];
m_Budget.m_BlockBytes[heapIndex] -= size;
--m_DeviceMemoryCount;
}
VkResult VmaAllocator_T::BindVulkanBuffer(
VkDeviceMemory memory,
VkDeviceSize memoryOffset,
VkBuffer buffer,
const void* pNext)
{
if(pNext != VMA_NULL)
{
#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
{
VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
bindBufferMemoryInfo.pNext = pNext;
bindBufferMemoryInfo.buffer = buffer;
bindBufferMemoryInfo.memory = memory;
bindBufferMemoryInfo.memoryOffset = memoryOffset;
return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
}
else
#endif
{
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
else
{
return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
}
}
VkResult VmaAllocator_T::BindVulkanImage(
VkDeviceMemory memory,
VkDeviceSize memoryOffset,
VkImage image,
const void* pNext)
{
if(pNext != VMA_NULL)
{
#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
{
VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
bindBufferMemoryInfo.pNext = pNext;
bindBufferMemoryInfo.image = image;
bindBufferMemoryInfo.memory = memory;
bindBufferMemoryInfo.memoryOffset = memoryOffset;
return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
}
else
#endif
{
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
}
else
{
return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
}
}
VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
{
switch(hAllocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
char *pBytes = VMA_NULL;
VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
if(res == VK_SUCCESS)
{
*ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
hAllocation->BlockAllocMap();
}
return res;
}
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
return hAllocation->DedicatedAllocMap(this, ppData);
default:
VMA_ASSERT(0);
return VK_ERROR_MEMORY_MAP_FAILED;
}
}
void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
{
switch(hAllocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
hAllocation->BlockAllocUnmap();
pBlock->Unmap(this, 1);
}
break;
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
hAllocation->DedicatedAllocUnmap(this);
break;
default:
VMA_ASSERT(0);
}
}
VkResult VmaAllocator_T::BindBufferMemory(
VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkBuffer hBuffer,
const void* pNext)
{
VkResult res = VK_ERROR_UNKNOWN;
switch(hAllocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
break;
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");
res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
break;
}
default:
VMA_ASSERT(0);
}
return res;
}
VkResult VmaAllocator_T::BindImageMemory(
VmaAllocation hAllocation,
VkDeviceSize allocationLocalOffset,
VkImage hImage,
const void* pNext)
{
VkResult res = VK_ERROR_UNKNOWN;
switch(hAllocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
break;
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");
res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
break;
}
default:
VMA_ASSERT(0);
}
return res;
}
VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
VmaAllocation hAllocation,
VkDeviceSize offset, VkDeviceSize size,
VMA_CACHE_OPERATION op)
{
VkResult res = VK_SUCCESS;
VkMappedMemoryRange memRange = {};
if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
{
switch(op)
{
case VMA_CACHE_FLUSH:
res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
break;
case VMA_CACHE_INVALIDATE:
res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
break;
default:
VMA_ASSERT(0);
}
}
return res;
}
VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
uint32_t allocationCount,
const VmaAllocation* allocations,
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
VMA_CACHE_OPERATION op)
{
typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{
const VmaAllocation alloc = allocations[allocIndex];
const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
VkMappedMemoryRange newRange;
if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
{
ranges.push_back(newRange);
}
}
VkResult res = VK_SUCCESS;
if(!ranges.empty())
{
switch(op)
{
case VMA_CACHE_FLUSH:
res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
break;
case VMA_CACHE_INVALIDATE:
res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
break;
default:
VMA_ASSERT(0);
}
}
return res;
}
void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
{
VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
VmaPool parentPool = allocation->GetParentPool();
if(parentPool == VK_NULL_HANDLE)
{
m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
}
else
{
parentPool->m_DedicatedAllocations.Unregister(allocation);
}
VkDeviceMemory hMemory = allocation->GetMemory();
FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
m_AllocationObjectAllocator.Free(allocation);
VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
}
uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
{
VkBufferCreateInfo dummyBufCreateInfo;
VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
uint32_t memoryTypeBits = 0;
VkBuffer buf = VK_NULL_HANDLE;
VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
if(res == VK_SUCCESS)
{
VkMemoryRequirements memReq;
(*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
memoryTypeBits = memReq.memoryTypeBits;
(*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
}
return memoryTypeBits;
}
uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
{
VMA_ASSERT(GetMemoryTypeCount() > 0);
uint32_t memoryTypeBits = UINT32_MAX;
if(!m_UseAmdDeviceCoherentMemory)
{
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
{
memoryTypeBits &= ~(1u << memTypeIndex);
}
}
}
return memoryTypeBits;
}
bool VmaAllocator_T::GetFlushOrInvalidateRange(
VmaAllocation allocation,
VkDeviceSize offset, VkDeviceSize size,
VkMappedMemoryRange& outRange) const
{
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
{
const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
const VkDeviceSize allocationSize = allocation->GetSize();
VMA_ASSERT(offset <= allocationSize);
outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
outRange.pNext = VMA_NULL;
outRange.memory = allocation->GetMemory();
switch(allocation->GetType())
{
case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
if(size == VK_WHOLE_SIZE)
{
outRange.size = allocationSize - outRange.offset;
}
else
{
VMA_ASSERT(offset + size <= allocationSize);
outRange.size = VMA_MIN(
VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
allocationSize - outRange.offset);
}
break;
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{
outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
if(size == VK_WHOLE_SIZE)
{
size = allocationSize - offset;
}
else
{
VMA_ASSERT(offset + size <= allocationSize);
}
outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
const VkDeviceSize allocationOffset = allocation->GetOffset();
VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
outRange.offset += allocationOffset;
outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
break;
}
default:
VMA_ASSERT(0);
}
return true;
}
return false;
}
#if VMA_MEMORY_BUDGET
void VmaAllocator_T::UpdateVulkanBudget()
{
VMA_ASSERT(m_UseExtMemoryBudget);
VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
VmaPnextChainPushFront(&memProps, &budgetProps);
GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
{
VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
{
m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
if(m_Budget.m_VulkanBudget[heapIndex] == 0)
{
m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10;
}
else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
{
m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
}
if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
{
m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
}
}
m_Budget.m_OperationsSinceBudgetFetch = 0;
}
}
#endif
void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
{
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
hAllocation->IsMappingAllowed() &&
(m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
void* pData = VMA_NULL;
VkResult res = Map(hAllocation, &pData);
if(res == VK_SUCCESS)
{
memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
Unmap(hAllocation);
}
else
{
VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
}
}
}
uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
{
uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
if(memoryTypeBits == UINT32_MAX)
{
memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
}
return memoryTypeBits;
}
#if VMA_STATS_STRING_ENABLED
void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
{
json.WriteString("DefaultPools");
json.BeginObject();
{
for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];
VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
if (pBlockVector != VMA_NULL)
{
json.BeginString("Type ");
json.ContinueString(memTypeIndex);
json.EndString();
json.BeginObject();
{
json.WriteString("PreferredBlockSize");
json.WriteNumber(pBlockVector->GetPreferredBlockSize());
json.WriteString("Blocks");
pBlockVector->PrintDetailedMap(json);
json.WriteString("DedicatedAllocations");
dedicatedAllocList.BuildStatsString(json);
}
json.EndObject();
}
}
}
json.EndObject();
json.WriteString("CustomPools");
json.BeginObject();
{
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
if (!m_Pools.IsEmpty())
{
for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
bool displayType = true;
size_t index = 0;
for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
{
VmaBlockVector& blockVector = pool->m_BlockVector;
if (blockVector.GetMemoryTypeIndex() == memTypeIndex)
{
if (displayType)
{
json.BeginString("Type ");
json.ContinueString(memTypeIndex);
json.EndString();
json.BeginArray();
displayType = false;
}
json.BeginObject();
{
json.WriteString("Name");
json.BeginString();
json.ContinueString((uint64_t)index++);
if (pool->GetName())
{
json.ContinueString(" - ");
json.ContinueString(pool->GetName());
}
json.EndString();
json.WriteString("PreferredBlockSize");
json.WriteNumber(blockVector.GetPreferredBlockSize());
json.WriteString("Blocks");
blockVector.PrintDetailedMap(json);
json.WriteString("DedicatedAllocations");
pool->m_DedicatedAllocations.BuildStatsString(json);
}
json.EndObject();
}
}
if (!displayType)
json.EndArray();
}
}
}
json.EndObject();
}
#endif
#endif
#ifndef _VMA_PUBLIC_INTERFACE
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
const VmaAllocatorCreateInfo* pCreateInfo,
VmaAllocator* pAllocator)
{
VMA_ASSERT(pCreateInfo && pAllocator);
VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
(VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
VMA_DEBUG_LOG("vmaCreateAllocator");
*pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
VkResult result = (*pAllocator)->Init(pCreateInfo);
if(result < 0)
{
vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
*pAllocator = VK_NULL_HANDLE;
}
return result;
}
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
VmaAllocator allocator)
{
if(allocator != VK_NULL_HANDLE)
{
VMA_DEBUG_LOG("vmaDestroyAllocator");
VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
vma_delete(&allocationCallbacks, allocator);
}
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
{
VMA_ASSERT(allocator && pAllocatorInfo);
pAllocatorInfo->instance = allocator->m_hInstance;
pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
pAllocatorInfo->device = allocator->m_hDevice;
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
VmaAllocator allocator,
const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
{
VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
*ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
VmaAllocator allocator,
const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
{
VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
*ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
VmaAllocator allocator,
uint32_t memoryTypeIndex,
VkMemoryPropertyFlags* pFlags)
{
VMA_ASSERT(allocator && pFlags);
VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
*pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
}
VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
VmaAllocator allocator,
uint32_t frameIndex)
{
VMA_ASSERT(allocator);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->SetCurrentFrameIndex(frameIndex);
}
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
VmaAllocator allocator,
VmaTotalStatistics* pStats)
{
VMA_ASSERT(allocator && pStats);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->CalculateStatistics(pStats);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
VmaAllocator allocator,
VmaBudget* pBudgets)
{
VMA_ASSERT(allocator && pBudgets);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());
}
#if VMA_STATS_STRING_ENABLED
VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
VmaAllocator allocator,
char** ppStatsString,
VkBool32 detailedMap)
{
VMA_ASSERT(allocator && ppStatsString);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VmaStringBuilder sb(allocator->GetAllocationCallbacks());
{
VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());
VmaTotalStatistics stats;
allocator->CalculateStatistics(&stats);
VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
json.BeginObject();
{
json.WriteString("General");
json.BeginObject();
{
const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;
const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;
json.WriteString("API");
json.WriteString("Vulkan");
json.WriteString("apiVersion");
json.BeginString();
json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion));
json.ContinueString(".");
json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion));
json.ContinueString(".");
json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion));
json.EndString();
json.WriteString("GPU");
json.WriteString(deviceProperties.deviceName);
json.WriteString("deviceType");
json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));
json.WriteString("maxMemoryAllocationCount");
json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);
json.WriteString("bufferImageGranularity");
json.WriteNumber(deviceProperties.limits.bufferImageGranularity);
json.WriteString("nonCoherentAtomSize");
json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);
json.WriteString("memoryHeapCount");
json.WriteNumber(memoryProperties.memoryHeapCount);
json.WriteString("memoryTypeCount");
json.WriteNumber(memoryProperties.memoryTypeCount);
}
json.EndObject();
}
{
json.WriteString("Total");
VmaPrintDetailedStatistics(json, stats.total);
}
{
json.WriteString("MemoryInfo");
json.BeginObject();
{
for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
{
json.BeginString("Heap ");
json.ContinueString(heapIndex);
json.EndString();
json.BeginObject();
{
const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];
json.WriteString("Flags");
json.BeginArray(true);
{
if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
json.WriteString("DEVICE_LOCAL");
#if VMA_VULKAN_VERSION >= 1001000
if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)
json.WriteString("MULTI_INSTANCE");
#endif
VkMemoryHeapFlags flags = heapInfo.flags &
~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT
#if VMA_VULKAN_VERSION >= 1001000
| VK_MEMORY_HEAP_MULTI_INSTANCE_BIT
#endif
);
if (flags != 0)
json.WriteNumber(flags);
}
json.EndArray();
json.WriteString("Size");
json.WriteNumber(heapInfo.size);
json.WriteString("Budget");
json.BeginObject();
{
json.WriteString("BudgetBytes");
json.WriteNumber(budgets[heapIndex].budget);
json.WriteString("UsageBytes");
json.WriteNumber(budgets[heapIndex].usage);
}
json.EndObject();
json.WriteString("Stats");
VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);
json.WriteString("MemoryPools");
json.BeginObject();
{
for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
{
if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
{
json.BeginString("Type ");
json.ContinueString(typeIndex);
json.EndString();
json.BeginObject();
{
json.WriteString("Flags");
json.BeginArray(true);
{
VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
json.WriteString("DEVICE_LOCAL");
if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
json.WriteString("HOST_VISIBLE");
if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
json.WriteString("HOST_COHERENT");
if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
json.WriteString("HOST_CACHED");
if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
json.WriteString("LAZILY_ALLOCATED");
#if VMA_VULKAN_VERSION >= 1001000
if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)
json.WriteString("PROTECTED");
#endif
#if VK_AMD_device_coherent_memory
if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)
json.WriteString("DEVICE_COHERENT_AMD");
if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)
json.WriteString("DEVICE_UNCACHED_AMD");
#endif
flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
#if VMA_VULKAN_VERSION >= 1001000
| VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
#endif
#if VK_AMD_device_coherent_memory
| VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY
| VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY
#endif
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
if (flags != 0)
json.WriteNumber(flags);
}
json.EndArray();
json.WriteString("Stats");
VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);
}
json.EndObject();
}
}
}
json.EndObject();
}
json.EndObject();
}
}
json.EndObject();
}
if (detailedMap == VK_TRUE)
allocator->PrintDetailedMap(json);
json.EndObject();
}
*ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
}
VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
VmaAllocator allocator,
char* pStatsString)
{
if(pStatsString != VMA_NULL)
{
VMA_ASSERT(allocator);
VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
}
}
#endif
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
VmaAllocator allocator,
uint32_t memoryTypeBits,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
uint32_t* pMemoryTypeIndex)
{
VMA_ASSERT(allocator != VK_NULL_HANDLE);
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
VmaAllocator allocator,
const VkBufferCreateInfo* pBufferCreateInfo,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
uint32_t* pMemoryTypeIndex)
{
VMA_ASSERT(allocator != VK_NULL_HANDLE);
VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
const VkDevice hDev = allocator->m_hDevice;
const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
VkResult res;
#if VMA_VULKAN_VERSION >= 1003000
if(funcs->vkGetDeviceBufferMemoryRequirements)
{
VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS};
devBufMemReq.pCreateInfo = pBufferCreateInfo;
VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
(*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);
res = allocator->FindMemoryTypeIndex(
memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
}
else
#endif
{
VkBuffer hBuffer = VK_NULL_HANDLE;
res = funcs->vkCreateBuffer(
hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
if(res == VK_SUCCESS)
{
VkMemoryRequirements memReq = {};
funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);
res = allocator->FindMemoryTypeIndex(
memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
funcs->vkDestroyBuffer(
hDev, hBuffer, allocator->GetAllocationCallbacks());
}
}
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
VmaAllocator allocator,
const VkImageCreateInfo* pImageCreateInfo,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
uint32_t* pMemoryTypeIndex)
{
VMA_ASSERT(allocator != VK_NULL_HANDLE);
VMA_ASSERT(pImageCreateInfo != VMA_NULL);
VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
const VkDevice hDev = allocator->m_hDevice;
const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
VkResult res;
#if VMA_VULKAN_VERSION >= 1003000
if(funcs->vkGetDeviceImageMemoryRequirements)
{
VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS};
devImgMemReq.pCreateInfo = pImageCreateInfo;
VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&
"Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");
VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
(*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);
res = allocator->FindMemoryTypeIndex(
memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
}
else
#endif
{
VkImage hImage = VK_NULL_HANDLE;
res = funcs->vkCreateImage(
hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
if(res == VK_SUCCESS)
{
VkMemoryRequirements memReq = {};
funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);
res = allocator->FindMemoryTypeIndex(
memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
funcs->vkDestroyImage(
hDev, hImage, allocator->GetAllocationCallbacks());
}
}
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
VmaAllocator allocator,
const VmaPoolCreateInfo* pCreateInfo,
VmaPool* pPool)
{
VMA_ASSERT(allocator && pCreateInfo && pPool);
VMA_DEBUG_LOG("vmaCreatePool");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->CreatePool(pCreateInfo, pPool);
}
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
VmaAllocator allocator,
VmaPool pool)
{
VMA_ASSERT(allocator);
if(pool == VK_NULL_HANDLE)
{
return;
}
VMA_DEBUG_LOG("vmaDestroyPool");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->DestroyPool(pool);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
VmaAllocator allocator,
VmaPool pool,
VmaStatistics* pPoolStats)
{
VMA_ASSERT(allocator && pool && pPoolStats);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->GetPoolStatistics(pool, pPoolStats);
}
VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
VmaAllocator allocator,
VmaPool pool,
VmaDetailedStatistics* pPoolStats)
{
VMA_ASSERT(allocator && pool && pPoolStats);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->CalculatePoolStatistics(pool, pPoolStats);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
{
VMA_ASSERT(allocator && pool);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VMA_DEBUG_LOG("vmaCheckPoolCorruption");
return allocator->CheckPoolCorruption(pool);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
VmaAllocator allocator,
VmaPool pool,
const char** ppName)
{
VMA_ASSERT(allocator && pool && ppName);
VMA_DEBUG_LOG("vmaGetPoolName");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
*ppName = pool->GetName();
}
VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
VmaAllocator allocator,
VmaPool pool,
const char* pName)
{
VMA_ASSERT(allocator && pool);
VMA_DEBUG_LOG("vmaSetPoolName");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
pool->SetName(pName);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
VmaAllocator allocator,
const VkMemoryRequirements* pVkMemoryRequirements,
const VmaAllocationCreateInfo* pCreateInfo,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
VMA_DEBUG_LOG("vmaAllocateMemory");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkResult result = allocator->AllocateMemory(
*pVkMemoryRequirements,
false,
false,
VK_NULL_HANDLE,
VK_NULL_HANDLE,
UINT32_MAX,
*pCreateInfo,
VMA_SUBALLOCATION_TYPE_UNKNOWN,
1,
pAllocation);
if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return result;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
VmaAllocator allocator,
const VkMemoryRequirements* pVkMemoryRequirements,
const VmaAllocationCreateInfo* pCreateInfo,
size_t allocationCount,
VmaAllocation* pAllocations,
VmaAllocationInfo* pAllocationInfo)
{
if(allocationCount == 0)
{
return VK_SUCCESS;
}
VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
VMA_DEBUG_LOG("vmaAllocateMemoryPages");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkResult result = allocator->AllocateMemory(
*pVkMemoryRequirements,
false,
false,
VK_NULL_HANDLE,
VK_NULL_HANDLE,
UINT32_MAX,
*pCreateInfo,
VMA_SUBALLOCATION_TYPE_UNKNOWN,
allocationCount,
pAllocations);
if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
{
for(size_t i = 0; i < allocationCount; ++i)
{
allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
}
}
return result;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
VmaAllocator allocator,
VkBuffer buffer,
const VmaAllocationCreateInfo* pCreateInfo,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkMemoryRequirements vkMemReq = {};
bool requiresDedicatedAllocation = false;
bool prefersDedicatedAllocation = false;
allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation);
VkResult result = allocator->AllocateMemory(
vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation,
buffer,
VK_NULL_HANDLE,
UINT32_MAX,
*pCreateInfo,
VMA_SUBALLOCATION_TYPE_BUFFER,
1,
pAllocation);
if(pAllocationInfo && result == VK_SUCCESS)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return result;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
VmaAllocator allocator,
VkImage image,
const VmaAllocationCreateInfo* pCreateInfo,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkMemoryRequirements vkMemReq = {};
bool requiresDedicatedAllocation = false;
bool prefersDedicatedAllocation = false;
allocator->GetImageMemoryRequirements(image, vkMemReq,
requiresDedicatedAllocation, prefersDedicatedAllocation);
VkResult result = allocator->AllocateMemory(
vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation,
VK_NULL_HANDLE,
image,
UINT32_MAX,
*pCreateInfo,
VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
1,
pAllocation);
if(pAllocationInfo && result == VK_SUCCESS)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return result;
}
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
VmaAllocator allocator,
VmaAllocation allocation)
{
VMA_ASSERT(allocator);
if(allocation == VK_NULL_HANDLE)
{
return;
}
VMA_DEBUG_LOG("vmaFreeMemory");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->FreeMemory(
1,
&allocation);
}
VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
VmaAllocator allocator,
size_t allocationCount,
const VmaAllocation* pAllocations)
{
if(allocationCount == 0)
{
return;
}
VMA_ASSERT(allocator);
VMA_DEBUG_LOG("vmaFreeMemoryPages");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->FreeMemory(allocationCount, pAllocations);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
VmaAllocator allocator,
VmaAllocation allocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && allocation && pAllocationInfo);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->GetAllocationInfo(allocation, pAllocationInfo);
}
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
VmaAllocator allocator,
VmaAllocation allocation,
void* pUserData)
{
VMA_ASSERT(allocator && allocation);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocation->SetUserData(allocator, pUserData);
}
VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
const char* VMA_NULLABLE pName)
{
allocation->SetName(allocator, pName);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)
{
VMA_ASSERT(allocator && allocation && pFlags);
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
*pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
VmaAllocator allocator,
VmaAllocation allocation,
void** ppData)
{
VMA_ASSERT(allocator && allocation && ppData);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->Map(allocation, ppData);
}
VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
VmaAllocator allocator,
VmaAllocation allocation)
{
VMA_ASSERT(allocator && allocation);
VMA_DEBUG_GLOBAL_MUTEX_LOCK
allocator->Unmap(allocation);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize offset,
VkDeviceSize size)
{
VMA_ASSERT(allocator && allocation);
VMA_DEBUG_LOG("vmaFlushAllocation");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize offset,
VkDeviceSize size)
{
VMA_ASSERT(allocator && allocation);
VMA_DEBUG_LOG("vmaInvalidateAllocation");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
VmaAllocator allocator,
uint32_t allocationCount,
const VmaAllocation* allocations,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes)
{
VMA_ASSERT(allocator);
if(allocationCount == 0)
{
return VK_SUCCESS;
}
VMA_ASSERT(allocations);
VMA_DEBUG_LOG("vmaFlushAllocations");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
VmaAllocator allocator,
uint32_t allocationCount,
const VmaAllocation* allocations,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes)
{
VMA_ASSERT(allocator);
if(allocationCount == 0)
{
return VK_SUCCESS;
}
VMA_ASSERT(allocations);
VMA_DEBUG_LOG("vmaInvalidateAllocations");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
VmaAllocator allocator,
uint32_t memoryTypeBits)
{
VMA_ASSERT(allocator);
VMA_DEBUG_LOG("vmaCheckCorruption");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->CheckCorruption(memoryTypeBits);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
VmaAllocator allocator,
const VmaDefragmentationInfo* pInfo,
VmaDefragmentationContext* pContext)
{
VMA_ASSERT(allocator && pInfo && pContext);
VMA_DEBUG_LOG("vmaBeginDefragmentation");
if (pInfo->pool != VMA_NULL)
{
if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
return VK_ERROR_FEATURE_NOT_PRESENT;
}
VMA_DEBUG_GLOBAL_MUTEX_LOCK
*pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);
return VK_SUCCESS;
}
VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
VmaAllocator allocator,
VmaDefragmentationContext context,
VmaDefragmentationStats* pStats)
{
VMA_ASSERT(allocator && context);
VMA_DEBUG_LOG("vmaEndDefragmentation");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
if (pStats)
context->GetStats(*pStats);
vma_delete(allocator, context);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
VmaAllocator VMA_NOT_NULL allocator,
VmaDefragmentationContext VMA_NOT_NULL context,
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
{
VMA_ASSERT(context && pPassInfo);
VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return context->DefragmentPassBegin(*pPassInfo);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
VmaAllocator VMA_NOT_NULL allocator,
VmaDefragmentationContext VMA_NOT_NULL context,
VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
{
VMA_ASSERT(context && pPassInfo);
VMA_DEBUG_LOG("vmaEndDefragmentationPass");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return context->DefragmentPassEnd(*pPassInfo);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
VmaAllocator allocator,
VmaAllocation allocation,
VkBuffer buffer)
{
VMA_ASSERT(allocator && allocation && buffer);
VMA_DEBUG_LOG("vmaBindBufferMemory");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize allocationLocalOffset,
VkBuffer buffer,
const void* pNext)
{
VMA_ASSERT(allocator && allocation && buffer);
VMA_DEBUG_LOG("vmaBindBufferMemory2");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
VmaAllocator allocator,
VmaAllocation allocation,
VkImage image)
{
VMA_ASSERT(allocator && allocation && image);
VMA_DEBUG_LOG("vmaBindImageMemory");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
VmaAllocator allocator,
VmaAllocation allocation,
VkDeviceSize allocationLocalOffset,
VkImage image,
const void* pNext)
{
VMA_ASSERT(allocator && allocation && image);
VMA_DEBUG_LOG("vmaBindImageMemory2");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
VmaAllocator allocator,
const VkBufferCreateInfo* pBufferCreateInfo,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
VkBuffer* pBuffer,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
if(pBufferCreateInfo->size == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
!allocator->m_UseKhrBufferDeviceAddress)
{
VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
return VK_ERROR_INITIALIZATION_FAILED;
}
VMA_DEBUG_LOG("vmaCreateBuffer");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
*pBuffer = VK_NULL_HANDLE;
*pAllocation = VK_NULL_HANDLE;
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
allocator->m_hDevice,
pBufferCreateInfo,
allocator->GetAllocationCallbacks(),
pBuffer);
if(res >= 0)
{
VkMemoryRequirements vkMemReq = {};
bool requiresDedicatedAllocation = false;
bool prefersDedicatedAllocation = false;
allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
requiresDedicatedAllocation, prefersDedicatedAllocation);
res = allocator->AllocateMemory(
vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation,
*pBuffer,
VK_NULL_HANDLE,
pBufferCreateInfo->usage,
*pAllocationCreateInfo,
VMA_SUBALLOCATION_TYPE_BUFFER,
1,
pAllocation);
if(res >= 0)
{
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{
res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
}
if(res >= 0)
{
#if VMA_STATS_STRING_ENABLED
(*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
#endif
if(pAllocationInfo != VMA_NULL)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return VK_SUCCESS;
}
allocator->FreeMemory(
1,
pAllocation);
*pAllocation = VK_NULL_HANDLE;
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
*pBuffer = VK_NULL_HANDLE;
return res;
}
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
*pBuffer = VK_NULL_HANDLE;
return res;
}
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
VmaAllocator allocator,
const VkBufferCreateInfo* pBufferCreateInfo,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
VkDeviceSize minAlignment,
VkBuffer* pBuffer,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
if(pBufferCreateInfo->size == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
!allocator->m_UseKhrBufferDeviceAddress)
{
VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
return VK_ERROR_INITIALIZATION_FAILED;
}
VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
*pBuffer = VK_NULL_HANDLE;
*pAllocation = VK_NULL_HANDLE;
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
allocator->m_hDevice,
pBufferCreateInfo,
allocator->GetAllocationCallbacks(),
pBuffer);
if(res >= 0)
{
VkMemoryRequirements vkMemReq = {};
bool requiresDedicatedAllocation = false;
bool prefersDedicatedAllocation = false;
allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
requiresDedicatedAllocation, prefersDedicatedAllocation);
vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
res = allocator->AllocateMemory(
vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation,
*pBuffer,
VK_NULL_HANDLE,
pBufferCreateInfo->usage,
*pAllocationCreateInfo,
VMA_SUBALLOCATION_TYPE_BUFFER,
1,
pAllocation);
if(res >= 0)
{
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{
res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
}
if(res >= 0)
{
#if VMA_STATS_STRING_ENABLED
(*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
#endif
if(pAllocationInfo != VMA_NULL)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return VK_SUCCESS;
}
allocator->FreeMemory(
1,
pAllocation);
*pAllocation = VK_NULL_HANDLE;
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
*pBuffer = VK_NULL_HANDLE;
return res;
}
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
*pBuffer = VK_NULL_HANDLE;
return res;
}
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
{
return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
VkDeviceSize allocationLocalOffset,
const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
{
VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);
VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize());
VMA_DEBUG_LOG("vmaCreateAliasingBuffer2");
*pBuffer = VK_NULL_HANDLE;
if (pBufferCreateInfo->size == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
!allocator->m_UseKhrBufferDeviceAddress)
{
VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
return VK_ERROR_INITIALIZATION_FAILED;
}
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
allocator->m_hDevice,
pBufferCreateInfo,
allocator->GetAllocationCallbacks(),
pBuffer);
if (res >= 0)
{
res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL);
if (res >= 0)
{
return VK_SUCCESS;
}
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
}
return res;
}
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
VmaAllocator allocator,
VkBuffer buffer,
VmaAllocation allocation)
{
VMA_ASSERT(allocator);
if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
{
return;
}
VMA_DEBUG_LOG("vmaDestroyBuffer");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
if(buffer != VK_NULL_HANDLE)
{
(*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
}
if(allocation != VK_NULL_HANDLE)
{
allocator->FreeMemory(
1,
&allocation);
}
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
VmaAllocator allocator,
const VkImageCreateInfo* pImageCreateInfo,
const VmaAllocationCreateInfo* pAllocationCreateInfo,
VkImage* pImage,
VmaAllocation* pAllocation,
VmaAllocationInfo* pAllocationInfo)
{
VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
if(pImageCreateInfo->extent.width == 0 ||
pImageCreateInfo->extent.height == 0 ||
pImageCreateInfo->extent.depth == 0 ||
pImageCreateInfo->mipLevels == 0 ||
pImageCreateInfo->arrayLayers == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
VMA_DEBUG_LOG("vmaCreateImage");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
*pImage = VK_NULL_HANDLE;
*pAllocation = VK_NULL_HANDLE;
VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
allocator->m_hDevice,
pImageCreateInfo,
allocator->GetAllocationCallbacks(),
pImage);
if(res >= 0)
{
VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
VkMemoryRequirements vkMemReq = {};
bool requiresDedicatedAllocation = false;
bool prefersDedicatedAllocation = false;
allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
requiresDedicatedAllocation, prefersDedicatedAllocation);
res = allocator->AllocateMemory(
vkMemReq,
requiresDedicatedAllocation,
prefersDedicatedAllocation,
VK_NULL_HANDLE,
*pImage,
pImageCreateInfo->usage,
*pAllocationCreateInfo,
suballocType,
1,
pAllocation);
if(res >= 0)
{
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
{
res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
}
if(res >= 0)
{
#if VMA_STATS_STRING_ENABLED
(*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
#endif
if(pAllocationInfo != VMA_NULL)
{
allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
}
return VK_SUCCESS;
}
allocator->FreeMemory(
1,
pAllocation);
*pAllocation = VK_NULL_HANDLE;
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
*pImage = VK_NULL_HANDLE;
return res;
}
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
*pImage = VK_NULL_HANDLE;
return res;
}
return res;
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
{
return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL allocation,
VkDeviceSize allocationLocalOffset,
const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
{
VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);
*pImage = VK_NULL_HANDLE;
VMA_DEBUG_LOG("vmaCreateImage2");
if (pImageCreateInfo->extent.width == 0 ||
pImageCreateInfo->extent.height == 0 ||
pImageCreateInfo->extent.depth == 0 ||
pImageCreateInfo->mipLevels == 0 ||
pImageCreateInfo->arrayLayers == 0)
{
return VK_ERROR_INITIALIZATION_FAILED;
}
VMA_DEBUG_GLOBAL_MUTEX_LOCK
VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
allocator->m_hDevice,
pImageCreateInfo,
allocator->GetAllocationCallbacks(),
pImage);
if (res >= 0)
{
res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL);
if (res >= 0)
{
return VK_SUCCESS;
}
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
}
return res;
}
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
VmaAllocator VMA_NOT_NULL allocator,
VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
VmaAllocation VMA_NULLABLE allocation)
{
VMA_ASSERT(allocator);
if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
{
return;
}
VMA_DEBUG_LOG("vmaDestroyImage");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
if(image != VK_NULL_HANDLE)
{
(*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
}
if(allocation != VK_NULL_HANDLE)
{
allocator->FreeMemory(
1,
&allocation);
}
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
{
VMA_ASSERT(pCreateInfo && pVirtualBlock);
VMA_ASSERT(pCreateInfo->size > 0);
VMA_DEBUG_LOG("vmaCreateVirtualBlock");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
*pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
VkResult res = (*pVirtualBlock)->Init();
if(res < 0)
{
vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
*pVirtualBlock = VK_NULL_HANDLE;
}
return res;
}
VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
{
if(virtualBlock != VK_NULL_HANDLE)
{
VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks;
vma_delete(&allocationCallbacks, virtualBlock);
}
}
VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
VkDeviceSize* VMA_NULLABLE pOffset)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);
VMA_DEBUG_LOG("vmaVirtualAllocate");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);
}
VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)
{
if(allocation != VK_NULL_HANDLE)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
VMA_DEBUG_LOG("vmaVirtualFree");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->Free(allocation);
}
}
VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
VMA_DEBUG_LOG("vmaClearVirtualBlock");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->Clear();
}
VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->SetAllocationUserData(allocation, pUserData);
}
VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
VmaStatistics* VMA_NOT_NULL pStats)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->GetStatistics(*pStats);
}
VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
VmaDetailedStatistics* VMA_NOT_NULL pStats)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
virtualBlock->CalculateDetailedStatistics(*pStats);
}
#if VMA_STATS_STRING_ENABLED
VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
VmaStringBuilder sb(allocationCallbacks);
virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
*ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
}
VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
char* VMA_NULLABLE pStatsString)
{
if(pStatsString != VMA_NULL)
{
VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
VMA_DEBUG_GLOBAL_MUTEX_LOCK;
VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
}
}
#endif
#endif
#endif