Added support for dedicated allocations in custom pools

A major refactoring.
JSON format changed!
This commit is contained in:
Adam Sawicki 2021-12-17 11:00:00 +01:00
parent a8c1543723
commit e9c083b4d2
2 changed files with 647 additions and 558 deletions

View file

@ -2908,6 +2908,8 @@ static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x000000
static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000; static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; 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_ALLOCATION_TRY_COUNT = 32;
static const uint32_t VMA_VENDOR_ID_AMD = 4098;
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
// Correspond to values of enum VmaSuballocationType. // Correspond to values of enum VmaSuballocationType.
@ -2961,6 +2963,9 @@ struct VmaMutexLock;
struct VmaMutexLockRead; struct VmaMutexLockRead;
struct VmaMutexLockWrite; struct VmaMutexLockWrite;
template<typename T>
struct AtomicTransactionalIncrement;
template<typename T> template<typename T>
struct VmaStlAllocator; struct VmaStlAllocator;
@ -3630,6 +3635,32 @@ private:
#endif #endif
#endif // _VMA_MUTEX_LOCK #endif // _VMA_MUTEX_LOCK
#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
// An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
template<typename T>
struct AtomicTransactionalIncrement
{
public:
typedef std::atomic<T> AtomicT;
~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 // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
#ifndef _VMA_STL_ALLOCATOR #ifndef _VMA_STL_ALLOCATOR
// STL-compatible allocator. // STL-compatible allocator.
template<typename T> template<typename T>
@ -5508,6 +5539,7 @@ public:
void InitLost(); void InitLost();
// pMappedData not null means allocation is created with MAPPED flag. // pMappedData not null means allocation is created with MAPPED flag.
void InitDedicatedAllocation( void InitDedicatedAllocation(
VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
VmaSuballocationType suballocationType, VmaSuballocationType suballocationType,
@ -5531,6 +5563,7 @@ public:
void ChangeBlockAllocation(VmaAllocator hAllocator, VmaDeviceMemoryBlock* block, VkDeviceSize offset); void ChangeBlockAllocation(VmaAllocator hAllocator, VmaDeviceMemoryBlock* block, VkDeviceSize offset);
void ChangeOffset(VkDeviceSize newOffset); void ChangeOffset(VkDeviceSize newOffset);
VkDeviceSize GetOffset() const; VkDeviceSize GetOffset() const;
VmaPool GetParentPool() const;
VkDeviceMemory GetMemory() const; VkDeviceMemory GetMemory() const;
void* GetMappedData() const; void* GetMappedData() const;
bool CanBecomeLost() const; bool CanBecomeLost() const;
@ -5570,6 +5603,7 @@ private:
// Allocation for an object that has its own private VkDeviceMemory. // Allocation for an object that has its own private VkDeviceMemory.
struct DedicatedAllocation struct DedicatedAllocation
{ {
VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
VkDeviceMemory m_hMemory; VkDeviceMemory m_hMemory;
void* m_pMappedData; // Not null means memory is mapped. void* m_pMappedData; // Not null means memory is mapped.
VmaAllocation_T* m_Prev; VmaAllocation_T* m_Prev;
@ -5644,7 +5678,7 @@ public:
void Init(bool useMutex) { m_UseMutex = useMutex; } void Init(bool useMutex) { m_UseMutex = useMutex; }
void CalculateStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex); void AddStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex);
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
// Writes JSON array with the list of allocations. // Writes JSON array with the list of allocations.
void BuildStatsString(VmaJsonWriter& json); void BuildStatsString(VmaJsonWriter& json);
@ -5672,9 +5706,9 @@ VmaDedicatedAllocationList::~VmaDedicatedAllocationList()
} }
} }
void VmaDedicatedAllocationList::CalculateStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex) void VmaDedicatedAllocationList::AddStats(VmaStats* stats, uint32_t memTypeIndex, uint32_t memHeapIndex)
{ {
VmaMutexLockWrite(m_Mutex, m_UseMutex); VmaMutexLockRead(m_Mutex, m_UseMutex);
for (VmaAllocation alloc = m_AllocationList.Front(); for (VmaAllocation alloc = m_AllocationList.Front();
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
{ {
@ -5689,7 +5723,7 @@ void VmaDedicatedAllocationList::CalculateStats(VmaStats* stats, uint32_t memTyp
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json) void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
{ {
VmaMutexLockWrite(m_Mutex, m_UseMutex); VmaMutexLockRead(m_Mutex, m_UseMutex);
json.BeginArray(); json.BeginArray();
for (VmaAllocation alloc = m_AllocationList.Front(); for (VmaAllocation alloc = m_AllocationList.Front();
alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
@ -5704,7 +5738,7 @@ void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
bool VmaDedicatedAllocationList::IsEmpty() bool VmaDedicatedAllocationList::IsEmpty()
{ {
VmaMutexLockWrite(m_Mutex, m_UseMutex); VmaMutexLockRead(m_Mutex, m_UseMutex);
return m_AllocationList.IsEmpty(); return m_AllocationList.IsEmpty();
} }
@ -9829,6 +9863,9 @@ public:
VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; } uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
uint32_t GetAlgorithm() const { return m_Algorithm; } uint32_t GetAlgorithm() const { return m_Algorithm; }
bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }
float GetPriority() const { return m_Priority; }
void* const GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }
VkResult CreateMinBlocks(); VkResult CreateMinBlocks();
void GetPoolStats(VmaPoolStats* pStats); void GetPoolStats(VmaPoolStats* pStats);
@ -10274,7 +10311,7 @@ struct VmaPool_T
VMA_CLASS_NO_COPY(VmaPool_T) VMA_CLASS_NO_COPY(VmaPool_T)
public: public:
VmaBlockVector m_BlockVector; VmaBlockVector m_BlockVector;
//VmaDedicatedAllocationList m_DedicatedAllocations; VmaDedicatedAllocationList m_DedicatedAllocations;
VmaPool_T( VmaPool_T(
VmaAllocator hAllocator, VmaAllocator hAllocator,
@ -10916,20 +10953,24 @@ private:
VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
VkResult AllocateMemoryOfType( VkResult AllocateMemoryOfType(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VkDeviceSize alignment, VkDeviceSize alignment,
bool dedicatedAllocation, bool dedicatedPreferred,
VkBuffer dedicatedBuffer, VkBuffer dedicatedBuffer,
VkBufferUsageFlags dedicatedBufferUsage, VkBufferUsageFlags dedicatedBufferUsage,
VkImage dedicatedImage, VkImage dedicatedImage,
const VmaAllocationCreateInfo& createInfo, const VmaAllocationCreateInfo& createInfo,
uint32_t memTypeIndex, uint32_t memTypeIndex,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
VmaBlockVector& blockVector,
size_t allocationCount, size_t allocationCount,
VmaAllocation* pAllocations); VmaAllocation* pAllocations);
// Helper function only to be used inside AllocateDedicatedMemory. // Helper function only to be used inside AllocateDedicatedMemory.
VkResult AllocateDedicatedMemoryPage( VkResult AllocateDedicatedMemoryPage(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
uint32_t memTypeIndex, uint32_t memTypeIndex,
@ -10941,10 +10982,11 @@ private:
// Allocates and registers new VkDeviceMemory specifically for dedicated allocations. // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
VkResult AllocateDedicatedMemory( VkResult AllocateDedicatedMemory(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
uint32_t memTypeIndex, uint32_t memTypeIndex,
bool withinBudget,
bool map, bool map,
bool isUserDataString, bool isUserDataString,
bool canAliasMemory, bool canAliasMemory,
@ -10954,16 +10996,26 @@ private:
VkBufferUsageFlags dedicatedBufferUsage, VkBufferUsageFlags dedicatedBufferUsage,
VkImage dedicatedImage, VkImage dedicatedImage,
size_t allocationCount, size_t allocationCount,
VmaAllocation* pAllocations); VmaAllocation* pAllocations,
const void* pNextChain = nullptr);
void FreeDedicatedMemory(const VmaAllocation allocation); 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);
/* /*
Calculates and returns bit mask of memory types that can support defragmentation Calculates and returns bit mask of memory types that can support defragmentation
on GPU as they support creation of required buffer for copy operations. on GPU as they support creation of required buffer for copy operations.
*/ */
uint32_t CalculateGpuDefragmentationMemoryTypeBits() const; uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
uint32_t CalculateGlobalMemoryTypeBits() const; uint32_t CalculateGlobalMemoryTypeBits() const;
bool GetFlushOrInvalidateRange( bool GetFlushOrInvalidateRange(
@ -11313,6 +11365,7 @@ void VmaAllocation_T::InitLost()
} }
void VmaAllocation_T::InitDedicatedAllocation( void VmaAllocation_T::InitDedicatedAllocation(
VmaPool hParentPool,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
VkDeviceMemory hMemory, VkDeviceMemory hMemory,
VmaSuballocationType suballocationType, VmaSuballocationType suballocationType,
@ -11327,6 +11380,7 @@ void VmaAllocation_T::InitDedicatedAllocation(
m_MemoryTypeIndex = memoryTypeIndex; m_MemoryTypeIndex = memoryTypeIndex;
m_SuballocationType = (uint8_t)suballocationType; m_SuballocationType = (uint8_t)suballocationType;
m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0; m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
m_DedicatedAllocation.m_hParentPool = hParentPool;
m_DedicatedAllocation.m_hMemory = hMemory; m_DedicatedAllocation.m_hMemory = hMemory;
m_DedicatedAllocation.m_pMappedData = pMappedData; m_DedicatedAllocation.m_pMappedData = pMappedData;
m_DedicatedAllocation.m_Prev = VMA_NULL; m_DedicatedAllocation.m_Prev = VMA_NULL;
@ -11394,6 +11448,20 @@ VkDeviceSize VmaAllocation_T::GetOffset() const
} }
} }
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 VkDeviceMemory VmaAllocation_T::GetMemory() const
{ {
switch (m_Type) switch (m_Type)
@ -12648,8 +12716,6 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
{ {
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
json.BeginObject();
if (IsCustomPool()) if (IsCustomPool())
{ {
const char* poolName = m_hParentPool->GetName(); const char* poolName = m_hParentPool->GetName();
@ -12710,8 +12776,6 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
} }
json.EndObject(); json.EndObject();
json.EndObject();
} }
#endif // VMA_STATS_STRING_ENABLED #endif // VMA_STATS_STRING_ENABLED
@ -15353,15 +15417,18 @@ VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
} }
VkResult VmaAllocator_T::AllocateMemoryOfType( VkResult VmaAllocator_T::AllocateMemoryOfType(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VkDeviceSize alignment, VkDeviceSize alignment,
bool dedicatedAllocation, bool dedicatedPreferred,
VkBuffer dedicatedBuffer, VkBuffer dedicatedBuffer,
VkBufferUsageFlags dedicatedBufferUsage, VkBufferUsageFlags dedicatedBufferUsage,
VkImage dedicatedImage, VkImage dedicatedImage,
const VmaAllocationCreateInfo& createInfo, const VmaAllocationCreateInfo& createInfo,
uint32_t memTypeIndex, uint32_t memTypeIndex,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
VmaBlockVector& blockVector,
size_t allocationCount, size_t allocationCount,
VmaAllocation* pAllocations) VmaAllocation* pAllocations)
{ {
@ -15369,95 +15436,22 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size); VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
VmaAllocationCreateInfo finalCreateInfo = createInfo; VmaAllocationCreateInfo finalCreateInfo = createInfo;
VkResult res = CalcMemTypeParams(
// If memory type is not HOST_VISIBLE, disable MAPPED. finalCreateInfo,
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && memTypeIndex,
(m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) size,
{ allocationCount);
finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; if(res != VK_SUCCESS)
} return res;
// If memory is lazily allocated, it should be always dedicated.
if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
{
finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
}
VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
bool preferDedicatedMemory =
VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
dedicatedAllocation ||
// Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
size > preferredBlockSize / 2;
if(preferDedicatedMemory &&
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
finalCreateInfo.pool == VK_NULL_HANDLE)
{
finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
}
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
{ {
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) return AllocateDedicatedMemory(
{ pool,
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
else
{
return AllocateDedicatedMemory(
size,
suballocType,
memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedBufferUsage,
dedicatedImage,
allocationCount,
pAllocations);
}
}
else
{
VkResult res = blockVector->Allocate(
m_CurrentFrameIndex.load(),
size,
alignment,
finalCreateInfo,
suballocType,
allocationCount,
pAllocations);
if(res == VK_SUCCESS)
{
return res;
}
// 5. Try dedicated memory.
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
// Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
// which can quickly deplete maxMemoryAllocationCount: Don't try dedicated allocations when above
// 3/4 of the maximum allocation count.
if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
{
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
res = AllocateDedicatedMemory(
size, size,
suballocType, suballocType,
dedicatedAllocations,
memTypeIndex, memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
@ -15467,27 +15461,108 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
dedicatedBufferUsage, dedicatedBufferUsage,
dedicatedImage, dedicatedImage,
allocationCount, 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)
{
// Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
if(size > blockVector.GetPreferredBlockSize() / 2)
{
dedicatedPreferred = true;
}
// Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
// which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above
// 3/4 of the maximum allocation count.
if(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_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedBufferUsage,
dedicatedImage,
allocationCount,
pAllocations,
blockVector.GetAllocationNextPtr());
if(res == VK_SUCCESS)
{
// Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
return VK_SUCCESS;
}
}
}
res = blockVector.Allocate(
m_CurrentFrameIndex.load(),
size,
alignment,
finalCreateInfo,
suballocType,
allocationCount,
pAllocations); pAllocations);
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
{
// Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
return VK_SUCCESS; return VK_SUCCESS;
}
else // Try dedicated memory.
if(canAllocateDedicated && !dedicatedPreferred)
{ {
// Everything failed: Return error code. res = AllocateDedicatedMemory(
VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); pool,
return res; 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_CAN_ALIAS_BIT) != 0,
finalCreateInfo.pUserData,
finalCreateInfo.priority,
dedicatedBuffer,
dedicatedBufferUsage,
dedicatedImage,
allocationCount,
pAllocations,
blockVector.GetAllocationNextPtr());
if(res == VK_SUCCESS)
{
// Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
return VK_SUCCESS;
}
} }
// Everything failed: Return error code.
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
return res;
} }
} }
VkResult VmaAllocator_T::AllocateDedicatedMemory( VkResult VmaAllocator_T::AllocateDedicatedMemory(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
VmaDedicatedAllocationList& dedicatedAllocations,
uint32_t memTypeIndex, uint32_t memTypeIndex,
bool withinBudget,
bool map, bool map,
bool isUserDataString, bool isUserDataString,
bool canAliasMemory, bool canAliasMemory,
@ -15497,24 +15572,15 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
VkBufferUsageFlags dedicatedBufferUsage, VkBufferUsageFlags dedicatedBufferUsage,
VkImage dedicatedImage, VkImage dedicatedImage,
size_t allocationCount, size_t allocationCount,
VmaAllocation* pAllocations) VmaAllocation* pAllocations,
const void* pNextChain)
{ {
VMA_ASSERT(allocationCount > 0 && pAllocations); VMA_ASSERT(allocationCount > 0 && pAllocations);
if(withinBudget)
{
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;
}
}
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
allocInfo.memoryTypeIndex = memTypeIndex; allocInfo.memoryTypeIndex = memTypeIndex;
allocInfo.allocationSize = size; allocInfo.allocationSize = size;
allocInfo.pNext = pNextChain;
#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
@ -15583,6 +15649,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{ {
res = AllocateDedicatedMemoryPage( res = AllocateDedicatedMemoryPage(
pool,
size, size,
suballocType, suballocType,
memTypeIndex, memTypeIndex,
@ -15599,15 +15666,10 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
{ {
// Register them in m_DedicatedAllocations. for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{ {
VmaDedicatedAllocationList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex]; dedicatedAllocations.Register(pAllocations[allocIndex]);
for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
{
dedicatedAllocations.Register(pAllocations[allocIndex]);
}
} }
VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex); VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
} }
else else
@ -15641,6 +15703,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
} }
VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
VmaPool pool,
VkDeviceSize size, VkDeviceSize size,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
uint32_t memTypeIndex, uint32_t memTypeIndex,
@ -15677,7 +15740,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
} }
*pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString); *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
(*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size); (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
(*pAllocation)->SetUserData(this, pUserData); (*pAllocation)->SetUserData(this, pUserData);
m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size); m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
@ -15752,6 +15815,77 @@ void VmaAllocator_T::GetImageMemoryRequirements(
} }
} }
VkResult VmaAllocator_T::CalcMemTypeParams(
VmaAllocationCreateInfo& inoutCreateInfo,
uint32_t memTypeIndex,
VkDeviceSize size,
size_t allocationCount)
{
// If memory type is not HOST_VISIBLE, disable MAPPED.
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)
{
if(dedicatedRequired ||
// If memory is lazily allocated, it should be always dedicated.
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((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
(inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
{
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
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;
}
return VK_SUCCESS;
}
VkResult VmaAllocator_T::AllocateMemory( VkResult VmaAllocator_T::AllocateMemory(
const VkMemoryRequirements& vkMemReq, const VkMemoryRequirements& vkMemReq,
bool requiresDedicatedAllocation, bool requiresDedicatedAllocation,
@ -15772,54 +15906,28 @@ VkResult VmaAllocator_T::AllocateMemory(
{ {
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }
if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
(createInfo.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_OUT_OF_DEVICE_MEMORY;
}
if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
{
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
if(requiresDedicatedAllocation)
{
if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
{
VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
if(createInfo.pool != VK_NULL_HANDLE)
{
VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
}
if((createInfo.pool != VK_NULL_HANDLE) &&
((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
{
VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
if(createInfo.pool != VK_NULL_HANDLE) VmaAllocationCreateInfo createInfoFinal = createInfo;
{ VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);
VmaAllocationCreateInfo createInfoForPool = createInfo; if(res != VK_SUCCESS)
// If memory type is not HOST_VISIBLE, disable MAPPED. return res;
if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
(m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
}
return createInfo.pool->m_BlockVector.Allocate( if(createInfoFinal.pool != VK_NULL_HANDLE)
m_CurrentFrameIndex.load(), {
VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
return AllocateMemoryOfType(
createInfoFinal.pool,
vkMemReq.size, vkMemReq.size,
vkMemReq.alignment, vkMemReq.alignment,
createInfoForPool, prefersDedicatedAllocation,
dedicatedBuffer,
dedicatedBufferUsage,
dedicatedImage,
createInfoFinal,
blockVector.GetMemoryTypeIndex(),
suballocType, suballocType,
createInfoFinal.pool->m_DedicatedAllocations,
blockVector,
allocationCount, allocationCount,
pAllocations); pAllocations);
} }
@ -15828,68 +15936,42 @@ VkResult VmaAllocator_T::AllocateMemory(
// Bit mask of memory Vulkan types acceptable for this allocation. // Bit mask of memory Vulkan types acceptable for this allocation.
uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
uint32_t memTypeIndex = UINT32_MAX; uint32_t memTypeIndex = UINT32_MAX;
VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex); res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
if(res == VK_SUCCESS) // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
if(res != VK_SUCCESS)
return res;
do
{ {
VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
res = AllocateMemoryOfType( res = AllocateMemoryOfType(
VK_NULL_HANDLE,
vkMemReq.size, vkMemReq.size,
vkMemReq.alignment, vkMemReq.alignment,
requiresDedicatedAllocation || prefersDedicatedAllocation, requiresDedicatedAllocation || prefersDedicatedAllocation,
dedicatedBuffer, dedicatedBuffer,
dedicatedBufferUsage, dedicatedBufferUsage,
dedicatedImage, dedicatedImage,
createInfo, createInfoFinal,
memTypeIndex, memTypeIndex,
suballocType, suballocType,
m_DedicatedAllocations[memTypeIndex],
*blockVector,
allocationCount, allocationCount,
pAllocations); pAllocations);
// Succeeded on first try. // Allocation succeeded
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
{ return VK_SUCCESS;
return res;
} // Remove old memTypeIndex from list of possibilities.
// Allocation from this memory type failed. Try other compatible memory types. memoryTypeBits &= ~(1u << memTypeIndex);
else // Find alternative memTypeIndex.
{ res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
for(;;) } while(res == VK_SUCCESS);
{
// Remove old memTypeIndex from list of possibilities. // No other matching memory type index could be found.
memoryTypeBits &= ~(1u << memTypeIndex); // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
// Find alternative memTypeIndex. return VK_ERROR_OUT_OF_DEVICE_MEMORY;
res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
if(res == VK_SUCCESS)
{
res = AllocateMemoryOfType(
vkMemReq.size,
vkMemReq.alignment,
requiresDedicatedAllocation || prefersDedicatedAllocation,
dedicatedBuffer,
dedicatedBufferUsage,
dedicatedImage,
createInfo,
memTypeIndex,
suballocType,
allocationCount,
pAllocations);
// Allocation from this alternative memory type succeeded.
if(res == VK_SUCCESS)
{
return res;
}
// else: Allocation from this memory type failed. Try next one - next loop iteration.
}
// No other matching memory type index could be found.
else
{
// Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
}
}
}
// Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
else
return res;
} }
} }
@ -15917,7 +15999,7 @@ void VmaAllocator_T::FreeMemory(
case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
{ {
VmaBlockVector* pBlockVector = VMA_NULL; VmaBlockVector* pBlockVector = VMA_NULL;
VmaPool hPool = allocation->GetBlock()->GetParentPool(); VmaPool hPool = allocation->GetParentPool();
if(hPool != VK_NULL_HANDLE) if(hPool != VK_NULL_HANDLE)
{ {
pBlockVector = &hPool->m_BlockVector; pBlockVector = &hPool->m_BlockVector;
@ -15969,7 +16051,11 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats)
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
{ {
pool->m_BlockVector.AddStats(pStats); VmaBlockVector& blockVector = pool->m_BlockVector;
blockVector.AddStats(pStats);
const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
pool->m_DedicatedAllocations.AddStats(pStats, memTypeIndex, memHeapIndex);
} }
} }
@ -15977,8 +16063,7 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats)
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{ {
const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; m_DedicatedAllocations[memTypeIndex].AddStats(pStats, memTypeIndex, memHeapIndex);
m_DedicatedAllocations->CalculateStats(pStats, memTypeIndex, memHeapIndex);
} }
// Postprocess. // Postprocess.
@ -16041,8 +16126,6 @@ void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, u
} }
} }
static const uint32_t VMA_VENDOR_ID_AMD = 4098;
VkResult VmaAllocator_T::DefragmentationBegin( VkResult VmaAllocator_T::DefragmentationBegin(
const VmaDefragmentationInfo2& info, const VmaDefragmentationInfo2& info,
VmaDefragmentationStats* pStats, VmaDefragmentationStats* pStats,
@ -16087,11 +16170,11 @@ VkResult VmaAllocator_T::DefragmentationPassBegin(
{ {
return context->DefragmentPassBegin(pInfo); return context->DefragmentPassBegin(pInfo);
} }
VkResult VmaAllocator_T::DefragmentationPassEnd( VkResult VmaAllocator_T::DefragmentationPassEnd(
VmaDefragmentationContext context) VmaDefragmentationContext context)
{ {
return context->DefragmentPassEnd(); return context->DefragmentPassEnd();
} }
void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
@ -16368,31 +16451,6 @@ void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
(*pAllocation)->InitLost(); (*pAllocation)->InitLost();
} }
// An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
template<typename T>
struct AtomicTransactionalIncrement
{
public:
typedef std::atomic<T> AtomicT;
~AtomicTransactionalIncrement()
{
if(m_Atomic)
--(*m_Atomic);
}
T Increment(AtomicT* atomic)
{
m_Atomic = atomic;
return m_Atomic->fetch_add(1);
}
void Commit()
{
m_Atomic = nullptr;
}
private:
AtomicT* m_Atomic = nullptr;
};
VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
{ {
AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement; AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
@ -16702,9 +16760,16 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
VmaPool parentPool = allocation->GetParentPool();
if(parentPool == VK_NULL_HANDLE)
{ {
VmaDedicatedAllocationList& dedicatedAllocations = m_DedicatedAllocations[memTypeIndex]; // Default pool
dedicatedAllocations.Unregister(allocation); m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
}
else
{
// Custom pool
parentPool->m_DedicatedAllocations.Unregister(allocation);
} }
VkDeviceMemory hMemory = allocation->GetMemory(); VkDeviceMemory hMemory = allocation->GetMemory();
@ -16954,7 +17019,9 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
json.ContinueString(memTypeIndex); json.ContinueString(memTypeIndex);
json.EndString(); json.EndString();
json.BeginObject();
pBlockVector->PrintDetailedMap(json); pBlockVector->PrintDetailedMap(json);
json.EndObject();
} }
} }
} }
@ -16977,7 +17044,17 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
json.ContinueString(pool->GetId()); json.ContinueString(pool->GetId());
json.EndString(); json.EndString();
json.BeginObject();
pool->m_BlockVector.PrintDetailedMap(json); pool->m_BlockVector.PrintDetailedMap(json);
if (!pool->m_DedicatedAllocations.IsEmpty())
{
json.WriteString("DedicatedAllocations");
json.BeginObject();
pool->m_DedicatedAllocations.BuildStatsString(json);
json.EndObject();
}
json.EndObject();
} }
json.EndObject(); json.EndObject();
} }

View file

@ -1,305 +1,317 @@
# #
# Copyright (c) 2018-2021 Advanced Micro Devices, Inc. All rights reserved. # Copyright (c) 2018-2021 Advanced Micro Devices, Inc. All rights reserved.
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
# #
import argparse import argparse
import json import json
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
PROGRAM_VERSION = 'VMA Dump Visualization 2.0.1' PROGRAM_VERSION = 'VMA Dump Visualization 2.0.1'
IMG_SIZE_X = 1200 IMG_SIZE_X = 1200
IMG_MARGIN = 8 IMG_MARGIN = 8
FONT_SIZE = 10 FONT_SIZE = 10
MAP_SIZE = 24 MAP_SIZE = 24
COLOR_TEXT_H1 = (0, 0, 0, 255) COLOR_TEXT_H1 = (0, 0, 0, 255)
COLOR_TEXT_H2 = (150, 150, 150, 255) COLOR_TEXT_H2 = (150, 150, 150, 255)
COLOR_OUTLINE = (155, 155, 155, 255) COLOR_OUTLINE = (155, 155, 155, 255)
COLOR_OUTLINE_HARD = (0, 0, 0, 255) COLOR_OUTLINE_HARD = (0, 0, 0, 255)
COLOR_GRID_LINE = (224, 224, 224, 255) COLOR_GRID_LINE = (224, 224, 224, 255)
argParser = argparse.ArgumentParser(description='Visualization of Vulkan Memory Allocator JSON dump.') argParser = argparse.ArgumentParser(description='Visualization of Vulkan Memory Allocator JSON dump.')
argParser.add_argument('DumpFile', type=argparse.FileType(mode='r', encoding='UTF-8'), help='Path to source JSON file with memory dump created by Vulkan Memory Allocator library') argParser.add_argument('DumpFile', type=argparse.FileType(mode='r', encoding='UTF-8'), help='Path to source JSON file with memory dump created by Vulkan Memory Allocator library')
argParser.add_argument('-v', '--version', action='version', version=PROGRAM_VERSION) argParser.add_argument('-v', '--version', action='version', version=PROGRAM_VERSION)
argParser.add_argument('-o', '--output', required=True, help='Path to destination image file (e.g. PNG)') argParser.add_argument('-o', '--output', required=True, help='Path to destination image file (e.g. PNG)')
args = argParser.parse_args() args = argParser.parse_args()
data = {} data = {}
def ProcessBlock(dstBlockList, iBlockId, objBlock, sAlgorithm): def ProcessBlock(dstBlockList, iBlockId, objBlock, sAlgorithm):
iBlockSize = int(objBlock['TotalBytes']) iBlockSize = int(objBlock['TotalBytes'])
arrSuballocs = objBlock['Suballocations'] arrSuballocs = objBlock['Suballocations']
dstBlockObj = {'ID': iBlockId, 'Size':iBlockSize, 'Suballocations':[]} dstBlockObj = {'ID': iBlockId, 'Size':iBlockSize, 'Suballocations':[]}
dstBlockObj['Algorithm'] = sAlgorithm dstBlockObj['Algorithm'] = sAlgorithm
for objSuballoc in arrSuballocs: for objSuballoc in arrSuballocs:
dstBlockObj['Suballocations'].append((objSuballoc['Type'], int(objSuballoc['Size']), int(objSuballoc['Usage']) if ('Usage' in objSuballoc) else 0)) dstBlockObj['Suballocations'].append((objSuballoc['Type'], int(objSuballoc['Size']), int(objSuballoc['Usage']) if ('Usage' in objSuballoc) else 0))
dstBlockList.append(dstBlockObj) dstBlockList.append(dstBlockObj)
def GetDataForMemoryType(iMemTypeIndex): def GetDataForMemoryType(iMemTypeIndex):
global data global data
if iMemTypeIndex in data: if iMemTypeIndex in data:
return data[iMemTypeIndex] return data[iMemTypeIndex]
else: else:
newMemTypeData = {'DedicatedAllocations':[], 'DefaultPoolBlocks':[], 'CustomPools':{}} newMemTypeData = {'DedicatedAllocations':[], 'DefaultPoolBlocks':[], 'CustomPools':{}}
data[iMemTypeIndex] = newMemTypeData data[iMemTypeIndex] = newMemTypeData
return newMemTypeData return newMemTypeData
def IsDataEmpty(): def IsDataEmpty():
global data global data
for dictMemType in data.values(): for dictMemType in data.values():
if 'DedicatedAllocations' in dictMemType and len(dictMemType['DedicatedAllocations']) > 0: if 'DedicatedAllocations' in dictMemType and len(dictMemType['DedicatedAllocations']) > 0:
return False return False
if 'DefaultPoolBlocks' in dictMemType and len(dictMemType['DefaultPoolBlocks']) > 0: if 'DefaultPoolBlocks' in dictMemType and len(dictMemType['DefaultPoolBlocks']) > 0:
return False return False
if 'CustomPools' in dictMemType: if 'CustomPools' in dictMemType:
for lBlockList in dictMemType['CustomPools'].values(): for lBlockList in dictMemType['CustomPools'].values():
if len(lBlockList) > 0: if len(lBlockList) > 0:
return False return False
return True return True
# Returns tuple: # Returns tuple:
# [0] image height : integer # [0] image height : integer
# [1] pixels per byte : float # [1] pixels per byte : float
def CalcParams(): def CalcParams():
global data global data
iImgSizeY = IMG_MARGIN iImgSizeY = IMG_MARGIN
iImgSizeY += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes iImgSizeY += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes
iMaxBlockSize = 0 iMaxBlockSize = 0
for dictMemType in data.values(): for dictMemType in data.values():
iImgSizeY += IMG_MARGIN + FONT_SIZE iImgSizeY += IMG_MARGIN + FONT_SIZE
lDedicatedAllocations = dictMemType['DedicatedAllocations'] lDedicatedAllocations = dictMemType['DedicatedAllocations']
iImgSizeY += len(lDedicatedAllocations) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) iImgSizeY += len(lDedicatedAllocations) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
for tDedicatedAlloc in lDedicatedAllocations: for tDedicatedAlloc in lDedicatedAllocations:
iMaxBlockSize = max(iMaxBlockSize, tDedicatedAlloc[1]) iMaxBlockSize = max(iMaxBlockSize, tDedicatedAlloc[1])
lDefaultPoolBlocks = dictMemType['DefaultPoolBlocks'] lDefaultPoolBlocks = dictMemType['DefaultPoolBlocks']
iImgSizeY += len(lDefaultPoolBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) iImgSizeY += len(lDefaultPoolBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
for objBlock in lDefaultPoolBlocks: for objBlock in lDefaultPoolBlocks:
iMaxBlockSize = max(iMaxBlockSize, objBlock['Size']) iMaxBlockSize = max(iMaxBlockSize, objBlock['Size'])
dCustomPools = dictMemType['CustomPools'] dCustomPools = dictMemType['CustomPools']
for lBlocks in dCustomPools.values(): for lBlocks in dCustomPools.values():
iImgSizeY += len(lBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) iImgSizeY += len(lBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
for objBlock in lBlocks: for objBlock in lBlocks:
iMaxBlockSize = max(iMaxBlockSize, objBlock['Size']) iMaxBlockSize = max(iMaxBlockSize, objBlock['Size'])
fPixelsPerByte = (IMG_SIZE_X - IMG_MARGIN * 2) / float(iMaxBlockSize) fPixelsPerByte = (IMG_SIZE_X - IMG_MARGIN * 2) / float(iMaxBlockSize)
return iImgSizeY, fPixelsPerByte return iImgSizeY, fPixelsPerByte
def TypeToColor(sType, iUsage): def TypeToColor(sType, iUsage):
if sType == 'FREE': if sType == 'FREE':
return 220, 220, 220, 255 return 220, 220, 220, 255
elif sType == 'BUFFER': elif sType == 'BUFFER':
if (iUsage & 0x1C0) != 0: # INDIRECT_BUFFER | VERTEX_BUFFER | INDEX_BUFFER if (iUsage & 0x1C0) != 0: # INDIRECT_BUFFER | VERTEX_BUFFER | INDEX_BUFFER
return 255, 148, 148, 255 # Red return 255, 148, 148, 255 # Red
elif (iUsage & 0x28) != 0: # STORAGE_BUFFER | STORAGE_TEXEL_BUFFER elif (iUsage & 0x28) != 0: # STORAGE_BUFFER | STORAGE_TEXEL_BUFFER
return 255, 187, 121, 255 # Orange return 255, 187, 121, 255 # Orange
elif (iUsage & 0x14) != 0: # UNIFORM_BUFFER | UNIFORM_TEXEL_BUFFER elif (iUsage & 0x14) != 0: # UNIFORM_BUFFER | UNIFORM_TEXEL_BUFFER
return 255, 255, 0, 255 # Yellow return 255, 255, 0, 255 # Yellow
else: else:
return 255, 255, 165, 255 # Light yellow return 255, 255, 165, 255 # Light yellow
elif sType == 'IMAGE_OPTIMAL': elif sType == 'IMAGE_OPTIMAL':
if (iUsage & 0x20) != 0: # DEPTH_STENCIL_ATTACHMENT if (iUsage & 0x20) != 0: # DEPTH_STENCIL_ATTACHMENT
return 246, 128, 255, 255 # Pink return 246, 128, 255, 255 # Pink
elif (iUsage & 0xD0) != 0: # INPUT_ATTACHMENT | TRANSIENT_ATTACHMENT | COLOR_ATTACHMENT elif (iUsage & 0xD0) != 0: # INPUT_ATTACHMENT | TRANSIENT_ATTACHMENT | COLOR_ATTACHMENT
return 179, 179, 255, 255 # Blue return 179, 179, 255, 255 # Blue
elif (iUsage & 0x4) != 0: # SAMPLED elif (iUsage & 0x4) != 0: # SAMPLED
return 0, 255, 255, 255 # Aqua return 0, 255, 255, 255 # Aqua
else: else:
return 183, 255, 255, 255 # Light aqua return 183, 255, 255, 255 # Light aqua
elif sType == 'IMAGE_LINEAR': elif sType == 'IMAGE_LINEAR':
return 0, 255, 0, 255 # Green return 0, 255, 0, 255 # Green
elif sType == 'IMAGE_UNKNOWN': elif sType == 'IMAGE_UNKNOWN':
return 0, 255, 164, 255 # Green/aqua return 0, 255, 164, 255 # Green/aqua
elif sType == 'UNKNOWN': elif sType == 'UNKNOWN':
return 175, 175, 175, 255 # Gray return 175, 175, 175, 255 # Gray
assert False assert False
return 0, 0, 0, 255 return 0, 0, 0, 255
def DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc): def DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc):
global fPixelsPerByte global fPixelsPerByte
iSizeBytes = tDedicatedAlloc[1] iSizeBytes = tDedicatedAlloc[1]
iSizePixels = int(iSizeBytes * fPixelsPerByte) iSizePixels = int(iSizeBytes * fPixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor(tDedicatedAlloc[0], tDedicatedAlloc[2]), outline=COLOR_OUTLINE) draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor(tDedicatedAlloc[0], tDedicatedAlloc[2]), outline=COLOR_OUTLINE)
def DrawBlock(draw, y, objBlock): def DrawBlock(draw, y, objBlock):
global fPixelsPerByte global fPixelsPerByte
iSizeBytes = objBlock['Size'] iSizeBytes = objBlock['Size']
iSizePixels = int(iSizeBytes * fPixelsPerByte) iSizePixels = int(iSizeBytes * fPixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor('FREE', 0), outline=None) draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor('FREE', 0), outline=None)
iByte = 0 iByte = 0
iX = 0 iX = 0
iLastHardLineX = -1 iLastHardLineX = -1
for tSuballoc in objBlock['Suballocations']: for tSuballoc in objBlock['Suballocations']:
sType = tSuballoc[0] sType = tSuballoc[0]
iByteEnd = iByte + tSuballoc[1] iByteEnd = iByte + tSuballoc[1]
iXEnd = int(iByteEnd * fPixelsPerByte) iXEnd = int(iByteEnd * fPixelsPerByte)
if sType != 'FREE': if sType != 'FREE':
if iXEnd > iX + 1: if iXEnd > iX + 1:
iUsage = tSuballoc[2] iUsage = tSuballoc[2]
draw.rectangle([IMG_MARGIN + iX, y, IMG_MARGIN + iXEnd, y + MAP_SIZE], fill=TypeToColor(sType, iUsage), outline=COLOR_OUTLINE) draw.rectangle([IMG_MARGIN + iX, y, IMG_MARGIN + iXEnd, y + MAP_SIZE], fill=TypeToColor(sType, iUsage), outline=COLOR_OUTLINE)
# Hard line was been overwritten by rectangle outline: redraw it. # Hard line was been overwritten by rectangle outline: redraw it.
if iLastHardLineX == iX: if iLastHardLineX == iX:
draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD) draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
else: else:
draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD) draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
iLastHardLineX = iX iLastHardLineX = iX
iByte = iByteEnd iByte = iByteEnd
iX = iXEnd iX = iXEnd
def BytesToStr(iBytes): def BytesToStr(iBytes):
if iBytes < 1024: if iBytes < 1024:
return "%d B" % iBytes return "%d B" % iBytes
iBytes /= 1024 iBytes /= 1024
if iBytes < 1024: if iBytes < 1024:
return "%d KiB" % iBytes return "%d KiB" % iBytes
iBytes /= 1024 iBytes /= 1024
if iBytes < 1024: if iBytes < 1024:
return "%d MiB" % iBytes return "%d MiB" % iBytes
iBytes /= 1024 iBytes /= 1024
return "%d GiB" % iBytes return "%d GiB" % iBytes
jsonSrc = json.load(args.DumpFile) jsonSrc = json.load(args.DumpFile)
if 'DedicatedAllocations' in jsonSrc: if 'DedicatedAllocations' in jsonSrc:
for tType in jsonSrc['DedicatedAllocations'].items(): for tType in jsonSrc['DedicatedAllocations'].items():
sType = tType[0] sType = tType[0]
assert sType[:5] == 'Type ' assert sType[:5] == 'Type '
iType = int(sType[5:]) iType = int(sType[5:])
typeData = GetDataForMemoryType(iType) typeData = GetDataForMemoryType(iType)
for objAlloc in tType[1]: for objAlloc in tType[1]:
typeData['DedicatedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc['Usage']) if ('Usage' in objAlloc) else 0)) typeData['DedicatedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc['Usage']) if ('Usage' in objAlloc) else 0))
if 'DefaultPools' in jsonSrc: if 'DefaultPools' in jsonSrc:
for tType in jsonSrc['DefaultPools'].items(): for tType in jsonSrc['DefaultPools'].items():
sType = tType[0] sType = tType[0]
assert sType[:5] == 'Type ' assert sType[:5] == 'Type '
iType = int(sType[5:]) iType = int(sType[5:])
typeData = GetDataForMemoryType(iType) typeData = GetDataForMemoryType(iType)
for sBlockId, objBlock in tType[1]['Blocks'].items(): for sBlockId, objBlock in tType[1]['Blocks'].items():
ProcessBlock(typeData['DefaultPoolBlocks'], int(sBlockId), objBlock, '') ProcessBlock(typeData['DefaultPoolBlocks'], int(sBlockId), objBlock, '')
if 'Pools' in jsonSrc: if 'Pools' in jsonSrc:
objPools = jsonSrc['Pools'] objPools = jsonSrc['Pools']
for sPoolId, objPool in objPools.items(): for sPoolId, objPool in objPools.items():
iType = int(objPool['MemoryTypeIndex']) iType = int(objPool['MemoryTypeIndex'])
typeData = GetDataForMemoryType(iType) typeData = GetDataForMemoryType(iType)
objBlocks = objPool['Blocks'] objBlocks = objPool['Blocks']
sAlgorithm = objPool.get('Algorithm', '') sAlgorithm = objPool.get('Algorithm', '')
sName = objPool.get('Name', None) sName = objPool.get('Name', None)
if sName: if sName:
sFullName = sPoolId + ' "' + sName + '"' sFullName = sPoolId + ' "' + sName + '"'
else: else:
sFullName = sPoolId sFullName = sPoolId
dstBlockArray = [] dstBlockArray = []
typeData['CustomPools'][sFullName] = dstBlockArray typeData['CustomPools'][sFullName] = dstBlockArray
for sBlockId, objBlock in objBlocks.items(): for sBlockId, objBlock in objBlocks.items():
ProcessBlock(dstBlockArray, int(sBlockId), objBlock, sAlgorithm) ProcessBlock(dstBlockArray, int(sBlockId), objBlock, sAlgorithm)
if 'DedicatedAllocations' in objPool:
if IsDataEmpty(): for tType in objPool['DedicatedAllocations'].items():
print("There is nothing to put on the image. Please make sure you generated the stats string with detailed map enabled.") sType = tType[0]
exit(1) assert sType[:5] == 'Type '
iType = int(sType[5:])
iImgSizeY, fPixelsPerByte = CalcParams() typeData = GetDataForMemoryType(iType)
for objAlloc in tType[1]:
img = Image.new('RGB', (IMG_SIZE_X, iImgSizeY), 'white') typeData['CustomPools'][sFullName].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc['Usage']) if ('Usage' in objAlloc) else 0))
draw = ImageDraw.Draw(img)
try: if IsDataEmpty():
font = ImageFont.truetype('segoeuib.ttf') print("There is nothing to put on the image. Please make sure you generated the stats string with detailed map enabled.")
except: exit(1)
font = ImageFont.load_default()
iImgSizeY, fPixelsPerByte = CalcParams()
y = IMG_MARGIN
img = Image.new('RGB', (IMG_SIZE_X, iImgSizeY), 'white')
# Draw grid lines draw = ImageDraw.Draw(img)
iBytesBetweenGridLines = 32
while iBytesBetweenGridLines * fPixelsPerByte < 64: try:
iBytesBetweenGridLines *= 2 font = ImageFont.truetype('segoeuib.ttf')
iByte = 0 except:
TEXT_MARGIN = 4 font = ImageFont.load_default()
while True:
iX = int(iByte * fPixelsPerByte) y = IMG_MARGIN
if iX > IMG_SIZE_X - 2 * IMG_MARGIN:
break # Draw grid lines
draw.line([iX + IMG_MARGIN, 0, iX + IMG_MARGIN, iImgSizeY], fill=COLOR_GRID_LINE) iBytesBetweenGridLines = 32
if iByte == 0: while iBytesBetweenGridLines * fPixelsPerByte < 64:
draw.text((iX + IMG_MARGIN + TEXT_MARGIN, y), "0", fill=COLOR_TEXT_H2, font=font) iBytesBetweenGridLines *= 2
else: iByte = 0
text = BytesToStr(iByte) TEXT_MARGIN = 4
textSize = draw.textsize(text, font=font) while True:
draw.text((iX + IMG_MARGIN - textSize[0] - TEXT_MARGIN, y), text, fill=COLOR_TEXT_H2, font=font) iX = int(iByte * fPixelsPerByte)
iByte += iBytesBetweenGridLines if iX > IMG_SIZE_X - 2 * IMG_MARGIN:
y += FONT_SIZE + IMG_MARGIN break
draw.line([iX + IMG_MARGIN, 0, iX + IMG_MARGIN, iImgSizeY], fill=COLOR_GRID_LINE)
# Draw main content if iByte == 0:
for iMemTypeIndex in sorted(data.keys()): draw.text((iX + IMG_MARGIN + TEXT_MARGIN, y), "0", fill=COLOR_TEXT_H2, font=font)
dictMemType = data[iMemTypeIndex] else:
draw.text((IMG_MARGIN, y), "Memory type %d" % iMemTypeIndex, fill=COLOR_TEXT_H1, font=font) text = BytesToStr(iByte)
y += FONT_SIZE + IMG_MARGIN textSize = draw.textsize(text, font=font)
index = 0 draw.text((iX + IMG_MARGIN - textSize[0] - TEXT_MARGIN, y), text, fill=COLOR_TEXT_H2, font=font)
for tDedicatedAlloc in dictMemType['DedicatedAllocations']: iByte += iBytesBetweenGridLines
draw.text((IMG_MARGIN, y), "Dedicated allocation %d" % index, fill=COLOR_TEXT_H2, font=font) y += FONT_SIZE + IMG_MARGIN
y += FONT_SIZE + IMG_MARGIN
DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc) # Draw main content
y += MAP_SIZE + IMG_MARGIN for iMemTypeIndex in sorted(data.keys()):
index += 1 dictMemType = data[iMemTypeIndex]
for objBlock in dictMemType['DefaultPoolBlocks']: draw.text((IMG_MARGIN, y), "Memory type %d" % iMemTypeIndex, fill=COLOR_TEXT_H1, font=font)
draw.text((IMG_MARGIN, y), "Default pool block %d" % objBlock['ID'], fill=COLOR_TEXT_H2, font=font) y += FONT_SIZE + IMG_MARGIN
y += FONT_SIZE + IMG_MARGIN index = 0
DrawBlock(draw, y, objBlock) for tDedicatedAlloc in dictMemType['DedicatedAllocations']:
y += MAP_SIZE + IMG_MARGIN draw.text((IMG_MARGIN, y), "Dedicated allocation %d" % index, fill=COLOR_TEXT_H2, font=font)
index = 0 y += FONT_SIZE + IMG_MARGIN
for sPoolName, listPool in dictMemType['CustomPools'].items(): DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc)
for objBlock in listPool: y += MAP_SIZE + IMG_MARGIN
if 'Algorithm' in objBlock and objBlock['Algorithm']: index += 1
sAlgorithm = ' (Algorithm: %s)' % (objBlock['Algorithm']) for objBlock in dictMemType['DefaultPoolBlocks']:
else: draw.text((IMG_MARGIN, y), "Default pool block %d" % objBlock['ID'], fill=COLOR_TEXT_H2, font=font)
sAlgorithm = '' y += FONT_SIZE + IMG_MARGIN
draw.text((IMG_MARGIN, y), "Custom pool %s%s block %d" % (sPoolName, sAlgorithm, objBlock['ID']), fill=COLOR_TEXT_H2, font=font) DrawBlock(draw, y, objBlock)
y += FONT_SIZE + IMG_MARGIN y += MAP_SIZE + IMG_MARGIN
DrawBlock(draw, y, objBlock) index = 0
y += MAP_SIZE + IMG_MARGIN for sPoolName, listPool in dictMemType['CustomPools'].items():
index += 1 for objBlock in listPool:
del draw if 'Algorithm' in objBlock and objBlock['Algorithm']:
img.save(args.output) sAlgorithm = ' (Algorithm: %s)' % (objBlock['Algorithm'])
else:
""" sAlgorithm = ''
Main data structure - variable `data` - is a dictionary. Key is integer - memory type index. Value is dictionary of: draw.text((IMG_MARGIN, y), "Custom pool %s%s block %d" % (sPoolName, sAlgorithm, objBlock['ID']), fill=COLOR_TEXT_H2, font=font)
- Fixed key 'DedicatedAllocations'. Value is list of tuples, each containing: y += FONT_SIZE + IMG_MARGIN
- [0]: Type : string DrawBlock(draw, y, objBlock)
- [1]: Size : integer y += FONT_SIZE + IMG_MARGIN
- [2]: Usage : integer (0 if unknown) DrawDedicatedAllocationBlock(draw, y, objBlock['DedicatedAllocations'])
- Fixed key 'DefaultPoolBlocks'. Value is list of objects, each containing dictionary with: y += MAP_SIZE + IMG_MARGIN
- Fixed key 'ID'. Value is int. index += 1
- Fixed key 'Size'. Value is int. del draw
- Fixed key 'Suballocations'. Value is list of tuples as above. img.save(args.output)
- Fixed key 'CustomPools'. Value is dictionary.
- Key is string with pool ID/name. Value is list of objects representing memory blocks, each containing dictionary with: """
- Fixed key 'ID'. Value is int. Main data structure - variable `data` - is a dictionary. Key is integer - memory type index. Value is dictionary of:
- Fixed key 'Size'. Value is int. - Fixed key 'DedicatedAllocations'. Value is list of tuples, each containing:
- Fixed key 'Algorithm'. Optional. Value is string. - [0]: Type : string
- Fixed key 'Suballocations'. Value is list of tuples as above. - [1]: Size : integer
""" - [2]: Usage : integer (0 if unknown)
- Fixed key 'DefaultPoolBlocks'. Value is list of objects, each containing dictionary with:
- Fixed key 'ID'. Value is int.
- Fixed key 'Size'. Value is int.
- Fixed key 'Suballocations'. Value is list of tuples as above.
- Fixed key 'CustomPools'. Value is dictionary.
- Key is string with pool ID/name. Value is list of objects representing memory blocks, each containing dictionary with:
- Fixed key 'ID'. Value is int.
- Fixed key 'Size'. Value is int.
- Fixed key 'Algorithm'. Optional. Value is string.
- Fixed key 'Suballocations'. Value is list of tuples as above.
- Fixed key 'DedicatedAllocations'. Value is list of tuples as above.
"""